ESP-IDF Firmware
Firmware architecture and call graph
Loading...
Searching...
No Matches
main.c File Reference
#include <assert.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include "FreeRTOS.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_filter.h"
#include "esp_attr.h"
#include "esp_crc.h"
#include "esp_dsp.h"
#include "esp_err.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "led_strip.h"
#include "string.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
Include dependency graph for main/main.c:

Go to the source code of this file.

Data Structures

struct  frame_wire_t

Macros

#define OPTIMIZE_O2
#define FFT_SIZE   CONFIG_ADC_FFT_SIZE
#define ADC_SPS   CONFIG_ADC_FS
#define CHANNELS   2
#define SAMPLE_MAX   4096
#define LED_STRIP_RMT_RES_HZ   (10 * 1000 * 1000)
#define SPIKE_DB_THRESHOLD   20.0f
#define SPIKE_RATIO_THRESHOLD   8.0f
#define SPIKE_MIN_AVG_LINEAR   1e-7f
#define FRAME_MAGIC_0   0xF0
#define FRAME_MAGIC_1   0x0D
#define FRAME_MAGIC_2   0xF0
#define FRAME_MAGIC_3   0x0D
#define FRAME_VERSION   0x02
#define FRAME_HEADER_SIZE   9u
#define FRAME_PAYLOAD_SIZE   (sizeof(frame_payload_t))
#define FRAME_TOTAL_SIZE   (FRAME_HEADER_SIZE + FRAME_PAYLOAD_SIZE + 4u)
#define CDC_TX_TASK_STACK_SIZE   4096
#define ADC_TASK_STACK_SIZE   8192

Functions

struct __attribute__ ((packed))
static void write_u16_le (uint8_t *dst, uint16_t value)
static void write_u32_le (uint8_t *dst, uint32_t value)
static void write_u64_le (uint8_t *dst, uint64_t value)
static uint32_t frame_crc32 (const uint8_t *data, size_t len)
static void build_frame (frame_wire_t *out, const float *data, const char label[4], uint64_t t_ms, uint32_t sps, uint16_t seq)
static bool safe_cdcacm_write_flush (tinyusb_cdcacm_itf_t port, int max_retries)
static void cdc_tx_task (void *arg)
bool adc_calibration_init (adc_unit_t unit, adc_atten_t atten)
static float sample_to_volts (uint16_t raw, bool calibrated)
static float sample_to_centered (uint16_t raw, bool calibrated, int midpoint_mv)
static void apply_window (float *buf)
static void perform_fft (float *buf)
static void apply_complex_cutoff (float *buf, uint32_t sps)
static adc_atten_t cfg_atten (void)
static void adc_fft_task (void *arg)
void app_main (void)

Variables

static const char * TAG = "adc_fft"
static float * ch0 = NULL
static float * ch1 = NULL
static float * win = NULL
static adc_cali_handle_t cali_handle
 frame_payload_t
static QueueHandle_t g_frameq
static uint16_t g_seq
static tinyusb_cdcacm_itf_t g_data_port = TINYUSB_CDC_ACM_0
static TaskHandle_t g_cdc_task_handle
static TaskHandle_t g_adc_task_handle

Macro Definition Documentation

◆ ADC_SPS

#define ADC_SPS   CONFIG_ADC_FS

Definition at line 40 of file main/main.c.

Referenced by adc_fft_task().

◆ ADC_TASK_STACK_SIZE

#define ADC_TASK_STACK_SIZE   8192

Definition at line 107 of file main/main.c.

Referenced by app_main().

◆ CDC_TX_TASK_STACK_SIZE

#define CDC_TX_TASK_STACK_SIZE   4096

Definition at line 104 of file main/main.c.

Referenced by app_main().

◆ CHANNELS

#define CHANNELS   2

Definition at line 50 of file main/main.c.

Referenced by adc_fft_task().

◆ FFT_SIZE

#define FFT_SIZE   CONFIG_ADC_FFT_SIZE

◆ FRAME_HEADER_SIZE

#define FRAME_HEADER_SIZE   9u

Definition at line 78 of file main/main.c.

Referenced by build_frame().

◆ FRAME_MAGIC_0

#define FRAME_MAGIC_0   0xF0

Definition at line 73 of file main/main.c.

Referenced by build_frame().

◆ FRAME_MAGIC_1

#define FRAME_MAGIC_1   0x0D

Definition at line 74 of file main/main.c.

Referenced by build_frame().

◆ FRAME_MAGIC_2

#define FRAME_MAGIC_2   0xF0

Definition at line 75 of file main/main.c.

Referenced by build_frame().

◆ FRAME_MAGIC_3

#define FRAME_MAGIC_3   0x0D

Definition at line 76 of file main/main.c.

Referenced by build_frame().

◆ FRAME_PAYLOAD_SIZE

#define FRAME_PAYLOAD_SIZE   (sizeof(frame_payload_t))

Definition at line 79 of file main/main.c.

Referenced by build_frame().

◆ FRAME_TOTAL_SIZE

#define FRAME_TOTAL_SIZE   (FRAME_HEADER_SIZE + FRAME_PAYLOAD_SIZE + 4u)

Definition at line 80 of file main/main.c.

80#define FRAME_TOTAL_SIZE \
81 (FRAME_HEADER_SIZE + FRAME_PAYLOAD_SIZE + 4u) // +4 for CRC32

Referenced by cdc_tx_task().

◆ FRAME_VERSION

#define FRAME_VERSION   0x02

Definition at line 77 of file main/main.c.

Referenced by build_frame().

◆ LED_STRIP_RMT_RES_HZ

#define LED_STRIP_RMT_RES_HZ   (10 * 1000 * 1000)

Definition at line 53 of file main/main.c.

◆ OPTIMIZE_O2

#define OPTIMIZE_O2

Definition at line 36 of file main/main.c.

Referenced by apply_complex_cutoff(), apply_window(), and perform_fft().

◆ SAMPLE_MAX

#define SAMPLE_MAX   4096

