├── .cproject ├── .project ├── CMakeLists.txt ├── README.md └── main ├── CMakeLists.txt ├── Kconfig.projbuild └── main.c /.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ESP32_Scope1 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.core.cBuilder 10 | clean,full,incremental, 11 | 12 | 13 | 14 | 15 | 16 | org.eclipse.cdt.core.cnature 17 | org.eclipse.cdt.core.ccnature 18 | com.espressif.idf.core.idfNature 19 | 20 | 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(app-template) 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ESP32 Sampling audio using I2S ADC and piping it to a remote PC via UDP 2 | ======================== 3 | The demo samples audio using I2S. The sampled buffer is then transmitted to a PC via UDP socket. The audio is then played using aplay utility. 4 | 5 | ### Hardware Required 6 | This example is able to run on any commonly available ESP32 development board. The ADC input should be connected to ADC1 Channel 0 pin. 7 | 8 | ### Wireless Configuration 9 | Setup your wireless SSID and password in sdkconfig file, via menuconfig, or use esp_wifi_set_config() API. 10 | 11 | ### PC Configuration 12 | Make sure alsa is installed. If not, please install alsa package first: 13 | 14 | `sudo apt-get install alsa-utils` 15 | 16 | Use netcat (nc) to open a UDP port. Then pipe the raw values to aplay as shown below: 17 | 18 | `nc -ul 7777 | aplay -r 16000 -f S16_BE` 19 | 20 | 21 | -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Edit following two lines to set component requirements (see docs) 2 | set(COMPONENT_REQUIRES ) 3 | set(COMPONENT_PRIV_REQUIRES ) 4 | 5 | set(COMPONENT_SRCS "main.c") 6 | set(COMPONENT_ADD_INCLUDEDIRS "") 7 | 8 | register_component() 9 | -------------------------------------------------------------------------------- /main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | # put here your custom config value 2 | menu "Example Configuration" 3 | config ESP_WIFI_SSID 4 | string "WiFi SSID" 5 | default "myssid" 6 | help 7 | SSID (network name) for the example to connect to. 8 | 9 | config ESP_WIFI_PASSWORD 10 | string "WiFi Password" 11 | default "mypassword" 12 | help 13 | WiFi password (WPA or WPA2) for the example to use. 14 | endmenu 15 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | /* example: 2 | * nc -ul 7777 | aplay -r 16000 -f S16_BE 3 | * 4 | */ 5 | 6 | 7 | #include "freertos/FreeRTOS.h" 8 | #include "esp_wifi.h" 9 | #include "esp_system.h" 10 | #include "esp_event.h" 11 | #include "esp_event_loop.h" 12 | #include "nvs_flash.h" 13 | #include "driver/gpio.h" 14 | 15 | #include "driver/gpio.h" 16 | #include "driver/adc.h" 17 | #include "driver/timer.h" 18 | #include 19 | #include 20 | #include "lwip/err.h" 21 | #include "lwip/sockets.h" 22 | #include "lwip/sys.h" 23 | #include 24 | #include "esp_log.h" 25 | #include "driver/i2s.h" 26 | 27 | // local setup 28 | #define ADC_SAMPLES_COUNT 512 29 | #define SAMPLE_RATE 16000 30 | #define HOST_IP_ADDR "192.168.86.31" 31 | #define HOST_PORT 7777 32 | 33 | #define I2S_SAMPLE_RATE 44100 34 | #define I2S_BUFFER 512 35 | 36 | // ADC buffer 37 | uint8_t abuf[ADC_SAMPLES_COUNT]; 38 | uint8_t abuf_tx[ADC_SAMPLES_COUNT]; 39 | int16_t abufPos = 0; 40 | 41 | // timer stuff 42 | static intr_handle_t s_timer_handle; 43 | portMUX_TYPE DRAM_ATTR timerMux = portMUX_INITIALIZER_UNLOCKED; 44 | 45 | // RTOS stuff 46 | TaskHandle_t udp_task_handle; 47 | TaskHandle_t i2s_task_handle; 48 | 49 | // debug tag 50 | static const char *TAG = "Scope1:"; 51 | int wifi_init = false; 52 | 53 | 54 | 55 | //----------------------------------------------------------------------------------------// 56 | // I2S setup for ADC sampling 57 | //----------------------------------------------------------------------------------------// 58 | void init_i2s() 59 | { 60 | // configure I2S 61 | i2s_config_t i2s_config; 62 | i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN; 63 | i2s_config.sample_rate = I2S_SAMPLE_RATE; 64 | i2s_config.dma_buf_len = I2S_BUFFER; 65 | i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; 66 | //i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT; 67 | i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; 68 | i2s_config.use_apll = false, 69 | i2s_config.communication_format = I2S_COMM_FORMAT_PCM; 70 | i2s_config.intr_alloc_flags = 0; 71 | i2s_config.dma_buf_count = 5; 72 | 73 | // install and start i2s driver 74 | ESP_ERROR_CHECK( adc_gpio_init(ADC_UNIT_1, ADC_CHANNEL_0) ); 75 | ESP_ERROR_CHECK( i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL) ); 76 | ESP_ERROR_CHECK( i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0) ); 77 | 78 | //vTaskDelay(5000 / portTICK_RATE_MS); 79 | //printf("Done waiting, enable ADC... \n"); 80 | 81 | ESP_ERROR_CHECK( i2s_adc_enable(I2S_NUM_0) ); 82 | } 83 | 84 | 85 | void disp_buf(uint8_t* buf, int length) 86 | { 87 | printf("======\n"); 88 | for (int i = 0; i < length; i++) { 89 | printf("%02x ", buf[i]); 90 | if ((i + 1) % 16 == 0) { 91 | printf("\n"); 92 | } 93 | } 94 | printf("======\n"); 95 | } 96 | 97 | 98 | //----------------------------------------------------------------------------------------// 99 | // ADC sampling through I2S 100 | //----------------------------------------------------------------------------------------// 101 | void sample_i2s() 102 | { 103 | ESP_LOGI(TAG, "Task called."); 104 | //uint16_t i2s_buff[I2S_BUFFER]; 105 | int i2s_read_len = I2S_BUFFER; 106 | char* i2s_buff = (char*) calloc(i2s_read_len, sizeof(char)); 107 | 108 | // send UDP data 109 | int addr_family = 0; 110 | int ip_protocol = 0; 111 | char host_ip[] = HOST_IP_ADDR; 112 | 113 | struct sockaddr_in dest_addr; 114 | dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR); 115 | dest_addr.sin_family = AF_INET; 116 | dest_addr.sin_port = htons(HOST_PORT); 117 | addr_family = AF_INET; 118 | ip_protocol = IPPROTO_IP; 119 | int sock = -1; 120 | sock = socket(addr_family, SOCK_DGRAM, ip_protocol); 121 | if (sock < 0) { 122 | ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); 123 | } 124 | ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, HOST_PORT); 125 | 126 | 127 | while (true) { 128 | 129 | size_t bytes_read = 0; 130 | //ESP_ERROR_CHECK( i2s_adc_enable(I2S_NUM_0) ); 131 | 132 | //ESP_LOGI(TAG, "before call."); 133 | ESP_ERROR_CHECK( i2s_read(I2S_NUM_0, (void*) i2s_buff, i2s_read_len, &bytes_read, portMAX_DELAY) ); 134 | //ESP_LOGI(TAG, "after call."); 135 | 136 | //ESP_LOGI(TAG,"I2s bytes read: %d", bytes_read); 137 | 138 | if (bytes_read == 0) 139 | continue; 140 | 141 | //vTaskDelay(10 / portTICK_RATE_MS); 142 | 143 | memcpy(abuf_tx, i2s_buff, bytes_read); 144 | 145 | // // convert to bytes 146 | // for (int i=0; i < bytes_read; i++) { 147 | // abuf_tx[abufPos++] = (i2s_buff[i] >> 8) & 0xFF; 148 | // abuf_tx[abufPos++] = i2s_buff[i] & 0xFF; 149 | // } 150 | 151 | // // notify ADC task that the buffer is full 152 | // BaseType_t xHigherPriorityTaskWoken = pdFALSE; 153 | // vTaskNotifyGiveFromISR(udp_task_handle, &xHigherPriorityTaskWoken); 154 | // if (xHigherPriorityTaskWoken) { 155 | // portYIELD_FROM_ISR(); 156 | // } 157 | 158 | // send UDP payload 159 | int err = sendto(sock, (const uint8_t *)abuf_tx, ADC_SAMPLES_COUNT, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 160 | if (err < 0) 161 | { 162 | ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); 163 | } 164 | } 165 | 166 | shutdown(sock, 0); 167 | close(sock); 168 | } 169 | 170 | //----------------------------------------------------------------------------------------// 171 | // ADC setup 172 | //----------------------------------------------------------------------------------------// 173 | void init_adc( void ) 174 | { 175 | adc1_config_width(ADC_WIDTH_BIT_12); 176 | adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_0); 177 | } 178 | 179 | 180 | //----------------------------------------------------------------------------------------// 181 | // timer setup 182 | //----------------------------------------------------------------------------------------// 183 | static void timer_isr(void* arg) 184 | { 185 | portENTER_CRITICAL_ISR(&timerMux); 186 | TIMERG0.int_clr_timers.t0 = 1; 187 | TIMERG0.hw_timer[0].config.alarm_en = 1; 188 | 189 | // read ADC 190 | uint16_t adc_value = adc1_get_raw(ADC1_CHANNEL_0); 191 | 192 | // convert to bytes 193 | abuf[abufPos++] = (adc_value >> 8) & 0xFF; 194 | abuf[abufPos++] = adc_value & 0xFF; 195 | 196 | // copy buffer into a second buffer for transmission 197 | if (abufPos >= ADC_SAMPLES_COUNT) { 198 | abufPos = 0; 199 | memcpy(abuf_tx, abuf, ADC_SAMPLES_COUNT); 200 | 201 | // notify ADC task that the buffer is full 202 | BaseType_t xHigherPriorityTaskWoken = pdFALSE; 203 | vTaskNotifyGiveFromISR(udp_task_handle, &xHigherPriorityTaskWoken); 204 | if (xHigherPriorityTaskWoken) { 205 | portYIELD_FROM_ISR(); 206 | } 207 | } 208 | 209 | portEXIT_CRITICAL_ISR(&timerMux); 210 | } 211 | 212 | 213 | //----------------------------------------------------------------------------------------// 214 | // init timer 215 | //----------------------------------------------------------------------------------------// 216 | void init_timer(int timer_period_us) 217 | { 218 | timer_config_t config = { 219 | .alarm_en = true, 220 | .counter_en = false, 221 | .intr_type = TIMER_INTR_LEVEL, 222 | .counter_dir = TIMER_COUNT_UP, 223 | .auto_reload = true, 224 | .divider = 80 /* 1 us per tick */ 225 | }; 226 | 227 | timer_init(TIMER_GROUP_0, TIMER_0, &config); 228 | timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); 229 | timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, timer_period_us); 230 | timer_enable_intr(TIMER_GROUP_0, TIMER_0); 231 | timer_isr_register(TIMER_GROUP_0, TIMER_0, &timer_isr, NULL, 0, &s_timer_handle); 232 | timer_start(TIMER_GROUP_0, TIMER_0); 233 | } 234 | 235 | 236 | //----------------------------------------------------------------------------------------// 237 | // send UDP data 238 | //----------------------------------------------------------------------------------------// 239 | void udp_task(void *param) { 240 | int addr_family = 0; 241 | int ip_protocol = 0; 242 | char host_ip[] = HOST_IP_ADDR; 243 | 244 | struct sockaddr_in dest_addr; 245 | dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR); 246 | dest_addr.sin_family = AF_INET; 247 | dest_addr.sin_port = htons(HOST_PORT); 248 | addr_family = AF_INET; 249 | ip_protocol = IPPROTO_IP; 250 | int sock = -1; 251 | while (sock < 0) { 252 | sock = socket(addr_family, SOCK_DGRAM, ip_protocol); 253 | if (sock < 0) 254 | ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); 255 | } 256 | ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, HOST_PORT); 257 | while (true) { 258 | // sleep until the ISR gives us something to do 259 | uint32_t tcount = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 260 | 261 | // for (int i = 0; i < 10; i++) { 262 | // ESP_LOGI(TAG, "ADC value: %d", abuf_tx[i]); 263 | // } 264 | 265 | // send UDP payload 266 | ESP_LOGI(TAG, "UDPTask got called."); 267 | int err = sendto(sock, (const uint8_t *)abuf_tx, ADC_SAMPLES_COUNT, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 268 | if (err < 0) 269 | { 270 | ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); 271 | ESP_LOGI(TAG, "Closing socket. Trying again..."); 272 | close(sock); 273 | sock = -1; 274 | while (sock < 0) { 275 | sock = socket(addr_family, SOCK_DGRAM, ip_protocol); 276 | if (sock < 0) 277 | ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); 278 | } 279 | ESP_LOGI(TAG, "Connected again."); 280 | } 281 | else 282 | ESP_LOGI(TAG, "Packet sent."); 283 | } 284 | shutdown(sock, 0); 285 | close(sock); 286 | } 287 | 288 | 289 | //----------------------------------------------------------------------------------------// 290 | // UDP client socket 291 | //----------------------------------------------------------------------------------------// 292 | void init_socket(void){ 293 | struct sockaddr_in dest_addr; 294 | dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR); 295 | dest_addr.sin_family = AF_INET; 296 | dest_addr.sin_port = htons(HOST_PORT); 297 | int addr_family = AF_INET; 298 | int ip_protocol = IPPROTO_IP; 299 | 300 | // connect to UDF server 301 | while (1) { 302 | int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); 303 | if (sock < 0) { 304 | ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); 305 | } 306 | ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, HOST_PORT); 307 | break; 308 | } 309 | } 310 | 311 | 312 | //----------------------------------------------------------------------------------------// 313 | // event handler 314 | //----------------------------------------------------------------------------------------// 315 | esp_err_t event_handler(void *ctx, system_event_t *event) 316 | { 317 | switch(event->event_id) { 318 | case SYSTEM_EVENT_STA_START: 319 | esp_wifi_connect(); 320 | ESP_LOGI(TAG,"Disconnected! Trying to connect to WiFi network..."); 321 | break; 322 | case SYSTEM_EVENT_STA_GOT_IP: 323 | ESP_LOGI(TAG, "Received assigned IP Address:%s", 324 | ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); 325 | if (!wifi_init) { 326 | wifi_init = true; 327 | 328 | // init timer TBD <<< enable again 329 | //float calls = (float)1/(float)SAMPLE_RATE * 1000000; 330 | //ESP_LOGI(TAG, "Timer is set to: %f", calls); 331 | //init_timer(calls); 332 | } 333 | break; 334 | case SYSTEM_EVENT_STA_DISCONNECTED: 335 | esp_wifi_connect(); 336 | ESP_LOGI(TAG,"Disconnected! Trying to connect to WiFi network..."); 337 | break; 338 | default: 339 | break; 340 | } 341 | return ESP_OK; 342 | } 343 | 344 | 345 | //----------------------------------------------------------------------------------------// 346 | // init wifi connection 347 | //----------------------------------------------------------------------------------------// 348 | void init_wifi(void) 349 | { 350 | nvs_flash_init(); 351 | tcpip_adapter_init(); 352 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 353 | ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); 354 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 355 | ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); 356 | ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); 357 | ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); 358 | wifi_config_t sta_config = { 359 | .sta = { 360 | .ssid = CONFIG_ESP_WIFI_SSID, 361 | .password = CONFIG_ESP_WIFI_PASSWORD, 362 | .bssid_set = false 363 | } 364 | }; 365 | ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config) ); 366 | ESP_ERROR_CHECK( esp_wifi_start() ); 367 | ESP_ERROR_CHECK( esp_wifi_connect() ); 368 | 369 | // wait until IP is assigned 370 | ESP_LOGI(TAG,"Waiting for IP address..."); 371 | while (!wifi_init) { 372 | vTaskDelay(100 / portTICK_RATE_MS); 373 | } 374 | 375 | } 376 | 377 | 378 | //----------------------------------------------------------------------------------------// 379 | // main app logic 380 | //----------------------------------------------------------------------------------------// 381 | void app_main(void) 382 | { 383 | // init wifi 384 | init_wifi(); 385 | 386 | // init ADC 387 | init_adc(); 388 | 389 | // init I2S 390 | init_i2s(); 391 | 392 | // UDP caller task 393 | //xTaskCreate(udp_task, "UDP Client Task", 8192, NULL, 5, &udp_task_handle); 394 | 395 | // I2S sampler task 396 | xTaskCreate(sample_i2s, "I2S Task", 8192, NULL, 5, &i2s_task_handle); 397 | } 398 | 399 | --------------------------------------------------------------------------------