├── .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 |
--------------------------------------------------------------------------------