Definition at line 51 of file main/main.c.

◆ SPIKE_DB_THRESHOLD

#define SPIKE_DB_THRESHOLD   20.0f

Definition at line 55 of file main/main.c.

◆ SPIKE_MIN_AVG_LINEAR

#define SPIKE_MIN_AVG_LINEAR   1e-7f

Definition at line 57 of file main/main.c.

◆ SPIKE_RATIO_THRESHOLD

#define SPIKE_RATIO_THRESHOLD   8.0f

Definition at line 56 of file main/main.c.

Function Documentation

◆ __attribute__()

struct __attribute__ ( (packed) )

Definition at line 63 of file main/main.c.

65 {
66 uint64_t t_ms;
67 uint32_t sps;
68 uint32_t fft;
69 char label[4];
70 float data[FFT_SIZE];
frame_payload_t
Definition main/main.c:71
#define FFT_SIZE
Definition main/main.c:39
static float data[128 *2]
Definition test_fft2r.c:34

References __attribute__(), cali_handle, data, and FFT_SIZE.

Referenced by __attribute__().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ adc_calibration_init()

bool adc_calibration_init ( adc_unit_t unit,
adc_atten_t atten )

Definition at line 411 of file main/main.c.

411 {
412 adc_cali_curve_fitting_config_t cali_config = {
413 .unit_id = unit,
414 .atten = atten,
415 .bitwidth = ADC_BITWIDTH_12,
416 };
417 if (adc_cali_create_scheme_curve_fitting(&cali_config, &cali_handle) ==
418 ESP_OK) {
419 ESP_LOGI(TAG, "ADC calibration ready (curve fitting)");
420 return true;
421 }
422 ESP_LOGW(TAG, "ADC calibration not available; raw counts will be used");
423 return false;
424}
#define ESP_OK
Definition esp_err.h:23
static const char * TAG
Definition main/main.c:31
static adc_cali_handle_t cali_handle
Definition main/main.c:63

References cali_handle, ESP_OK, and TAG.

Referenced by adc_fft_task().

Here is the caller graph for this function:

◆ adc_fft_task()

void adc_fft_task ( void * arg)
static

Definition at line 488 of file main/main.c.

488 {
489 adc_atten_t atten = cfg_atten();
490 bool calibrated = adc_calibration_init(ADC_UNIT_1, atten);
491 int midpoint_mv = 0;
492 if (calibrated) {
493 (void)adc_cali_raw_to_voltage(cali_handle, 2048, &midpoint_mv);
494 }
495
496 /* ADC decode diagnostics: validates that DMA words are parsed as expected
497 * (unit/channel/bitwidth). If ADC output format mismatches parsing, these
498 * counters will look obviously wrong (e.g., few accepted samples, many
499 * skipped/other channels).
500 */
501 uint64_t last_adc_diag_ms = esp_timer_get_time() / 1000ULL;
502 uint32_t diag_reads = 0;
503 uint32_t diag_out_bytes = 0;
504 uint32_t diag_accept_ch0 = 0;
505 uint32_t diag_accept_ch1 = 0;
506 uint32_t diag_skip_unit = 0;
507 uint32_t diag_other_chan = 0;
508
509 adc_continuous_handle_t adc_handle = NULL;
510 adc_continuous_handle_cfg_t handle_cfg = {
511 .max_store_buf_size =
512 FFT_SIZE * CHANNELS * sizeof(adc_digi_output_data_t),
513 .conv_frame_size = 512,
514 };
515 ESP_ERROR_CHECK(adc_continuous_new_handle(&handle_cfg, &adc_handle));
516
517 uint32_t sample_rate = ADC_SPS;
518#if defined(CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH)
519 if (sample_rate > CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH) {
520 ESP_LOGW(TAG, "ADC_SPS=%u above SOC max %u; clamping", sample_rate,
521 (unsigned)CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH);
522 sample_rate = CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH;
523 }
524#endif
525#if defined(CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW)
526 if (sample_rate < CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW) {
527 ESP_LOGW(TAG, "ADC_SPS=%u below SOC min %u; clamping", sample_rate,
528 (unsigned)CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW);
529 sample_rate = CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW;
530 }
531#endif
532
533 /* Per-channel sampling rate: adc_continuous divides sample_freq_hz by the
534 * pattern length. With two channels, each gets half the configured rate. */
535 const uint32_t per_channel_sps = sample_rate / CHANNELS;
536
537 adc_digi_pattern_config_t pattern[CHANNELS] = {0};
538 pattern[0].atten = atten;
539 pattern[0].channel = CONFIG_ADC_CH0;
540 pattern[0].unit = ADC_UNIT_1;
541 pattern[0].bit_width = ADC_BITWIDTH_12;
542 pattern[1].atten = atten;
543 pattern[1].channel = CONFIG_ADC_CH1;
544 pattern[1].unit = ADC_UNIT_1;
545 pattern[1].bit_width = ADC_BITWIDTH_12;
546
547 adc_continuous_config_t dig_cfg = {
548 .sample_freq_hz = sample_rate,
549 .conv_mode = ADC_CONV_SINGLE_UNIT_1,
550 /* Must match adc_digi_output_data_t parsing below (p->type2.*). */
551 .format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,
552 .pattern_num = CHANNELS,
553 .adc_pattern = pattern,
554 };
555 ESP_ERROR_CHECK(adc_continuous_config(adc_handle, &dig_cfg));
556
557#if CONFIG_ADC_IIR_FILTER_ENABLE
558 adc_digi_iir_filter_t coeff = ADC_DIGI_IIR_FILTER_COEFF_2;
559#if CONFIG_ADC_IIR_FILTER_COEFF_4
560 coeff = ADC_DIGI_IIR_FILTER_COEFF_4;
561#elif CONFIG_ADC_IIR_FILTER_COEFF_8
562 coeff = ADC_DIGI_IIR_FILTER_COEFF_8;
563#elif CONFIG_ADC_IIR_FILTER_COEFF_16
564 coeff = ADC_DIGI_IIR_FILTER_COEFF_16;
565#elif CONFIG_ADC_IIR_FILTER_COEFF_64
566 coeff = ADC_DIGI_IIR_FILTER_COEFF_64;
567#endif
568 adc_continuous_iir_filter_config_t filter_cfg = {
569 .unit = ADC_UNIT_1,
570 .coeff = coeff,
571 };
572 adc_iir_filter_handle_t iir_handle = NULL;
573 ESP_ERROR_CHECK(
574 adc_new_continuous_iir_filter(adc_handle, &filter_cfg, &iir_handle));
575 ESP_ERROR_CHECK(adc_continuous_iir_filter_enable(iir_handle));
576 ESP_LOGI(TAG, "ADC IIR Filter enabled");
577#endif
578
579 ESP_ERROR_CHECK(adc_continuous_start(adc_handle));
580
581 ESP_LOGI(TAG,
582 "Sampling ADC1 CH%d (GPIO%d) and CH%d (GPIO%d) at %u sps/ch, FFT %d",
583 CONFIG_ADC_CH0, CONFIG_ADC_CH0 + 1, CONFIG_ADC_CH1,
584 CONFIG_ADC_CH1 + 1, (unsigned)per_channel_sps, FFT_SIZE);
585
586 // Reduce DMA buffer size to save Internal RAM; we loop to fill the FFT buffer anyway.
587 const size_t read_len = 2048 * sizeof(adc_digi_output_data_t);
588 uint8_t *raw = heap_caps_aligned_alloc(16, read_len,
589 MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
590 if (!raw) {
591 raw = heap_caps_aligned_alloc(16, read_len, MALLOC_CAP_DMA);
592 }
593 if (!raw) {
594 ESP_LOGE(TAG, "Failed to reserve DMA buffer (%zu bytes)", read_len);
595 vTaskDelete(NULL);
596 return;
597 }
598
599 static frame_wire_t frame;
600 while (1) {
601#if CONFIG_STATUS_NEOPIXEL_ENABLE
602 status_led_set(0, 0, 255);
603#endif
604 int count0 = 0;
605 int count1 = 0;
606 float sum0 = 0.0f;
607 float sum1 = 0.0f;
608 while (count0 < FFT_SIZE || count1 < FFT_SIZE) {
609 uint32_t out_len = 0;
610 esp_err_t ret = adc_continuous_read(adc_handle, raw, read_len, &out_len,
611 pdMS_TO_TICKS(100));
612 if (ret == ESP_ERR_TIMEOUT) {
613 continue;
614 }
615 if (ret != ESP_OK) {
616 ESP_LOGW(TAG, "adc_continuous_read returned %s", esp_err_to_name(ret));
617 vTaskDelay(pdMS_TO_TICKS(1));
618 continue;
619 }
620
621 /* Per-read diagnostics. */
622 diag_reads++;
623 diag_out_bytes += out_len;
624
625 for (uint32_t i = 0; i + sizeof(adc_digi_output_data_t) <= out_len;
626 i += sizeof(adc_digi_output_data_t)) {
627 adc_digi_output_data_t *p = (adc_digi_output_data_t *)&raw[i];
628 if (p->type2.unit != ADC_UNIT_1) {
629 diag_skip_unit++;
630 continue;
631 }
632 if (p->type2.channel == CONFIG_ADC_CH0 && count0 < FFT_SIZE) {
633 float val = sample_to_centered(p->type2.data, calibrated, midpoint_mv);
634 ch0[count0++] = val;
635 sum0 += val;
636 diag_accept_ch0++;
637 } else if (p->type2.channel == CONFIG_ADC_CH1 && count1 < FFT_SIZE) {
638 float val = sample_to_centered(p->type2.data, calibrated, midpoint_mv);
639 ch1[count1++] = val;
640 sum1 += val;
641 diag_accept_ch1++;
642 } else {
643 diag_other_chan++;
644 }
645 }
646
647 /* Rate-limit to avoid log spam. */
648 uint64_t now_ms = esp_timer_get_time() / 1000ULL;
649 if (now_ms > last_adc_diag_ms + 2000ULL) {
650 ESP_LOGI(TAG,
651 "ADC diag (2s): reads=%u out=%uB accept[ch0/ch1]=%u/%u skip_unit=%u other_chan=%u",
652 (unsigned)diag_reads, (unsigned)diag_out_bytes,
653 (unsigned)diag_accept_ch0, (unsigned)diag_accept_ch1,
654 (unsigned)diag_skip_unit, (unsigned)diag_other_chan);
655 diag_reads = 0;
656 diag_out_bytes = 0;
657 diag_accept_ch0 = 0;
658 diag_accept_ch1 = 0;
659 diag_skip_unit = 0;
660 diag_other_chan = 0;
661 last_adc_diag_ms = now_ms;
662 }
663 }
664
665 /* Dynamic DC bias removal: subtract current block mean */
666 float mean0 = (float)(sum0 / FFT_SIZE);
667 float mean1 = (float)(sum1 / FFT_SIZE);
668 for (int i = 0; i < FFT_SIZE; i++) {
669 ch0[i] -= mean0;
670 ch1[i] -= mean1;
671 }
672
673#if CONFIG_ADC_ENABLE_WINDOW
676#endif
679
680 // apply_complex_cutoff(ch0, per_channel_sps);
681 // apply_complex_cutoff(ch1, per_channel_sps);
682
683 uint64_t now_ms = esp_timer_get_time() / 1000ULL;
684
685 build_frame(&frame, ch0, "ch0", now_ms, per_channel_sps, g_seq++);
686 if (xQueueSend(g_frameq, &frame, portMAX_DELAY) != pdTRUE) {
687 ESP_LOGW(TAG, "Frame queue send failed (ch0)");
688 }
689
690 build_frame(&frame, ch1, "ch1", now_ms, per_channel_sps, g_seq++);
691 if (xQueueSend(g_frameq, &frame, portMAX_DELAY) != pdTRUE) {
692 ESP_LOGW(TAG, "Frame queue send failed (ch1)");
693 }
694 }
695}
int esp_err_t
Definition esp_err.h:21
static QueueHandle_t g_frameq
Definition main/main.c:91
#define CHANNELS
Definition main/main.c:50
static float * ch0
Definition main/main.c:59
static float sample_to_centered(uint16_t raw, bool calibrated, int midpoint_mv)
Definition main/main.c:436
static void build_frame(frame_wire_t *out, const float *data, const char label[4], uint64_t t_ms, uint32_t sps, uint16_t seq)
Definition main/main.c:130
static void apply_window(float *buf)
Definition main/main.c:447
bool adc_calibration_init(adc_unit_t unit, adc_atten_t atten)
Definition main/main.c:411
static void perform_fft(float *buf)
Definition main/main.c:453
static float * ch1
Definition main/main.c:60
static adc_atten_t cfg_atten(void)
Definition main/main.c:476
#define ADC_SPS
Definition main/main.c:40
static uint16_t g_seq
Definition main/main.c:92

References adc_calibration_init(), ADC_SPS, apply_window(), build_frame(), cali_handle, cfg_atten(), ch0, ch1, CHANNELS, ESP_OK, FFT_SIZE, g_frameq, g_seq, perform_fft(), sample_to_centered(), and TAG.

Referenced by app_main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ app_main()

void app_main ( void )

Definition at line 720 of file main/main.c.

720 {
721#if CONFIG_STATUS_NEOPIXEL_ENABLE
722 status_led_init();
723 status_led_set(255, 0, 0);
724#endif
725 tinyusb_config_t tusb_cfg = {
726 .device_descriptor = NULL,
727 .string_descriptor = NULL,
728 .external_phy = false,
729 };
730 ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
731
732#if CONFIG_TINYUSB_CDC_COUNT > 1
734#else
736#endif
737
738 tinyusb_config_cdcacm_t cdc_cfg_data = {
739 .usb_dev = TINYUSB_USBDEV_0,
740 .cdc_port = g_data_port,
741 .rx_unread_buf_sz = CONFIG_TINYUSB_CDC_RX_BUFSIZE,
742 .callback_rx = NULL,
743 .callback_rx_wanted_char = NULL,
744 .callback_line_state_changed = NULL,
745 .callback_line_coding_changed = NULL,
746 };
747 ESP_ERROR_CHECK(tusb_cdc_acm_init(&cdc_cfg_data));
748
749
750#if CONFIG_ENABLE_I2S_WAVE_GEN
751 // Initialize Wave Gen
752 // Using GPIO 15-18 to avoid VDD_SPI (GPIO 11) and ADC1 interference
753 wave_gen_config_t wg_cfg = {
754 .mck_io_num = -1, // SCK disconnected (PCM5102 internal generation)
755 .bck_io_num = 16,
756 .ws_io_num = 17,
757 .data_out_num = 18,
758 .sample_rate = 44100,
759 };
760 ESP_ERROR_CHECK(wave_gen_init(&wg_cfg));
761 // wave_gen_set_freq(33.0f); // Controlled by sweep_task
764 ESP_ERROR_CHECK(wave_gen_start());
765#endif
766
767#if CONFIG_ENABLE_SDM_WAVE_GEN
768 ESP_ERROR_CHECK(wave_gen_sdm_start());
769#endif
770
771 size_t fft_mem_sz = FFT_SIZE * sizeof(float);
772 ch0 = heap_caps_aligned_alloc(16, fft_mem_sz, MALLOC_CAP_SPIRAM);
773 ch1 = heap_caps_aligned_alloc(16, fft_mem_sz, MALLOC_CAP_SPIRAM);
774 win = heap_caps_aligned_alloc(16, fft_mem_sz, MALLOC_CAP_SPIRAM);
775
776 if (!ch0 || !ch1 || !win) {
777 ESP_LOGE(TAG, "Failed to allocate FFT buffers");
778 abort();
779 }
780
781 /* Initialize DSP before any task can call FFT/window code. */
782 ESP_ERROR_CHECK(dsps_fft4r_init_fc32(NULL, FFT_SIZE >> 1));
783#if CONFIG_ADC_ENABLE_WINDOW
784#if CONFIG_ADC_WINDOW_HANN
786#elif CONFIG_ADC_WINDOW_BLACKMAN
788#elif CONFIG_ADC_WINDOW_BLACKMAN_HARRIS
790#elif CONFIG_ADC_WINDOW_BLACKMAN_NUTTALL
792#elif CONFIG_ADC_WINDOW_NUTTALL
794#elif CONFIG_ADC_WINDOW_FLAT_TOP
796#else
797 for (int i = 0; i < FFT_SIZE; i++) {
798 win[i] = 1.0f;
799 }
800#endif
801#endif
802
803
804#if CONFIG_ENABLE_I2S_WAVE_GEN
805 xTaskCreate(sweep_task, "sweep", 2048, NULL, 5, NULL);
806#endif
807
808 g_frameq = xQueueCreate(
809 2,
810 sizeof(frame_wire_t));
811 assert(g_frameq);
812
813 const BaseType_t tusb_core = 0;
814#if CONFIG_FREERTOS_UNICORE
815 const BaseType_t adc_core = 0;
816#else
817 const BaseType_t adc_core = 1;
818#endif
819
820 xTaskCreatePinnedToCore(
822 "cdc_tx",
824 NULL,
825 8,
827 tusb_core);
828
829
830
831
832 xTaskCreatePinnedToCore(
834 "adc_fft",
836 NULL,
837 8,
839 adc_core);
840
841 xTaskCreatePinnedToCore(
843 "adc_fft",
845 NULL,
846 8,
848 adc_core
849 );
850
851 xTaskCreatePinnedToCore(adc_fft_task,"adc_fft",ADC_TASK_STACK_SIZE,NULL,8,&g_adc_task_handle,adc_core);
852
853
854 // Tasks run indefinitely; app_main can return.
855}
esp_err_t dsps_fft4r_init_fc32(float *fft_table_buff, int max_fft_size)
init fft tables
void dsps_wind_blackman_f32(float *window, int len)
Blackman window.
void dsps_wind_blackman_harris_f32(float *window, int len)
Blackman-Harris window.
void dsps_wind_blackman_nuttall_f32(float *window, int len)
Blackman-Nuttall window.
void dsps_wind_flat_top_f32(float *window, int len)
Flat-Top window.
void dsps_wind_hann_f32(float *window, int len)
Hann window.
void dsps_wind_nuttall_f32(float *window, int len)
Nuttall window.
#define ADC_TASK_STACK_SIZE
Definition main/main.c:107
#define CDC_TX_TASK_STACK_SIZE
Definition main/main.c:104
static float * win
Definition main/main.c:61
static TaskHandle_t g_cdc_task_handle
Definition main/main.c:94
static void adc_fft_task(void *arg)
Definition main/main.c:488
static tinyusb_cdcacm_itf_t g_data_port
Definition main/main.c:93
static void cdc_tx_task(void *arg)
Definition main/main.c:254
static TaskHandle_t g_adc_task_handle
Definition main/main.c:95
Configuration structure for CDC-ACM.
Configuration structure of the TinyUSB core.
Definition tinyusb.h:29
Configuration structure for Wave Generator.
Definition wave_gen.h:22
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
This is an all-in-one helper function, including:
Definition tinyusb.c:32
@ TINYUSB_USBDEV_0
esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg)
Initialize CDC ACM. Initialization will be finished with the tud_cdc_line_state_cb callback.
@ TINYUSB_CDC_ACM_1
@ TINYUSB_CDC_ACM_0
void wave_gen_set_type(wave_type_t type)
Set the waveform type.
Definition wave_gen.c:291
esp_err_t wave_gen_init(const wave_gen_config_t *config)
Initialize the Wave Generator component.
Definition wave_gen.c:213
esp_err_t wave_gen_start(void)
Start the wave generation task.
Definition wave_gen.c:253
@ WAVE_TYPE_SINE
Definition wave_gen.h:13
esp_err_t wave_gen_sdm_start(void)
Start the SDM sine generator (uses CONFIG_SDM_* settings).
Definition wave_gen.c:303
void wave_gen_set_volume(float volume)
Set the output volume/amplitude.
Definition wave_gen.c:296

References adc_fft_task(), ADC_TASK_STACK_SIZE, cdc_tx_task(), CDC_TX_TASK_STACK_SIZE, ch0, ch1, dsps_fft4r_init_fc32(), dsps_wind_blackman_f32(), dsps_wind_blackman_harris_f32(), dsps_wind_blackman_nuttall_f32(), dsps_wind_flat_top_f32(), dsps_wind_hann_f32(), dsps_wind_nuttall_f32(), FFT_SIZE, g_adc_task_handle, g_cdc_task_handle, g_data_port, g_frameq, TAG, TINYUSB_CDC_ACM_0, TINYUSB_CDC_ACM_1, tinyusb_driver_install(), TINYUSB_USBDEV_0, tusb_cdc_acm_init(), wave_gen_init(), wave_gen_sdm_start(), wave_gen_set_type(), wave_gen_set_volume(), wave_gen_start(), WAVE_TYPE_SINE, and win.

Here is the call graph for this function:

◆ apply_complex_cutoff()

void apply_complex_cutoff ( float * buf,
uint32_t sps )
static

Definition at line 459 of file main/main.c.

459 {
460#if CONFIG_OUTPUT_CUTOFF_HZ > 0
461 uint32_t cutoff_hz = (uint32_t)CONFIG_OUTPUT_CUTOFF_HZ;
462 uint32_t max_bin = (cutoff_hz * (uint64_t)FFT_SIZE) / sps;
463 if (max_bin >= FFT_SIZE / 2) {
464 return;
465 }
466 if (max_bin < FFT_SIZE / 2) {
467 buf[1] = 0.0f; // Nyquist packed at index 1
468 }
469 for (uint32_t k = max_bin + 1; k < FFT_SIZE / 2; k++) {
470 buf[2 * k] = 0.0f;
471 buf[2 * k + 1] = 0.0f;
472 }
473#endif
474}
const int k
Definition test_mmult.c:18

References FFT_SIZE, k, and OPTIMIZE_O2.

◆ apply_window()

void apply_window ( float * buf)
static

Definition at line 447 of file main/main.c.

447 {
448 for (int i = 0; i < FFT_SIZE; i++) {
449 buf[i] *= win[i];
450 }
451}

References FFT_SIZE, OPTIMIZE_O2, and win.

Referenced by adc_fft_task().

Here is the caller graph for this function:

◆ build_frame()

void build_frame ( frame_wire_t * out,
const float * data,
const char label[4],
uint64_t t_ms,
uint32_t sps,
uint16_t seq )
static

Definition at line 130 of file main/main.c.

132 {
133 uint8_t *dst = out->bytes;
134 dst[0] = FRAME_MAGIC_0;
135 dst[1] = FRAME_MAGIC_1;
136 dst[2] = FRAME_MAGIC_2;
137 dst[3] = FRAME_MAGIC_3;
138 dst[4] = FRAME_VERSION;
139 write_u16_le(dst + 5, seq);
140 write_u16_le(dst + 7, (uint16_t)FRAME_PAYLOAD_SIZE);
141
142 size_t off = FRAME_HEADER_SIZE;
143 write_u64_le(dst + off, t_ms);
144 off += sizeof(uint64_t);
145 write_u32_le(dst + off, sps);
146 off += sizeof(uint32_t);
147 write_u32_le(dst + off, FFT_SIZE);
148 off += sizeof(uint32_t);
149 memcpy(dst + off, label, 4);
150 off += 4;
151 memcpy(dst + off, data, sizeof(float) * FFT_SIZE);
152 off += sizeof(float) * FFT_SIZE;
153
154 uint32_t crc = frame_crc32(dst, FRAME_HEADER_SIZE + FRAME_PAYLOAD_SIZE);
155 write_u32_le(dst + off, crc);
156}
#define FRAME_MAGIC_2
Definition main/main.c:75
static void write_u32_le(uint8_t *dst, uint32_t value)
Definition main/main.c:114
#define FRAME_PAYLOAD_SIZE
Definition main/main.c:79
#define FRAME_MAGIC_3
Definition main/main.c:76
#define FRAME_VERSION
Definition main/main.c:77
#define FRAME_HEADER_SIZE
Definition main/main.c:78
static void write_u16_le(uint8_t *dst, uint16_t value)
Definition main/main.c:109
#define FRAME_MAGIC_1
Definition main/main.c:74
static uint32_t frame_crc32(const uint8_t *data, size_t len)
Definition main/main.c:126
#define FRAME_MAGIC_0
Definition main/main.c:73
static void write_u64_le(uint8_t *dst, uint64_t value)
Definition main/main.c:121
uint8_t bytes[(9u+(sizeof(frame_payload_t))+4u)]
Definition main/main.c:88

References frame_wire_t::bytes, data, FFT_SIZE, frame_crc32(), FRAME_HEADER_SIZE, FRAME_MAGIC_0, FRAME_MAGIC_1, FRAME_MAGIC_2, FRAME_MAGIC_3, FRAME_PAYLOAD_SIZE, FRAME_VERSION, write_u16_le(), write_u32_le(), and write_u64_le().

Referenced by adc_fft_task().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cdc_tx_task()

void cdc_tx_task ( void * arg)
static

Definition at line 254 of file main/main.c.

254 {
255#if 1
256 static frame_wire_t frm;
257
258 const size_t max_chunk = 512;
259 const uint32_t wait_step_ms = 2;
260 /* Increase timeout significantly for large FFT sizes to prevent frame drops
261 * that cause stream desynchronization. For FFT=4096, frame interval is ~100ms;
262 * allow up to 10 seconds for host processing/backpressure.
263 */
264 const uint32_t wait_budget_ms = 10000;
265
266
267#if CONFIG_DIAG_CDC_ENABLE
268 /* initialize diagnostic window at task start */
269 diag_cdc_reset_window(esp_timer_get_time() / 1000ULL);
270 uint64_t last_stack_log_ms = esp_timer_get_time() / 1000ULL;
271#endif
272 while (1) {
273 if (xQueueReceive(g_frameq, &frm, portMAX_DELAY) == pdTRUE) {
275 !tud_cdc_n_connected((uint8_t)g_data_port)) {
276 /* Host not connected: sleep longer to avoid starving idle/other tasks
277 */
278 vTaskDelay(pdMS_TO_TICKS(100));
279 continue;
280 }
281
282 /* Send in bounded chunks, waiting briefly for space if needed. */
283 size_t sent = 0;
284 uint32_t waited_ms = 0;
285 while (sent < FRAME_TOTAL_SIZE) {
286 if (!tud_cdc_n_connected((uint8_t)g_data_port)) {
287 break;
288 }
289
290 size_t chunk = FRAME_TOTAL_SIZE - sent;
291 if (chunk > max_chunk) {
292 chunk = max_chunk;
293 }
294
295 uint32_t wa = tud_cdc_n_write_available((uint8_t)g_data_port);
296#if CONFIG_DIAG_CDC_ENABLE && CONFIG_DIAG_CDC_SAMPLE_WRITE_AVAILABLE
297 diag_cdc_sample_wa(wa);
298#endif
299 if (wa < chunk) {
300 if (waited_ms >= wait_budget_ms) {
301 ESP_LOGW(TAG, "CDC TX backpressure (%u ms), dropping frame",
302 (unsigned)waited_ms);
303 break;
304 }
305 vTaskDelay(pdMS_TO_TICKS(wait_step_ms));
306 waited_ms += wait_step_ms;
307 continue;
308 }
309
310 size_t queued =
311 tinyusb_cdcacm_write_queue(g_data_port, frm.bytes + sent, chunk);
312 if (queued == 0) {
313 if (waited_ms >= wait_budget_ms) {
314 ESP_LOGW(TAG, "CDC TX stalled (%u ms), dropping frame",
315 (unsigned)waited_ms);
316 break;
317 }
318 vTaskDelay(pdMS_TO_TICKS(wait_step_ms));
319 waited_ms += wait_step_ms;
320 continue;
321 }
322#if CONFIG_DIAG_CDC_ENABLE
323 diag_cdc_on_queued(queued);
324#endif
325 sent += queued;
326 }
327
328 if (sent < FRAME_TOTAL_SIZE) {
329 ESP_LOGW(TAG, "CDC send dropped %u bytes",
330 (unsigned)(FRAME_TOTAL_SIZE - sent));
331#if CONFIG_DIAG_CDC_ENABLE
332 /* Report diagnostics even when a frame is dropped. */
333 uint64_t now_ms = esp_timer_get_time() / 1000ULL;
334 diag_cdc_maybe_log(now_ms);
335#endif
336 continue;
337 }
338
340 ESP_LOGD(TAG, "CDC flush timeout (non-fatal)");
341 }
342#if CONFIG_STATUS_NEOPIXEL_ENABLE
343 if (g_led_strip) {
344 if (!g_led_lock ||
345 xSemaphoreTake(g_led_lock, pdMS_TO_TICKS(10)) == pdTRUE) {
346 uint8_t b = (uint8_t)CONFIG_STATUS_NEOPIXEL_BRIGHTNESS;
347 ESP_ERROR_CHECK(led_strip_set_pixel(g_led_strip, 0, 0, b, 0));
348 ESP_ERROR_CHECK(led_strip_refresh(g_led_strip));
349 if (g_led_lock) {
350 xSemaphoreGive(g_led_lock);
351 }
352 }
353 }
354#endif
355#if CONFIG_DIAG_CDC_ENABLE
356 /* periodic summary log (non-blocking) */
357 uint64_t now_ms = esp_timer_get_time() / 1000ULL;
358 diag_cdc_maybe_log(now_ms);
359
360 if (now_ms > last_stack_log_ms + 5000ULL) {
361 UBaseType_t cdc_hwm = uxTaskGetStackHighWaterMark(g_cdc_task_handle);
362 UBaseType_t adc_hwm = uxTaskGetStackHighWaterMark(g_adc_task_handle);
363 ESP_LOGI(TAG, "Stack HWM: cdc_tx=%lu words, adc_fft=%lu words",
364 (unsigned long)cdc_hwm, (unsigned long)adc_hwm);
365 last_stack_log_ms = now_ms;
366 }
367#endif
368 }
369 }
370}
#define ESP_LOGD
Definition esp_log.h:22
esp_err_t led_strip_refresh(led_strip_handle_t strip)
Refresh memory colors to LEDs.
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
Set RGB for a specific pixel.
#define FRAME_TOTAL_SIZE
Definition main/main.c:80
static bool safe_cdcacm_write_flush(tinyusb_cdcacm_itf_t port, int max_retries)
Definition main/main.c:221
size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, const uint8_t *in_buf, size_t in_size)
Write data to write buffer from a byte array.
bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf)
Check if the ACM initialized.

