/* This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include "espd.h" #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #ifdef PD_LYRAT #include "board.h" #endif /* PD_LYRAT */ #ifdef OBSOLETEAPI #include "driver/i2s.h" #else /* OBSOLETEAPI */ #include "driver/i2s_std.h" #include "driver/gpio.h" #endif /* OBSOLETEAPI */ #include "esp_log.h" #include "nvs.h" #include "nvs_flash.h" #include "esp_timer.h" #ifdef PD_USE_CONSOLE #include "driver/uart.h" #include "esp_console.h" #endif static const char *TAG = "ESPD"; extern void pdmain_tick( void); void pdmain_init( void); void sd_init( void); static i2s_chan_handle_t tx_handle; #ifdef USEADC static i2s_chan_handle_t rx_handle; #endif #define BLKSIZE 64 float soundin[IOCHANS * BLKSIZE], soundout[IOCHANS * BLKSIZE]; static int espd_printdacs; void senddacs( void) { int i, j, ret; static int count; size_t transferred; short poodle[IOCHANS * BLKSIZE]; for (i = j = 0; i < BLKSIZE; i++, j += IOCHANS) { int ch1 = floor(0.5 + 32768.*soundout[i]); #if IOCHANS > 1 int ch2 = floor(0.5 + 32768.*soundout[i+BLKSIZE]); #endif if (ch1 > 32767) ch1 = 32767; else if (ch1 < -32768) ch1 = -32768; ch1 &= 0xffff; #if IOCHANS > 1 if (ch2 > 32767) ch2 = 32767; else if (ch2 < -32768) ch2 = -32768; ch2 &= 0xffff; #endif poodle[j] = ch1; soundout[i] = 0; #if IOCHANS > 1 poodle[j+1] = ch2; soundout[i+BLKSIZE] = 0; #endif } if (espd_printdacs && count++ > 250) { ESP_LOGI(TAG, "sample %lx", poodle[0]); count = 0; } #ifdef OBSOLETEAPI ret = i2s_write(I2S_NUM_0, poodle, sizeof(poodle), &transferred, portMAX_DELAY); if (ret != ESP_OK) ESP_LOGE(TAG, "error writing"); #else i2s_channel_write(tx_handle, poodle, sizeof(poodle), &transferred, portMAX_DELAY); #endif #ifdef USEADC #ifdef OBSOLETEAPI ret = i2s_read(I2S_NUM_0, poodle, sizeof(poodle), &transferred, portMAX_DELAY); if (ret != ESP_OK) ESP_LOGE(TAG, "error reading"); #else i2s_channel_read(rx_handle, poodle, sizeof(poodle), &transferred, portMAX_DELAY); #endif for (i = j = 0; i < BLKSIZE; i++, j += IOCHANS) { uint32_t ch1 = poodle[j] & 0xffff; #if IOCHANS > 1 uint32_t ch2 = poodle[j+1] & 0xffff; if (ch2 & 0x8000) soundin[i+BLKSIZE] = (ch2*(1./32768.)) - 2; else soundin[i+BLKSIZE] = (ch2*(1./32768.)); #endif if (ch1 & 0x8000) soundin[i] = (ch1*(1./32768.)) - 2; else soundin[i] = (ch1*(1./32768.)); } #endif /* USEADC */ } #if OBSOLETEAPI /* allow deprecated form if new one unavailable */ #ifndef I2S_COMM_FORMAT_STAND_I2S #define I2S_COMM_FORMAT_STAND_I2S I2S_COMM_FORMAT_I2S #endif static void initdacs( void) { i2s_config_t i2s_config = { .mode = (I2S_MODE_MASTER | I2S_MODE_TX #ifdef USEADC | I2S_MODE_RX #endif ), .sample_rate = 48000, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, #if IOCHANS > 1 .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, #else .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, #endif .communication_format = I2S_COMM_FORMAT_STAND_I2S, .dma_buf_count = 16, .dma_buf_len = 256, #ifdef PD_LYRAT .use_apll=1, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, #else .use_apll=0, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, /* high interrupt priority */ .tx_desc_auto_clear= true, .fixed_mclk=-1 #endif }; ESP_LOGI(TAG, "[ 1 ] Start audio codec chip"); #ifdef PD_LYRAT audio_board_handle_t board_handle = audio_board_init(); audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START); audio_hal_set_volume(board_handle->audio_hal, 100); #endif i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); { #ifdef PD_LYRAT i2s_pin_config_t i2s_pin_cfg; get_i2s_pins(I2S_NUM_0, (board_i2s_pin_t *)(&i2s_pin_cfg)); #else /* PD_LYRAT */ i2s_pin_config_t i2s_pin_cfg = { #if 1 /* generic board 1 - edit this as needed */ .bck_io_num = 13, /* bit clock */ .ws_io_num = 33, /* Word select, aka left right clock */ .data_out_num = 32, /* Data out from ESP32, to DIN on 38357A */ .data_in_num = 35 /* data from ADC */ #endif #if 0 /* generic board 2 */ .bck_io_num = 33, /* bit clock */ .ws_io_num = 25, /* Word select, aka left right clock */ .data_out_num = 32, /* Data out from ESP32, to DIN on 38357A */ .data_in_num = I2S_PIN_NO_CHANGE /* no ADC */ #endif }; #endif /* PD_LYRAT */ i2s_set_pin(I2S_NUM_0, &i2s_pin_cfg); } } #else /* OBSOLETEAPI */ static void initdacs( void) { /* Get the default channel configuration by the helper macro. * This helper macro is defined in `i2s_common.h` and shared by all the I2S communication modes. */ i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); /* Allocate a new TX channel and get the handle of this channel */ #ifdef USEADC i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle); #else i2s_new_channel(&chan_cfg, &tx_handle, NULL); #endif /* Setting the configurations, the slot configuration and clock configuration can be generated by the macros defined in `i2s_std.h` which can only be used in STD mode. They can help to specify the slot and clock configurations for initialization or updating */ i2s_std_config_t std_cfg = { .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000), .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), .gpio_cfg = { .mclk = I2S_GPIO_UNUSED, .bclk = 13, .ws = 33, .dout = 32, .din = 35, .invert_flags = { .mclk_inv = false, .bclk_inv = false, .ws_inv = false, }, }, }; /* Initialize the channel */ i2s_channel_init_std_mode(tx_handle, &std_cfg); /* Before writing data, start the TX channel first */ i2s_channel_enable(tx_handle); #ifdef USEADC i2s_channel_init_std_mode(rx_handle, &std_cfg); i2s_channel_enable(rx_handle); #endif } #endif /* OBSOLETEAPI */ static int audiostate; void sys_set_audio_state(int onoff) { /* if (onoff && !audiostate) i2s_start(I2S_NUM_0); else if (!onoff && audiostate) i2s_stop(I2S_NUM_0); */ audiostate = onoff; } /* queue from host. Need to make this a proper RTOS queue */ void *getbytes(size_t nbytes); void freebytes(void *x, size_t nbytes); void *resizebytes(void *x, size_t oldsize, size_t newsize); static char *pd_bt_buf; static int pd_bt_size; static SemaphoreHandle_t pd_bt_mutex; #include /* enqueue a message from host to Pd */ void pd_fromhost(char *data, size_t size) { if (!pd_bt_buf) pd_bt_buf = getbytes(0); if (!pd_bt_mutex) pd_bt_mutex = xSemaphoreCreateMutex(); while (xSemaphoreTake(pd_bt_mutex, 1) != pdTRUE) ; pd_bt_buf = (char *)resizebytes(pd_bt_buf, pd_bt_size, pd_bt_size+size); memcpy(pd_bt_buf + pd_bt_size, data, size); pd_bt_size += size; xSemaphoreGive(pd_bt_mutex); } #ifdef PD_USE_CONSOLE static QueueHandle_t uart_queue; static void console_init( void) { const int uart_buffer_size = 256; ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, uart_buffer_size, uart_buffer_size, 10, &uart_queue, 0)); } #endif /* dispatch messages enqueued above */ void pd_pollhost( void) { int lastchar; #ifdef PD_USE_CONSOLE uint8_t data[128]; int length = 0; ESP_ERROR_CHECK(uart_get_buffered_data_len(CONFIG_ESP_CONSOLE_UART_NUM, (size_t*)&length)); if (length > 0) { int i; /* ESP_LOGI(TAG, "serial in %d", length); */ length = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, length, 100); for (i = 0; i < length; i++) { char foo[80]; ESP_LOGI(TAG, " %d", data[i] & 0xff); sprintf(foo, "key %d;", data[i] & 0xff); pd_sendmsg(foo, strlen(foo)); } } #endif if (!pd_bt_mutex) pd_bt_mutex = xSemaphoreCreateMutex(); if (xSemaphoreTake(pd_bt_mutex, 0) != pdTRUE) return; /* only interpret this text if terminated by a semicolon */ lastchar = pd_bt_size-1; while (lastchar >= 0 && isspace((int)(pd_bt_buf[lastchar]))) lastchar--; if (lastchar >= 3 && pd_bt_buf[lastchar] == ';' && pd_bt_buf[lastchar-1] != '\\') { pd_sendmsg(pd_bt_buf, pd_bt_size); pd_bt_buf = (char *)resizebytes(pd_bt_buf, pd_bt_size, 0); pd_bt_size = 0; } xSemaphoreGive(pd_bt_mutex); } /* this doesn't work as a printhook yet since posts are split into atoms */ void pdmain_print( const char *s) { char y[81]; strncpy(y, s, 79); y[79]=0; strcat(y, ";"); #ifdef PD_USE_BLUETOOTH if (strlen(y) > 0) pd_bt_writeback((unsigned char *)y, strlen(y)); #endif #ifdef PD_USE_WIFI net_sendudp(y, strlen(y), CONFIG_ESP_WIFI_SENDPORT); net_sendtcp(y, strlen(y)); #endif } void trymem(int foo); void app_main(void) { esp_log_level_set("*", ESP_LOG_WARN); esp_log_level_set(TAG, ESP_LOG_INFO); pdmain_init(); initdacs(); #ifdef PD_USE_BLUETOOTH bt_init(); #endif #ifdef PD_USE_SDCARD sd_init(); #endif #ifdef PD_USE_WIFI ESP_LOGI(TAG, "[ 1a ] start network"); wifi_init(); net_init(); net_hello(); #endif #ifdef PD_USE_CONSOLE console_init(); #endif ESP_LOGI(TAG, "[ 2 ] now write some shit"); while (1) { /* int zz = 0; if (!((zz++)%1000)) { trymem(5); ESP_LOGI(TAG, "tick"); } */ pd_pollhost(); pdmain_tick(); senddacs(); #ifdef PD_USE_WIFI net_alive(); #endif } } #ifdef PD_USE_SDCARD void sd_init( void) { /* initialize SD card */ ESP_LOGI(TAG, "[ 1 ] Mount sdcard"); // Initialize peripherals management esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG(); esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg); // Initialize SD Card peripheral audio_board_sdcard_init(set, SD_MODE_1_LINE); ESP_LOGI(TAG, "[ 1b ] done starting network"); } #endif static void espd_printtimediff( void) { static int64_t whensent; int64_t newtime = esp_timer_get_time(); int elapsed = (newtime - whensent)/1000; char msg[80]; whensent = newtime; sprintf(msg, "elapsed msec %d\n", elapsed); pdmain_print(msg); } #define t_floatarg float void glob_foo(void *dummy, t_floatarg f) { if (f == 0) espd_printdacs = 0; else if (f == 1) espd_printdacs = 1; else if (f == 2) trymem(2); else if (f == 3) espd_printtimediff(); }