effects split into functions

This commit is contained in:
Patrick Moessler 2025-03-09 03:12:51 +01:00
parent f5e663b724
commit 36f896db35
2 changed files with 327 additions and 42 deletions

View file

@ -2,5 +2,6 @@ idf_component_register(SRCS "ledstick_main.c"
PRIV_REQUIRES
spi_flash
esp_driver_i2s
esp_driver_uart
# esp_driver_uart
esp_driver_spi
INCLUDE_DIRS "")

View file

@ -12,8 +12,13 @@
#include "sdkconfig.h"
#include "driver/i2s_std.h"
#include "driver/uart.h"
#include "driver/spi_master.h"
// #include "driver/uart.h"
#include "hal/spi_ll.h"
#include "esp_dsp.h"
#include "esp_random.h"
static const char *TAG = "main";
@ -32,7 +37,7 @@ complex format: buf[0]=re[0], buf[1]=im[0]
#define I2S_MIC_WS_PIN GPIO_NUM_6
#define SAMPLE_RATE 24000
#define SAMPLE_COUNT 512
#define SAMPLE_COUNT 1024
#define SAMPLE_CHANNELS 1
#define SAMPLE_TYPE int32_t
#define SAMPLE_MAX 2147483648.0f
@ -41,6 +46,8 @@ complex format: buf[0]=re[0], buf[1]=im[0]
#define BANDS_COUNT 3
#define LEDS_COUNT 72
// ----------------------------------------------------------------
/*
@ -66,6 +73,7 @@ treb: 43-255
#define ABS(T, v) ((v < 0 ? (((T)0) - v) : v))
#define MAX(a, b) ((b > a) ? (b) : (a))
#define MIN(a, b) ((b < a) ? (b) : (a))
#define DEC(v, delta) (MAX(v, delta) - delta)
#define SUM_BAND(IDX, BUF, START, END) \
current_powers[IDX] = 0; \
@ -75,7 +83,20 @@ treb: 43-255
} \
current_powers[IDX] /= (END + 1 - START);
i2s_chan_handle_t rx_handle = NULL;
#define RGB(R, G, B, V) ((uint32_t)(0xe0) | (V) | (B << 8) | (G << 16) | (R << 24))
#define SET_RGB(old, R, G, B) ((old&0x000000E0) | (uint32_t)(0xe0) | (V) | (B << 8) | (G << 16) | (R << 24)))
#define SET_R(old, R) ((old & 0x00FFFFFF) | (R << 24))
#define SET_G(old, G) ((old & 0xFF00FFFF) | (G << 16))
#define SET_B(old, B) ((old & 0xFFFF00FF) | (B << 8))
#define SET_V(old, V) ((old & 0xFFFFFFE0) | (V))
#define GET_R(color) ((color & 0xFF000000) >> 24)
#define GET_G(color) ((color & 0x00FF0000) >> 16)
#define GET_B(color) ((color & 0x0000FF00) >> 8)
#define GET_V(color) ((color & 0x0000001F))
spi_device_handle_t led_spi_dev = NULL;
i2s_chan_handle_t i2s_rx_channel = NULL;
TaskHandle_t proc_task = NULL;
TaskHandle_t output_task = NULL;
@ -90,14 +111,21 @@ float avg_powers[BANDS_COUNT];
SAMPLE_TYPE floating_max;
// float recursive_add_f(float* buffer, int start, int end) {
/* 111v.vvvv gggg.gggg rrrr.rrrr bbbb.bbbb */
typedef struct LedDataStruct
{
// SK9822 has one start and one stop word
uint32_t zeros;
uint32_t buffer[LEDS_COUNT];
uint32_t ones;
} LedData;
// }
LedData leds;
void app_init_mic(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &i2s_rx_channel));
i2s_std_config_t i2s_rx_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
@ -129,38 +157,288 @@ void app_init_mic(void)
},
},
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &i2s_rx_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_rx_channel, &i2s_rx_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_channel));
}
void app_init_led_spi()
{
esp_err_t ret;
spi_bus_config_t bus_cfg = {
.mosi_io_num = 11,
.sclk_io_num = 12,
.miso_io_num = -1,
.quadhd_io_num = -1,
.quadwp_io_num = -1,
.data4_io_num = -1,
.data5_io_num = -1,
.data6_io_num = -1,
.data7_io_num = -1,
.flags =
SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_IOMUX_PINS | SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MOSI,
.max_transfer_sz = sizeof(leds),
};
ret = spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
ESP_ERROR_CHECK(ret);
spi_device_interface_config_t dev_cfg = {
.clock_speed_hz = 10000000,
.spics_io_num = 10,
.mode = 3,
.queue_size = 1,
.cs_ena_pretrans = 1,
.cs_ena_posttrans = 1,
.command_bits = 0,
.address_bits = 0,
.dummy_bits = 0,
};
ret = spi_bus_add_device(SPI2_HOST, &dev_cfg, &led_spi_dev);
ESP_ERROR_CHECK(ret);
ret = spi_device_acquire_bus(led_spi_dev, portMAX_DELAY);
ESP_ERROR_CHECK(ret);
// spi_ll_master_set_mode(&GPSPI2, 3);
// spi_ll_set_mosi_free_level(&GPSPI2, 1);
// spi_ll_apply_config(&GPSPI2);
}
// static void app_output_task(void *args)
// {
// uint32_t in_fft_buf_idx;
// const uint8_t sync_word[] = {0x00, 0x11, 0x80, 0x00, 0x7f, 0xff, 0x11, 0x00};
// while (1)
// {
// xTaskNotifyWait(0, 0, &in_fft_buf_idx, portMAX_DELAY);
// if (in_fft_buf_idx >= BUF_COUNT)
// {
// printf("output buf count wrong: %ld", in_fft_buf_idx);
// continue;
// }
// float *fft_buf = fft_buffer + in_fft_buf_idx * SAMPLE_COUNT;
// // buf[SAMPLE_COUNT - 1] = floating_max;
// uart_write_bytes(UART_NUM_0, sync_word, sizeof(sync_word));
// xSemaphoreTake(output_mutex, portMAX_DELAY);
// uart_write_bytes(UART_NUM_0, &floating_max, sizeof(floating_max));
// uart_write_bytes(UART_NUM_0, avg_powers, sizeof(avg_powers));
// uart_write_bytes(UART_NUM_0, current_powers, sizeof(current_powers));
// xSemaphoreGive(output_mutex);
// uart_write_bytes(UART_NUM_0, fft_buf, sizeof(float) * SAMPLE_COUNT / 2);
// }
// }
static uint32_t random_at_most(uint32_t max)
{
// impl from https://stackoverflow.com/a/6852396, adapted to uint32/2
// Assumes 0 <= max <= INT32_MAX
// Returns in the closed interval [0, max]
uint32_t
// max <= INT32_MAX < UINT32_MAX, so this is okay.
num_bins = (uint32_t)max + 1,
num_rand = (uint32_t)INT32_MAX + 1, bin_size = num_rand / num_bins, defect = num_rand % num_bins;
long x;
do
{
x = esp_random() >> 1;
}
// This is carefully written not to overflow
while (num_rand - defect <= (uint32_t)x);
// Truncated division is intentional
return x / bin_size;
}
static void app_init_led_buffer()
{
leds.zeros = 0;
leds.ones = 0; // sic. works as well, and triggers PWM change at the end of frame transfer instead of next one.
for (int i = 0; i < LEDS_COUNT; i++)
{
leds.buffer[i] = RGB(0, 0, 0, 0);
}
}
static inline TickType_t app_effect_blob_run()
{
uint32_t in_fft_buf_idx;
if (xTaskNotifyWait(0, 0, &in_fft_buf_idx, pdMS_TO_TICKS(100)) == pdPASS)
{
if (in_fft_buf_idx >= BUF_COUNT)
{
printf("output buf count wrong: %ld", in_fft_buf_idx);
return pdMS_TO_TICKS(10);
}
xSemaphoreTake(output_mutex, portMAX_DELAY);
if (current_powers[0] > 1.2 * avg_powers[0])
{
leds.buffer[0] = RGB(255, 0, 0, 31);
}
xSemaphoreGive(output_mutex);
}
for (int i = 0; i < LEDS_COUNT / 2 - 1; i++)
{
leds.buffer[LEDS_COUNT / 2 + i] = leds.buffer[LEDS_COUNT / 2 + i + 1];
leds.buffer[LEDS_COUNT / 2 - 1 - i] = leds.buffer[LEDS_COUNT / 2 - 1 - i - 1];
}
xSemaphoreTake(output_mutex, portMAX_DELAY);
uint32_t new_color =
RGB(GET_R(leds.buffer[0]) >> 1, GET_G(leds.buffer[0]) >> 1, GET_B(leds.buffer[0]) >> 1, GET_V(leds.buffer[0]));
if (current_powers[0] > 1.35 * avg_powers[0])
{
new_color = SET_R(new_color, 255);
}
if (current_powers[1] > 1.35 * avg_powers[1])
{
new_color = SET_G(new_color, 255);
}
if (current_powers[2] > 1.35 * avg_powers[2])
{
new_color = SET_B(new_color, 255);
}
xSemaphoreGive(output_mutex);
leds.buffer[0] = new_color;
leds.buffer[LEDS_COUNT - 1] = new_color;
return pdMS_TO_TICKS(10);
}
static inline TickType_t app_effect_bass_sparks()
{
static uint32_t bass_color = RGB(0, 0, 0, 0);
// if (xTaskNotifyWait(0, 0, &in_fft_buf_idx, pdMS_TO_TICKS(100)) == pdPASS)
// {
// if (in_fft_buf_idx >= BUF_COUNT)
// {
// printf("output buf count wrong: %ld", in_fft_buf_idx);
// return pdMS_TO_TICKS(10);
// }
// }
xSemaphoreTake(output_mutex, portMAX_DELAY);
if (current_powers[0] > 1.25 * avg_powers[0])
{
bass_color = RGB(127, 0, 255, 4);
}
for (int i = 0; i < LEDS_COUNT; i++)
{
leds.buffer[i] = bass_color;
}
// bass_color = SET_V(bass_color, MAX(GET_V(bass_color), 1) - 1);
// bass_color = RGB(GET_R(bass_color) >> 1, GET_G(bass_color) >> 1, GET_B(bass_color) >> 1, GET_V(bass_color));
bass_color =
RGB(DEC(GET_R(bass_color), 3), DEC(GET_G(bass_color), 5), DEC(GET_B(bass_color), 6), GET_V(bass_color));
if ((current_powers[1] > 1.35 * avg_powers[1]) && (current_powers[2] > 1.35 * avg_powers[2]))
{
for (int i = 0; i < 10; i++)
{
uint32_t led_idx = random_at_most(LEDS_COUNT);
leds.buffer[led_idx] = RGB(255, 255, 255, 31);
}
}
xSemaphoreGive(output_mutex);
return pdMS_TO_TICKS(10);
}
static inline TickType_t app_effect_fft_colors()
{
uint32_t in_fft_buf_idx;
if (xTaskNotifyWait(0, 0, &in_fft_buf_idx, pdMS_TO_TICKS(1)) == pdPASS)
{
if (in_fft_buf_idx >= BUF_COUNT)
{
printf("output buf count wrong: %ld", in_fft_buf_idx);
return pdMS_TO_TICKS(10);
}
float *fft_buf = fft_buffer + in_fft_buf_idx * SAMPLE_COUNT;
for (int i = 0; i < LEDS_COUNT && i < SAMPLE_COUNT / 4; i++)
{
uint32_t new_color = RGB(0, 0, 0, 2);
if (fft_buf[i * 2] < 0.3)
{
new_color = SET_R(new_color, (uint8_t)(fft_buf[i * 2] * 700));
}
else if (fft_buf[i * 2] < 0.6)
{
new_color = SET_G(new_color, (uint8_t)(fft_buf[i * 2] * 400));
}
else
{
new_color = SET_B(new_color, (uint8_t)(fft_buf[i * 2] * 200));
}
leds.buffer[i] = new_color;
}
}
xSemaphoreTake(output_mutex, portMAX_DELAY);
uint32_t new_color =
RGB(GET_R(leds.buffer[0]) >> 1, GET_G(leds.buffer[0]) >> 1, GET_B(leds.buffer[0]) >> 1, GET_V(leds.buffer[0]));
if (current_powers[0] > 1.35 * avg_powers[0])
{
new_color = SET_R(new_color, 255);
}
if (current_powers[1] > 1.35 * avg_powers[1])
{
new_color = SET_G(new_color, 255);
}
if (current_powers[2] > 1.35 * avg_powers[2])
{
new_color = SET_B(new_color, 255);
}
xSemaphoreGive(output_mutex);
return pdMS_TO_TICKS(10);
}
static void app_output_task(void *args)
{
uint32_t in_fft_buf_idx;
esp_err_t ret;
const uint8_t sync_word[] = {0x00, 0x11, 0x80, 0x00, 0x7f, 0xff, 0x11, 0x00};
app_init_led_spi();
app_init_led_buffer();
spi_transaction_t trans = {
.length = sizeof(leds) * 8, // in bits
.tx_buffer = (uint8_t *)&leds,
};
TickType_t last_ticks = xTaskGetTickCount();
while (1)
{
xTaskNotifyWait(0, 0, &in_fft_buf_idx, portMAX_DELAY);
if (in_fft_buf_idx >= BUF_COUNT)
{
printf("output buf count wrong: %ld", in_fft_buf_idx);
continue;
}
float *fft_buf = fft_buffer + in_fft_buf_idx * SAMPLE_COUNT;
// TickType_t next_ticks = app_effect_blob_run();
// TickType_t next_ticks = app_effect_fft_colors();
TickType_t next_ticks = app_effect_bass_sparks();
// buf[SAMPLE_COUNT - 1] = floating_max;
ret = spi_device_transmit(led_spi_dev, &trans);
ESP_ERROR_CHECK(ret);
uart_write_bytes(UART_NUM_0, sync_word, sizeof(sync_word));
xTaskDelayUntil(&last_ticks, next_ticks);
xSemaphoreTake(output_mutex, portMAX_DELAY);
uart_write_bytes(UART_NUM_0, &floating_max, sizeof(floating_max));
uart_write_bytes(UART_NUM_0, avg_powers, sizeof(avg_powers));
uart_write_bytes(UART_NUM_0, current_powers, sizeof(current_powers));
xSemaphoreGive(output_mutex);
uart_write_bytes(UART_NUM_0, fft_buf, sizeof(float) * SAMPLE_COUNT / 2);
// uart_write_bytes(UART_NUM_0, fft_buf, sizeof(float) * SAMPLE_COUNT / 2);
}
}
@ -230,9 +508,15 @@ static void app_process_task(void *args)
xSemaphoreTake(output_mutex, portMAX_DELAY);
SUM_BAND(0, fft_buf, 1, 4);
SUM_BAND(1, fft_buf, 5, 45);
SUM_BAND(2, fft_buf, 46, 255);
SUM_BAND(0, fft_buf, 1, 8);
SUM_BAND(1, fft_buf, 9, 86);
SUM_BAND(2, fft_buf, 87, 470);
// 512
// SUM_BAND(0, fft_buf, 1, 4);
// SUM_BAND(1, fft_buf, 5, 45);
// SUM_BAND(2, fft_buf, 46, 255);
// SUM_BAND(0, fft_buf, 1, 4);
// SUM_BAND(1, fft_buf, 5, 42);
// SUM_BAND(2, fft_buf, 43, 511);
@ -259,17 +543,17 @@ void app_main(void)
app_init_mic();
uart_config_t uart_cfg = {
.baud_rate = 1000000,
.data_bits = UART_DATA_8_BITS,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
};
uart_param_config(UART_NUM_0, &uart_cfg);
uart_set_pin(UART_NUM_0, 17, 18, -1, -1);
// uart_config_t uart_cfg = {
// .baud_rate = 1000000,
// .data_bits = UART_DATA_8_BITS,
// .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
// .parity = UART_PARITY_DISABLE,
// .stop_bits = UART_STOP_BITS_1,
// };
// uart_param_config(UART_NUM_0, &uart_cfg);
// uart_set_pin(UART_NUM_0, 17, 18, -1, -1);
uart_driver_install(UART_NUM_0, 256, 256, 0, NULL, 0);
// uart_driver_install(UART_NUM_0, 256, 256, 0, NULL, 0);
ret = dsps_fft2r_init_fc32(NULL, SAMPLE_COUNT >> 1);
if (ret != ESP_OK)
@ -292,8 +576,8 @@ void app_main(void)
while (1)
{
if (i2s_channel_read(rx_handle, (char *)i2s_readraw_buff + (BUF_SIZE_BYTES * next_rx_buf_idx), BUF_SIZE_BYTES,
&bytes_read, 1000) == ESP_OK)
if (i2s_channel_read(i2s_rx_channel, (char *)i2s_readraw_buff + (BUF_SIZE_BYTES * next_rx_buf_idx),
BUF_SIZE_BYTES, &bytes_read, 1000) == ESP_OK)
{
xTaskNotify(proc_task, next_rx_buf_idx, eSetValueWithOverwrite);
next_rx_buf_idx = (next_rx_buf_idx + 1) & (BUF_COUNT - 1);