References frame_wire_t::bytes, ESP_LOGD, FRAME_TOTAL_SIZE, g_adc_task_handle, g_cdc_task_handle, g_data_port, g_frameq, led_strip_refresh(), led_strip_set_pixel(), safe_cdcacm_write_flush(), TAG, tinyusb_cdcacm_write_queue(), and tusb_cdc_acm_initialized().

Referenced by app_main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cfg_atten()

adc_atten_t cfg_atten ( void )
static

Definition at line 476 of file main/main.c.

476 {
477#if CONFIG_ADC_ATTEN_DB_0
478 return ADC_ATTEN_DB_0;
479#elif CONFIG_ADC_ATTEN_DB_2_5
480 return ADC_ATTEN_DB_2_5;
481#elif CONFIG_ADC_ATTEN_DB_6
482 return ADC_ATTEN_DB_6;
483#else
484 return ADC_ATTEN_DB_12;
485#endif
486}

Referenced by adc_fft_task().

Here is the caller graph for this function:

◆ frame_crc32()

uint32_t frame_crc32 ( const uint8_t * data,
size_t len )
static

Definition at line 126 of file main/main.c.

126 {
127 return esp_crc32_le(0, data, len);
128}

References data.

Referenced by build_frame().

Here is the caller graph for this function:

