|
@@ -0,0 +1,296 @@
|
|
|
+/*
|
|
|
+ * SPDX-FileCopyrightText: 2019 Helmut Pozimski <helmut@pozimski.eu>
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: GPL-2.0-only
|
|
|
+ */
|
|
|
+#include <freertos/FreeRTOS.h>
|
|
|
+#include <freertos/event_groups.h>
|
|
|
+
|
|
|
+#include <esp_event.h>
|
|
|
+#include <esp_wifi.h>
|
|
|
+#include <esp_log.h>
|
|
|
+#include <esp_event_loop.h>
|
|
|
+
|
|
|
+#include <driver/gpio.h>
|
|
|
+#include <driver/i2s.h>
|
|
|
+#include <lwip/sockets.h>
|
|
|
+#include <soc/io_mux_reg.h>
|
|
|
+#include <nvs_flash.h>
|
|
|
+
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#include "wifi_credentials.h"
|
|
|
+#include "configuration.h"
|
|
|
+
|
|
|
+#define WIFI_LOG_PREFIX "Wifi"
|
|
|
+#define TCP_LOG_PREFIX "TCP"
|
|
|
+
|
|
|
+// Mapping of board pins to the functions they are used for
|
|
|
+#define MD1 16
|
|
|
+#define MD0 17
|
|
|
+#define MD 18
|
|
|
+#define MC 19
|
|
|
+#define MS 21
|
|
|
+
|
|
|
+#define SLAVE_DATA_IN 27
|
|
|
+#define SLAVE_BCK_IN 26
|
|
|
+#define SLAVE_WS_IN 25
|
|
|
+
|
|
|
+#define GPIO_OUTPUT_PIN_SEL ((1ULL<<MD0) |(1ULL<<MD1) | (1ULL<<MD) | (1ULL<<MC) | (1ULL<<MS))
|
|
|
+
|
|
|
+#define gpio_set(x,y) ESP_ERROR_CHECK(gpio_set_level(x,y))
|
|
|
+
|
|
|
+/* Bit sequence to send to the clock generator */
|
|
|
+
|
|
|
+#if SAMPLE_RATE == 8000
|
|
|
+static uint16_t pll_seq = 21518;
|
|
|
+#elif SAMPLE_RATE == 16000
|
|
|
+static uint16_t pll_seq = 17422;
|
|
|
+#elif SAMPLE_RATE == 32000
|
|
|
+static uint16_t pll_seq = 25614;
|
|
|
+#elif SAMPLE_RATE == 44100
|
|
|
+static uint16_t pll_seq = 41998;
|
|
|
+#elif SAMPLE_RATE == 48000
|
|
|
+static uint16_t pll_seq = 9230;
|
|
|
+#endif
|
|
|
+
|
|
|
+/* Event group for Wifi events */
|
|
|
+static EventGroupHandle_t wifi_event_group;
|
|
|
+
|
|
|
+/* Bit to signal that a connection to the AP has been establisched */
|
|
|
+const int WIFI_CONNECTED_BIT = BIT0 ;
|
|
|
+
|
|
|
+/* Structures to handle synchronisation with the server */
|
|
|
+typedef struct {
|
|
|
+ uint32_t sample_rate;
|
|
|
+ uint32_t bits_per_sample;
|
|
|
+} sync;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ char ack[4];
|
|
|
+ sync dat;
|
|
|
+} sync_ack;
|
|
|
+
|
|
|
+/*
|
|
|
+ * Function: wifi_event_handler
|
|
|
+ * ----------------------------
|
|
|
+ * Callback function that handles wifi events
|
|
|
+ *
|
|
|
+ * ctx: context from which the function was called
|
|
|
+ * event: event that occured and should be handled
|
|
|
+ *
|
|
|
+ * returns: an error code
|
|
|
+ */
|
|
|
+static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) {
|
|
|
+ ESP_LOGI(WIFI_LOG_PREFIX, "Event handler called");
|
|
|
+ if (event->event_id == SYSTEM_EVENT_STA_START) {
|
|
|
+ ESP_LOGI(WIFI_LOG_PREFIX, "Wifi connection started");
|
|
|
+ esp_wifi_connect();
|
|
|
+ } else if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) {
|
|
|
+ ESP_LOGI(WIFI_LOG_PREFIX, "Acquired IP address: %s",
|
|
|
+ ip4addr_ntoa (&event->event_info.got_ip.ip_info.ip));
|
|
|
+ xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
|
|
|
+ } else if (event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) {
|
|
|
+ ESP_LOGI(WIFI_LOG_PREFIX, "Connection lost");
|
|
|
+ esp_wifi_connect();
|
|
|
+ xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
|
|
|
+ }
|
|
|
+ return ESP_OK;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Function: configure_wifi
|
|
|
+ * ------------------------
|
|
|
+ * Configures the necessary parameters to establish a wifi connection
|
|
|
+ */
|
|
|
+static void configure_wifi(void) {
|
|
|
+ wifi_event_group = xEventGroupCreate();
|
|
|
+ tcpip_adapter_init();
|
|
|
+ ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL));
|
|
|
+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
|
+ ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
|
|
|
+ wifi_config_t wifi_config = {
|
|
|
+ .sta = {
|
|
|
+ .ssid = WIFI_AP,
|
|
|
+ .password = WIFI_WPA_KEY
|
|
|
+ },
|
|
|
+ };
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_set_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCOL_11B |
|
|
|
+ WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N));
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_set_bandwidth(ESP_IF_WIFI_STA, WIFI_BW_HT40));
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_start());
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Function: setup_gpio
|
|
|
+ * --------------------
|
|
|
+ * Configures the GPIO Pins that are used
|
|
|
+ *
|
|
|
+ * returns: error code of the configuration operation
|
|
|
+ */
|
|
|
+static esp_err_t setup_gpio(void) {
|
|
|
+ esp_err_t gpio_err;
|
|
|
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO16_U, FUNC_GPIO16_GPIO16);
|
|
|
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U, FUNC_GPIO17_GPIO17);
|
|
|
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO18_U, FUNC_GPIO18_GPIO18);
|
|
|
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO19_U, FUNC_GPIO19_GPIO19);
|
|
|
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO21_U, FUNC_GPIO21_GPIO21);
|
|
|
+ gpio_config_t io_conf = {
|
|
|
+ .intr_type = GPIO_INTR_DISABLE,
|
|
|
+ .mode = GPIO_MODE_OUTPUT,
|
|
|
+ .pin_bit_mask = GPIO_OUTPUT_PIN_SEL,
|
|
|
+ .pull_down_en = 0,
|
|
|
+ .pull_up_en = 0,
|
|
|
+ };
|
|
|
+ gpio_err = gpio_config(&io_conf);
|
|
|
+ return gpio_err;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Function pcm1808_config
|
|
|
+ * --------------------------
|
|
|
+ * Sets the MD0 and MD1 inputs for the pcm1808
|
|
|
+ *
|
|
|
+ * md1: value for input md1
|
|
|
+ * md0: value for input md0
|
|
|
+ */
|
|
|
+static void pcm1808_config(uint32_t md1, uint32_t md0) {
|
|
|
+ gpio_set(MD1, md1);
|
|
|
+ gpio_set(MD0, md0);
|
|
|
+}
|
|
|
+
|
|
|
+/* Function send_pll_bits
|
|
|
+ * ----------------------
|
|
|
+ * Sends a bit sequence to configure the PLL1708
|
|
|
+ *
|
|
|
+ * bits: 16 bit unsigned integer representing the bits to be send from right to left
|
|
|
+ */
|
|
|
+static void send_pll_bits(uint16_t bits) {
|
|
|
+ gpio_set(MS,0);
|
|
|
+ gpio_set(MC,0);
|
|
|
+ for (uint8_t i=16; i>0; i--) {
|
|
|
+ vTaskDelay(100 / portTICK_PERIOD_MS);
|
|
|
+ gpio_set(MD, bits & 1);
|
|
|
+ bits = bits >>1;
|
|
|
+ gpio_set(MC, 1);
|
|
|
+ vTaskDelay(100 / portTICK_PERIOD_MS);
|
|
|
+ gpio_set(MC, 0);
|
|
|
+ }
|
|
|
+ gpio_set(MS,1);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Function: setup_i2s
|
|
|
+ * -------------------
|
|
|
+ * Initializes the i2s driver with the proper configuration
|
|
|
+ */
|
|
|
+static void setup_i2s(void) {
|
|
|
+ i2s_config_t i2s_config = {
|
|
|
+ .mode = I2S_MODE_SLAVE | I2S_MODE_RX,
|
|
|
+ .sample_rate = SAMPLE_RATE,
|
|
|
+ .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
|
|
|
+ .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
|
|
+ .communication_format = I2S_COMM_FORMAT_I2S,
|
|
|
+ .dma_buf_count = 35,
|
|
|
+ .dma_buf_len = 256
|
|
|
+ };
|
|
|
+ i2s_pin_config_t pin_config = {
|
|
|
+ .bck_io_num = SLAVE_BCK_IN,
|
|
|
+ .ws_io_num = SLAVE_WS_IN,
|
|
|
+ .data_in_num = SLAVE_DATA_IN,
|
|
|
+ .data_out_num = I2S_PIN_NO_CHANGE
|
|
|
+ };
|
|
|
+ ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL));
|
|
|
+ ESP_ERROR_CHECK(i2s_set_pin(I2S_NUM_0, &pin_config));
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Function: transmit_task
|
|
|
+ * Function that reads values from i2s and transmits them via TCP, it represents the main task
|
|
|
+ *
|
|
|
+ * pvParameters: parameters passed to the task
|
|
|
+ */
|
|
|
+static void transmit_task(void *pvParameters) {
|
|
|
+ send_pll_bits(pll_seq);
|
|
|
+ struct sockaddr_in dest_addr;
|
|
|
+ dest_addr.sin_addr.s_addr = inet_addr(TARGET_HOST);
|
|
|
+ dest_addr.sin_family = AF_INET;
|
|
|
+ dest_addr.sin_port = htons(TARGET_PORT);
|
|
|
+ int sock = 0;
|
|
|
+ void *transmit_buffer = malloc(1458);
|
|
|
+ if (transmit_buffer == NULL) {
|
|
|
+ ESP_LOGE("MALLOC", "Could not allocate memory for data buffer");
|
|
|
+ }
|
|
|
+ uint16_t offset = 0;
|
|
|
+ unsigned int bytes_read;
|
|
|
+ int err;
|
|
|
+ sync sync_data = {
|
|
|
+ .sample_rate = SAMPLE_RATE,
|
|
|
+ .bits_per_sample = 24
|
|
|
+ };
|
|
|
+ sync_ack ack;
|
|
|
+ while (1) {
|
|
|
+ if (sock != 0) {
|
|
|
+ close(sock);
|
|
|
+ }
|
|
|
+ sock = socket(dest_addr.sin_family, SOCK_STREAM, IPPROTO_IP);
|
|
|
+ if (sock < 0) {
|
|
|
+ ESP_LOGE(TCP_LOG_PREFIX, "Could not create socket");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, false, true, portMAX_DELAY);
|
|
|
+ ESP_LOGI(TCP_LOG_PREFIX, "Establishing connection");
|
|
|
+ err = connect(sock, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
|
|
|
+ if (err != 0) {
|
|
|
+ ESP_LOGE(TCP_LOG_PREFIX, "Failed to establish a TCP connection");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ send(sock, &sync_data, sizeof(sync_data), 0);
|
|
|
+ recv(sock, &ack, sizeof(ack), 0);
|
|
|
+ if ((strcmp(ack.ack, "ACK") !=0) || (ack.dat.sample_rate != SAMPLE_RATE) || (ack.dat.bits_per_sample != 24)) {
|
|
|
+ ESP_LOGE(TCP_LOG_PREFIX, "Synchronisation with server not successful");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ ESP_LOGI(TCP_LOG_PREFIX, "Connection successfully established");
|
|
|
+ uint8_t discard;
|
|
|
+ while (1) {
|
|
|
+ if (offset < 1458) {
|
|
|
+ i2s_read(I2S_NUM_0, transmit_buffer + offset, 3, &bytes_read, portMAX_DELAY);
|
|
|
+ offset += 3;
|
|
|
+ i2s_read(I2S_NUM_0, &discard, 1, &bytes_read, portMAX_DELAY);
|
|
|
+ }
|
|
|
+ if (offset == 1458) {
|
|
|
+ err = send(sock, transmit_buffer, 1458, 0);
|
|
|
+ if (err < 0) {
|
|
|
+ ESP_LOGE(TCP_LOG_PREFIX, "TCP connection lost");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ offset = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vTaskDelete(NULL);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Function: app_main
|
|
|
+ * ------------------
|
|
|
+ * Entry function that sets up the main task and initializes everything
|
|
|
+ */
|
|
|
+void app_main(void) {
|
|
|
+ ESP_ERROR_CHECK(nvs_flash_init());
|
|
|
+ configure_wifi();
|
|
|
+ xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, false, true, portMAX_DELAY);
|
|
|
+ // Initialize GPIO pins
|
|
|
+ ESP_ERROR_CHECK(setup_gpio());
|
|
|
+ // Set PCM1808 configuration
|
|
|
+ pcm1808_config(0, 1);
|
|
|
+ // Power down PLL1708
|
|
|
+ send_pll_bits(40974);
|
|
|
+ // Initialize the i2s driver
|
|
|
+ setup_i2s();
|
|
|
+ xTaskCreate(transmit_task, "transmit_task", 4096, NULL, 7, NULL);
|
|
|
+}
|