◆ perform_fft()

void perform_fft ( float * buf)
static

Definition at line 453 of file main/main.c.

453 {
454 dsps_fft4r_fc32(buf, FFT_SIZE >> 1);
455 dsps_bit_rev4r_fc32(buf, FFT_SIZE >> 1);
456 dsps_cplx2real_fc32(buf, FFT_SIZE >> 1);
457}
#define dsps_bit_rev4r_fc32
Definition dsps_fft4r.h:184
#define dsps_cplx2real_fc32
Definition dsps_fft4r.h:185
#define dsps_fft4r_fc32
Definition dsps_fft4r.h:182

References dsps_bit_rev4r_fc32, dsps_cplx2real_fc32, dsps_fft4r_fc32, FFT_SIZE, and OPTIMIZE_O2.

Referenced by adc_fft_task().

Here is the caller graph for this function:

◆ safe_cdcacm_write_flush()

bool safe_cdcacm_write_flush ( tinyusb_cdcacm_itf_t port,
int max_retries )
static

Definition at line 221 of file main/main.c.

222{
223 if (!tud_cdc_n_connected((uint8_t)port)) {
224 return false;
225 }
226
227 const int base_delay_ms = 100;
228 uint64_t now_ms = 0;
229 static uint64_t last_flush_log_ms = 0;
230
231 for (int attempt = 0; attempt < max_retries; ++attempt) {
232 /* Increase flush timeout to 1000ms to allow large buffers (32KB) to drain
233 * at minimum USB speeds (335KB/s -> ~96ms), with margin for host latency.
234 */
235 esp_err_t err = tinyusb_cdcacm_write_flush(port, 1000);
236 if (err == ESP_OK) {
237 return true;
238 }
239 if (!tud_cdc_n_connected((uint8_t)port)) {
240 return false;
241 }
242 vTaskDelay(pdMS_TO_TICKS(base_delay_ms << attempt));
243 }
244
245 /* Rate-limit the warning to once per second to avoid spam. */
246 now_ms = esp_timer_get_time() / 1000ULL;
247 if (now_ms > last_flush_log_ms + 1000ULL) {
248 ESP_LOGW(TAG, "CDC flush failed after %d retries", max_retries);
249 last_flush_log_ms = now_ms;
250 }
251 return false;
252}
esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks)
Send all data from a write buffer. Use tinyusb_cdcacm_write_queue to add data to the buffer.

References ESP_OK, TAG, and tinyusb_cdcacm_write_flush().

Referenced by cdc_tx_task().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sample_to_centered()

float sample_to_centered ( uint16_t raw,
bool calibrated,
int midpoint_mv )
inlinestatic

Definition at line 436 of file main/main.c.

437 {
438 if (calibrated) {
439 int mv = 0;
440 if (adc_cali_raw_to_voltage(cali_handle, raw, &mv) == ESP_OK) {
441 return ((float)(mv - midpoint_mv)) / 1000.0f;
442 }
443 }
444 return (float)raw - 2048.0f; // 12-bit midpoint
445}

References cali_handle, and ESP_OK.

Referenced by adc_fft_task().

Here is the caller graph for this function:

◆ sample_to_volts()

float sample_to_volts ( uint16_t raw,
bool calibrated )
inlinestatic

Definition at line 426 of file main/main.c.

426 {
427 if (calibrated) {
428 int mv = 0;
429 if (adc_cali_raw_to_voltage(cali_handle, raw, &mv) == ESP_OK) {
430 return (float)mv / 1000.0f;
431 }
432 }
433 return (float)raw; // fallback raw counts
434}

References cali_handle, and ESP_OK.

◆ write_u16_le()

void write_u16_le ( uint8_t * dst,
uint16_t value )
inlinestatic

Definition at line 109 of file main/main.c.

109 {
110 dst[0] = (uint8_t)(value & 0xFFu);
111 dst[1] = (uint8_t)((value >> 8) & 0xFFu);
112}

Referenced by build_frame().

Here is the caller graph for this function:

◆ write_u32_le()

void write_u32_le ( uint8_t * dst,
uint32_t value )
inlinestatic

Definition at line 114 of file main/main.c.

114 {
115 dst[0] = (uint8_t)(value & 0xFFu);
116 dst[1] = (uint8_t)((value >> 8) & 0xFFu);
117 dst[2] = (uint8_t)((value >> 16) & 0xFFu);
118 dst[3] = (uint8_t)((value >> 24) & 0xFFu);
119}

Referenced by build_frame(), and write_u64_le().

Here is the caller graph for this function:

◆ write_u64_le()

void write_u64_le ( uint8_t * dst,
uint64_t value )
inlinestatic

Definition at line 121 of file main/main.c.

121 {
122 write_u32_le(dst, (uint32_t)(value & 0xFFFFFFFFu));
123 write_u32_le(dst + 4, (uint32_t)((value >> 32) & 0xFFFFFFFFu));
124}

References write_u32_le().

Referenced by build_frame().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ cali_handle

adc_cali_handle_t cali_handle
static

◆ ch0

float* ch0 = NULL
static

Definition at line 59 of file main/main.c.

Referenced by adc_fft_task(), and app_main().

◆ ch1

float* ch1 = NULL
static

Definition at line 60 of file main/main.c.

Referenced by adc_fft_task(), and app_main().

◆ frame_payload_t

frame_payload_t

Definition at line 71 of file main/main.c.

◆ g_adc_task_handle

TaskHandle_t g_adc_task_handle
static

Definition at line 95 of file main/main.c.

Referenced by app_main(), and cdc_tx_task().

◆ g_cdc_task_handle

TaskHandle_t g_cdc_task_handle
static

Definition at line 94 of file main/main.c.

Referenced by app_main(), and cdc_tx_task().

◆ g_data_port

Definition at line 93 of file main/main.c.

Referenced by app_main(), and cdc_tx_task().

◆ g_frameq

QueueHandle_t g_frameq
static

Definition at line 91 of file main/main.c.

Referenced by adc_fft_task(), app_main(), and cdc_tx_task().

◆ g_seq

uint16_t g_seq
static

Definition at line 92 of file main/main.c.

Referenced by adc_fft_task().

◆ TAG

const char* TAG = "adc_fft"
static

Definition at line 31 of file main/main.c.

Referenced by adc_calibration_init(), adc_fft_task(), app_init(), app_init(), app_main(), app_main(), apply_path(), audio_process_task(), audio_process_task(), audio_process_task(), audio_read_task(), audio_read_task(), audio_read_task(), bsp_spi_init(), bsp_spi_init(), buttons_process_task(), buttons_process_task(), buttons_process_task(), cdc_obj_check(), cdc_tx_task(), dsp_display_brightness_init(), dsp_display_brightness_init(), dsp_display_new(), dsps_ccorr_f32_ansi(), dsps_conv_f32_ansi(), dsps_cplx_gen_freq_get(), dsps_cplx_gen_freq_set(), dsps_cplx_gen_init(), dsps_cplx_gen_phase_get(), dsps_cplx_gen_phase_set(), dsps_cplx_gen_set(), dsps_gen_bitrev2r_table(), dsps_gen_bitrev4r_table(), dsps_sfdr_f32(), dsps_snr_f32(), esp_tusb_deinit_console(), esp_tusb_init_console(), esp_vfs_tusb_cdc_register(), esp_vfs_tusb_cdc_unregister(), init_3d_matrix_struct(), init_3d_matrix_struct(), led_strip_clear(), led_strip_del(), led_strip_new_rmt_device(), led_strip_new_rmt_device(), led_strip_new_spi_device(), led_strip_refresh(), led_strip_rmt_del(), led_strip_rmt_del(), led_strip_rmt_refresh(), led_strip_rmt_refresh(), led_strip_rmt_set_pixel(), led_strip_rmt_set_pixel(), led_strip_rmt_set_pixel_rgbw(), led_strip_set_pixel(), led_strip_set_pixel_hsv(), led_strip_set_pixel_rgbw(), led_strip_spi_del(), led_strip_spi_refresh(), led_strip_spi_set_pixel(), led_strip_spi_set_pixel_rgbw(), microphone_read_task(), microphone_read_task(), redirect_std_streams_to(), restore_std_streams(), ringbuf_mux_take(), rmt_new_led_strip_encoder(), safe_cdcacm_write_flush(), tinyusb_cdc_deinit(), tinyusb_cdc_init(), tinyusb_cdcacm_read(), tinyusb_cdcacm_register_callback(), tinyusb_cdcacm_unregister_callback(), tinyusb_cdcacm_write_flush(), tinyusb_driver_install(), tud_cdc_line_state_cb(), tud_cdc_rx_cb(), tud_descriptor_string_cb(), tusb_cdc_acm_init(), tusb_cdc_comm_init(), tusb_cdc_data_init(), tusb_cdc_deinit_comm(), tusb_cdc_deinit_data(), tusb_device_task(), tusb_run_task(), tusb_set_descriptor(), tusb_stop_task(), wave_gen_init(), wave_gen_sdm_start(), wave_gen_start(), wave_gen_stop(), and wave_gen_task().

◆ win

float* win = NULL
static

Definition at line 61 of file main/main.c.

Referenced by app_main(), and apply_window().