├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── components ├── WM8960 │ ├── CMakeLists.txt │ ├── wm8960.c │ └── wm8960.h └── a2dp_sink │ ├── CMakeLists.txt │ ├── bt_app_av.c │ ├── bt_app_av.h │ ├── bt_app_core.c │ └── bt_app_core.h ├── frontend └── index.html ├── main ├── CMakeLists.txt ├── Kconfig ├── Kconfig.projbuild ├── component.mk ├── main.c ├── tuning_http_server.c ├── tuning_http_server.h ├── wifi_handler.c └── wifi_handler.h ├── partition_table.csv └── sdkconfig.defaults /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | sdkconfig 3 | sdkconfig.old 4 | .DS_Store 5 | Readme.md 6 | .vscode/ -------------------------------------------------------------------------------- /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 | set(EXTRA_COMPONENT_DIRS /components) 6 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 7 | project(a2dp_sink) 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jamm02 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := a2dp_sink 7 | 8 | include $(IDF_PATH)/make/project.mk 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Esp32-audio-router 2 | 3 | Stream audio using bluetooth. 4 | 5 | ## Table of Contents 6 | 7 | - [esp32-audio-router](#esp32-audio-router) 8 | - [Table of Contents](#table-of-contents) 9 | - [Demonstrations](#demonstrations) 10 | - [About the project](#about-the-project) 11 | - [Tech Stack](#tech-stack) 12 | - [File Structure](#file-structure) 13 | - [Data Processing](#data-processing) 14 | - [Program Flow:](#program-flow) 15 | - [A2DP-Sink:](#a2dp-sink) 16 | - [Example Output](#example-output) 17 | - [WM8960:](#wm8960) 18 | - [Getting Started](#getting-started) 19 | - [Prerequisites](#prerequisites) 20 | - [Installation](#installation) 21 | - [Usage](#usage) 22 | - [Configuration](#configuration) 23 | - [Contributors](#contributors) 24 | - [Acknowledgements and Resources](#acknowledgements-and-resources) 25 | 26 | 27 | 28 | ## Demonstrations 29 | 30 | 31 | Screenshot 2021-10-24 at 2 19 12 PM 32 | 33 | https://user-images.githubusercontent.com/84293091/138590340-0dacf2f0-549a-4249-93e1-128a6e0cad74.mp4 34 | 35 | 36 | 37 | 38 | 39 | ## About the project 40 | ### Tech Stack 41 | The Technologies used for this project are 42 | * [FreeRTOS](https://www.freertos.org/openrtos.html) 43 | * [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/) 44 | 45 | ### File Structure 46 | . 47 | ├── Components # Contains files of specific library of functions or Hardware used 48 | │ ├──WM8960 # Library for WM8960 49 | | |──CMakeLists.txt 50 | | |──wm8960.c 51 | | |──a2dp_sink # a2dp sink source codes 52 | | |──bt_app_core.c 53 | | |──bt_app_av.c 54 | ├── frontend # Frontend file 55 | │ ├── index.html # Code for frontend 56 | ├── main # Source files (alternatively `lib` or `app`) 57 | │ ├──main.c # Main Source code to be executed 58 | | ├──tuning_http_server.c # Source code for http server 59 | | ├──wifi_handler.c # Source code for handling wifi operations 60 | │ ├──kconfig.projbuild # defines the entries of the menu for configuration 61 | │ ├──CMakeLists.txt # contains commands to include the bluetooth library and main.c in esp-idf 62 | ├── CmakeLists.txt # contains commands to include Components and main folder while executing 63 | ├── LICENSE 64 | └── README.md 65 | 66 | ### Data Processing: 67 | * Data of button press is relayed from the frontend to the server(ESP32) using JSON. 68 | ``` 69 | JSON format: 70 | { 71 | data:parameter 72 | } 73 | 74 | ``` 75 | * The data fetched is then decoded using cJSON and hence ESP32 gets to kmow about the input provided by the user. 76 | 77 | 78 | ### Program Flow: 79 | 80 | ![Flowchart (1)](https://user-images.githubusercontent.com/84293091/138586892-d9802a5b-e4aa-4fc6-a1b8-0f8f436a8f29.png) 81 | 82 | 83 | ## A2DP-Sink: 84 | 85 | ## Example Output 86 | 87 | After the program is started, the example starts inquiry scan and page scan, awaiting being discovered and connected. Other bluetooth devices such as smart phones can discover a device named as providef by webpage from the user. A smartphone or another ESP-IDF example of A2DP source can be used to connect to the local device. 88 | 89 | Once A2DP connection is set up, there will be a notification message with the remote device's bluetooth MAC address like the following: 90 | 91 | 92 | I (106427) BT_AV: A2DP connection state: Connected, [64:a2:f9:69:57:a4] 93 | 94 | 95 | If a smartphone is used to connect to local device, starting to play music with an APP will result in the transmission of audio stream. The transmitting of audio stream will be visible in the application log including a count of audio data packets, like this: 96 | 97 | 98 | I (120627) BT_AV: A2DP audio state: Started 99 | I (122697) BT_AV: Audio packet count 100 100 | I (124697) BT_AV: Audio packet count 200 101 | I (126697) BT_AV: Audio packet count 300 102 | I (128697) BT_AV: Audio packet count 400 103 | 104 | 105 | ## WM8960: 106 | 107 | 108 | The WM8960 is a low power, high quality stereo CODEC 109 | designed for portable digital audio applications. 110 | 111 | Stereo class D speaker drivers provide 1W per channel into 8 ohm 112 | loads with a 5V supply. Low leakage, excellent PSRR and 113 | pop/click suppression mechanisms also allow direct battery 114 | connection to the speaker supply. Flexible speaker boost 115 | settings allow speaker output power to be maximised while 116 | minimising other analogue supply currents. 117 | 118 | ![WM8960-Audio-Board__74552 1561069495](https://user-images.githubusercontent.com/84293091/138586930-4cbeead6-43b0-4f46-b3f5-f95132e6c200.jpg) 119 | 120 | 121 | A highly flexible input configuration for up to three stereo 122 | sources is integrated, with a complete microphone interface. 123 | External component requirements are drastically reduced as no 124 | separate microphone, speaker or headphone amplifiers are 125 | required. Advanced on-chip digital signal processing performs 126 | automatic level control for the microphone or line input. 127 | 128 | Stereo 24-bit sigma-delta ADCs and DACs are used with low 129 | power over-sampling digital interpolation and decimation filters 130 | and a flexible digital audio interface. 131 | The master clock can be input directly or generated internally by 132 | an onboard PLL, supporting most commonly-used clocking 133 | schemes. 134 | 135 | The WM8960 operates at analogue supply voltages down to 136 | 2.7V, although the digital supplies can operate at voltages down 137 | to 1.71V to save power. The speaker supply can operate at up 138 | to 5.5V, providing 1W per channel into 8 ohm loads. Unused 139 | functions can be disabled using software control to save power. 140 | The WM8960 is supplied in a very small and thin 5x5mm QFN 141 | package, ideal for use in hand-held and portable systems. 142 | 143 | 144 | 145 | ## Getting Started 146 | 147 | ### Prerequisites 148 | Install ESP-IDF : https://github.com/espressif/esp-idf 149 | 150 | ### Installation 151 | Clone the project 152 | ``` 153 | https://github.com/Jamm02/esp32-audio-router.git 154 | 155 | cd esp32-audio-router 156 | ``` 157 | ## Usage 158 | 159 | Build 160 | ``` 161 | idf.py build 162 | ``` 163 | Flash 164 | ``` 165 | idf.py -p (PORT) flash monitor 166 | 167 | ``` 168 | ### Configuration 169 | 170 | ``` 171 | idf.py menuconfig 172 | ``` 173 | * `Example Connection Configuration` 174 | * `WiFi SSID` - Set wifi SSID 175 | * `WiFi PASSWORD` - Set wifi Password 176 | 177 | 178 | ## Contributors 179 | * [Moteen Shah](https://github.com/Jamm02) 180 | * [Chinmay Lonkar](https://github.com/ChinmayLonkar) 181 | 182 | ## Acknowledgements and Resources 183 | * [SRA VJTI](https://github.com/SRA-VJTI) 184 | * Special thanks to [Gautam Agarwal](https://github.com/gautam-dev-maker), [Shreyas Atre](https://github.com/SAtacker), [Dhairya Shah](https://github.com/dhairyashah1), [Vedant Paranjape](https://github.com/VedantParanjape) 185 | * https://www.waveshare.com/w/upload/1/18/WM8960_v4.2.pdf 186 | * https://www.waveshare.com/wiki/File:WM8960_Audio_Board_Code.7z 187 | * https://github.com/espressif/esp-skainet/blob/master/components/hardware_driver/MediaHal/Codec/wm8960/wm8960.c 188 | * https://github.com/DaveGamble/cJSON 189 | * https://github.com/espressif/esp-idf/tree/release/v4.2/examples/protocols/http_server 190 | 191 | 192 | ## License 193 | The [License](https://github.com/Jamm02/esp32-audio-router/blob/master/LICENSE) Used for this Project. 194 | 195 | 196 | -------------------------------------------------------------------------------- /components/WM8960/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "wm8960.c" 2 | INCLUDE_DIRS ".") 3 | 4 | -------------------------------------------------------------------------------- /components/WM8960/wm8960.c: -------------------------------------------------------------------------------- 1 | #include "wm8960.h" 2 | 3 | 4 | const uint16_t wm8960_reg_defaults[] = { 5 | 0x0, 0x0117, 6 | 0x1, 0x0117, 7 | 0x2, 0x01fd, 8 | 0x3, 0x01fd, 9 | 0x4, 0x0000, 10 | 0x5, 0x0000, 11 | 0x6, 0x0000, 12 | 0x7, 0x0002, 13 | 0x8, 0x01c0, 14 | 0x9, 0x0000, 15 | 0xa, 0x01ff, 16 | 0xb, 0x01ff, 17 | 18 | 0x10, 0x0000, 19 | 0x11, 0x007b, 20 | 0x12, 0x0100, 21 | 0x13, 0x0032, 22 | 0x14, 0x0000, 23 | 0x15, 0x00c3, 24 | 0x16, 0x00c3, 25 | 0x17, 0x01c0, 26 | 0x18, 0x0000, 27 | 0x19, 0x0000, 28 | 0x1a, 0x01e0, 29 | 0x1b, 0x0000, 30 | 0x1c, 0x0000, 31 | 0x1d, 0x0000, 32 | 33 | 0x20, 0x0100, 34 | 0x21, 0x0100, 35 | 0x22, 0x0150, 36 | 37 | 0x25, 0x0150, 38 | 0x26, 0x0000, 39 | 0x27, 0x0000, 40 | 0x28, 0x0000, 41 | 0x29, 0x0000, 42 | 0x2a, 0x0040, 43 | 0x2b, 0x0000, 44 | 0x2c, 0x0000, 45 | 0x2d, 0x0050, 46 | 0x2e, 0x0050, 47 | 0x2f, 0x0000, 48 | 0x30, 0x0002, 49 | 0x31, 0x0037, 50 | 51 | 0x33, 0x0080, 52 | 0x34, 0x0008, 53 | 0x35, 0x0031, 54 | 0x36, 0x0026, 55 | 0x37, 0x00e9, 56 | 57 | }; 58 | 59 | static void wm8960_i2c_init() 60 | { 61 | esp_err_t rc; 62 | i2c_config_t i2c_conf; 63 | memset(&i2c_conf, 0x00, sizeof(i2c_conf)); 64 | i2c_conf.mode = I2C_MODE_MASTER; 65 | i2c_conf.sda_io_num = SDA_PIN; 66 | i2c_conf.scl_io_num = SCL_PIN; 67 | i2c_conf.sda_pullup_en = GPIO_PULLUP_ENABLE; 68 | i2c_conf.scl_pullup_en = GPIO_PULLUP_ENABLE; 69 | i2c_conf.master.clk_speed = 100 * 1000; 70 | ESP_LOGI(TAG, "Configuring I2C"); 71 | rc = i2c_param_config(I2C_BUS_NO, &i2c_conf); 72 | // ESP_LOGD(TAG, "I2C Param Config: %s", esp_err_to_name(rc)); 73 | rc = i2c_driver_install(I2C_BUS_NO, I2C_MODE_MASTER, 0, 0, 0); 74 | // ESP_LOGD(TAG, "I2C Driver Install; %s", esp_err_to_name(rc)); 75 | } 76 | 77 | static esp_err_t i2c_trans(i2c_port_t i2c_num, uint8_t addr, void *txdata, uint8_t txlen) 78 | { 79 | esp_err_t rc; 80 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 81 | // ESP_LOGV(TAG, "CMD Handle: %s", esp_err_to_name(rc)); 82 | rc = i2c_master_start(cmd); 83 | // ESP_LOGV(TAG, "[W] Master_start: %s", esp_err_to_name(rc)); 84 | rc = i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN); 85 | // ESP_LOGV(TAG, "[W] Master_write_byte: %s", esp_err_to_name(rc)); 86 | rc = i2c_master_write(cmd, txdata, txlen, ACK_CHECK_EN); 87 | // ESP_LOGV(TAG, "[W] Master_write: %s", esp_err_to_name(rc)); 88 | rc = i2c_master_stop(cmd); 89 | // ESP_LOGV(TAG, "[W] Master stop: %s", esp_err_to_name(rc)); 90 | rc = i2c_master_cmd_begin(I2C_BUS_NO, cmd, 10); 91 | // ESP_LOGI(TAG, "[W] CMD begin: %s", esp_err_to_name(rc)); 92 | i2c_cmd_link_delete(cmd); 93 | return rc; 94 | } 95 | 96 | static esp_err_t i2c_recv(i2c_port_t i2c_num, uint8_t addr, void *rxdata, uint8_t rxlen) 97 | { 98 | esp_err_t rc; 99 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 100 | i2c_master_start(cmd); 101 | i2c_master_write_byte(cmd, (CODEC_ADDR << 1) | I2C_MASTER_READ, ACK_CHECK_DIS); 102 | i2c_master_start(cmd); 103 | i2c_master_write_byte(cmd, (addr << 1), ACK_CHECK_EN); 104 | if (rxlen > 1) { 105 | i2c_master_read(cmd, rxdata, rxlen - 1, ACK_VAL); 106 | } 107 | i2c_master_read_byte(cmd, rxdata + rxlen - 1, NACK_VAL); 108 | i2c_master_stop(cmd); 109 | rc = i2c_master_cmd_begin(i2c_num, cmd, 10); 110 | printf("Recv: %d\n", rc); 111 | i2c_cmd_link_delete(cmd); 112 | return rc; 113 | } 114 | 115 | static esp_err_t write_register_i2c(uint8_t slave_id, uint32_t reg_addr, uint32_t reg_val) 116 | { 117 | esp_err_t rc; 118 | uint8_t buff[3]; 119 | buff[0] = (reg_addr << 1) | ((reg_val >> 8) & 0x0f); 120 | buff[1] = reg_val & 0xff; 121 | //printf("%x %x \t %x %x\n", reg_addr, reg_val, buff[0], buff[1]); 122 | rc = i2c_trans(I2C_BUS_NO, slave_id, buff, 2); 123 | return rc; 124 | } 125 | 126 | static esp_err_t read_register_i2c(uint8_t slave_id, uint8_t reg_addr) 127 | { 128 | esp_err_t rc; 129 | uint8_t data = 0; 130 | rc = i2c_recv(I2C_BUS_NO, reg_addr, &data, 1); 131 | printf("Read Value: %x\n", data); 132 | return rc; 133 | 134 | esp_err_t res; 135 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 136 | 137 | res = i2c_master_start(cmd); 138 | res |= i2c_master_write_byte(cmd, (slave_id << 1) | I2C_MASTER_WRITE, 1 /*ACK_CHECK_EN*/); 139 | res |= i2c_master_write_byte(cmd, (reg_addr << 1), 1 /*ACK_CHECK_EN*/); 140 | res |= i2c_master_stop(cmd); 141 | res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS); 142 | printf("%d\n", res); 143 | i2c_cmd_link_delete(cmd); 144 | 145 | } 146 | 147 | esp_err_t wm8960_init() 148 | { 149 | esp_err_t ret = 0; 150 | wm8960_i2c_init(); 151 | ret = write_register_i2c(CODEC_ADDR,0x0f, 0x0000); 152 | if(ret != 0) 153 | return ret; 154 | else 155 | ESP_LOGI(TAG, "WM8960 is reset"); 156 | 157 | ret = write_register_i2c(CODEC_ADDR,0x19, 1<<8 | 1<<7 | 1<<6); 158 | ret += write_register_i2c(CODEC_ADDR,0x1A, 1<<8 | 1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3); 159 | ret += write_register_i2c(CODEC_ADDR,0x2F, 1<<3 | 1<<2); 160 | if(ret != 0) { 161 | printf("Source set fail !!\r\n"); 162 | printf("Error code: %d\r\n",ret); 163 | return ret; 164 | } 165 | 166 | //Configure clock 167 | //MCLK->div1->SYSCLK->DAC/ADC sample Freq = 25MHz(MCLK)/2*256 = 48.8kHz 168 | write_register_i2c(CODEC_ADDR,0x04, 0x0000); 169 | 170 | //Configure ADC/DAC 171 | write_register_i2c(CODEC_ADDR,0x05, 0x0000); 172 | 173 | //Configure audio interface 174 | //I2S format 16 bits word length 175 | write_register_i2c(CODEC_ADDR,0x07, 0x0002); 176 | 177 | //Configure HP_L and HP_R OUTPUTS 178 | write_register_i2c(CODEC_ADDR,0x02, 0x0061 | 0x0100); //LOUT1 Volume Set 179 | write_register_i2c(CODEC_ADDR,0x03, 0x0061 | 0x0100); //ROUT1 Volume Set 180 | 181 | //Configure SPK_RP and SPK_RN 182 | write_register_i2c(CODEC_ADDR,0x28, 0x0077 | 0x0100); //Left Speaker Volume 183 | write_register_i2c(CODEC_ADDR,0x29, 0x0077 | 0x0100); //Right Speaker Volume 184 | 185 | //Enable the OUTPUTS 186 | write_register_i2c(CODEC_ADDR,0x31, 0x00F7); //Enable Class D Speaker Outputs 187 | 188 | //Configure DAC volume 189 | write_register_i2c(CODEC_ADDR,0x0a, 0x00FF | 0x0100); 190 | write_register_i2c(CODEC_ADDR,0x0b, 0x00FF | 0x0100); 191 | 192 | //3D 193 | // write_register_i2c(0x10, 0x000F); 194 | 195 | //Configure MIXER 196 | write_register_i2c(CODEC_ADDR,0x22, 1<<8 | 1<<7); 197 | write_register_i2c(CODEC_ADDR,0x25, 1<<8 | 1<<7); 198 | 199 | //Jack Detect 200 | write_register_i2c(CODEC_ADDR,0x18, 1<<6 | 0<<5); 201 | write_register_i2c(CODEC_ADDR,0x17, 0x01C3); 202 | write_register_i2c(CODEC_ADDR,0x30, 0x0009);//0x000D,0x0005 203 | 204 | return 0; 205 | } 206 | 207 | 208 | 209 | /* for (int i = 0; i < sizeof(wm8960_reg_defaults)/sizeof(uint16_t); i += 2 ) { 210 | printf("[%d] %x %x\n", i, wm8960_reg_defaults[i], wm8960_reg_defaults[i+1]); 211 | ret = write_register_i2c(CODEC_ADDR, wm8960_reg_defaults[i], wm8960_reg_defaults[i+1]); 212 | if (ret != 0) { 213 | ESP_LOGE(TAG, "Error: %d", ret); 214 | return ret; 215 | } 216 | } 217 | if (ret == ESP_OK) { 218 | ESP_LOGI(TAG, "WM8960 is up"); 219 | } else { 220 | ESP_LOGE(TAG, "WM8960 setup error"); 221 | } 222 | 223 | */ 224 | // return ret; 225 | 226 | esp_err_t wm8960_set_vol(int vol) 227 | { 228 | esp_err_t ret = 0; 229 | int vol_to_set = 0; 230 | if (vol == 0) { 231 | vol_to_set = 0; 232 | } else { 233 | volume = vol; 234 | vol_to_set = (vol / 10) * 5 + 200; 235 | } 236 | ret |= write_register_i2c(CODEC_ADDR, 0xc, 0x100 | vol_to_set); 237 | ret |= write_register_i2c(CODEC_ADDR, 0xb, 0x100 | vol_to_set); 238 | 239 | return ret; 240 | } 241 | 242 | esp_err_t wm8960_set_mute(bool mute) 243 | { 244 | esp_err_t ret = 0; 245 | if (mute) { 246 | ret |= wm8960_set_vol(0); 247 | } else { 248 | ret |= wm8960_set_vol(volume); 249 | } 250 | return ret; 251 | } 252 | 253 | esp_err_t wm8960_get_volume(uint8_t* vol) 254 | { 255 | *vol = volume; 256 | return ESP_OK; 257 | } 258 | 259 | 260 | -------------------------------------------------------------------------------- /components/WM8960/wm8960.h: -------------------------------------------------------------------------------- 1 | #ifndef _WM8960_H_ 2 | #define _WM8960_H_ 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include "freertos/FreeRTOS.h" 9 | #include "freertos/task.h" 10 | #include "driver/spi_master.h" 11 | #include "driver/gpio.h" 12 | #include "esp_err.h" 13 | #include "esp_log.h" 14 | #include "driver/i2c.h" 15 | 16 | static const char *TAG = "WM8960"; 17 | 18 | static uint16_t volume; 19 | #define CODEC_ADDR 0x1a 20 | #define SDA_PIN 21 21 | #define SCL_PIN 22 22 | #define I2C_BUS_NO 0 23 | #define ACK_CHECK_EN 0 24 | #define ACK_CHECK_DIS 0 25 | #define ACK_VAL 0 26 | #define NACK_VAL 1 27 | 28 | 29 | #define CHECK(x) do { esp_err_t _; if ((_ = x) != ESP_OK) return __; } while (0) 30 | #define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) 31 | 32 | #define R0_LEFT_INPUT_VOLUME_ADR 0x00 33 | #define R1_RIGHT_INPUT_VOLUME_ADR 0x01 34 | #define R2_LOUT1_VOLUME_ADR 0x02 35 | #define R3_ROUT1_VOLUME_ADR 0x03 36 | #define R4_CLOCKING_1_ADR 0x04 37 | #define R5_ADC_DAC_CONTROL_CTR1_ADR 0x05 38 | #define R6_ADC_DAC_CONTROL_CTR2_ADR 0x06 39 | #define R7_AUDIO_INTERFACE_1_ADR 0x07 40 | #define R8_CLOCKING_2_ADR 0x08 41 | #define R10_LEFT_DAC_VOLUME_ADR 0x09 42 | #define R11_RIGHT_DAC_VOLUME_ADR 0x0A 43 | #define R15_RESET_ADR 0x0F 44 | #define R16_3D_CONTROL_ADR 0x10 45 | #define R17_ALC1_ADR 0x11 46 | #define R18_ALC2_ADR 0x12 47 | #define R19_ALC3_ADR 0x13 48 | #define R20_NOISE_GATE_ADR 0x14 49 | #define R21_LEFT_ADC_VOLUME_ADR 0x15 50 | #define R22_RIGHT_ADC_VOLUME_ADR 0x16 51 | #define R23_ADDITIONAL_CONTROL_1_ADR 0x17 52 | #define R24_ADDITIONAL_CONTROL_2_ADR 0x18 53 | #define R25_PWR_MGMT_1_ADR 0x19 54 | #define R26_PWR_MGMT_2_ADR 0x1A 55 | #define R27_ADDITIONAL_CONTROL_3_ADR 0x1B 56 | #define R28_ANTI_POP_1_ADR 0x1C 57 | #define R29_ANTI_POP_2_ADR 0x1D 58 | #define R32_ADCL_SIGNAL_PATH 0x20 59 | #define R33_ADCR_SIGNAL_PATH 0x21 60 | #define R34_LEFT_OUT_MIX_2 0x22 61 | #define R37_RIGHT_OUT_MIX_2 0x23 62 | #define R38_MONO_OUT_MIX_1 0x26 63 | #define R39_MONO_OUT_MIX_2 0x27 64 | #define R40_LOUT2_VOLUME 0x28 65 | #define R41_ROUT2_VOLUME 0x29 66 | #define R42_MONOOUT_VOLUME 0x2A 67 | #define R43_INPUT_BOOST_MIXER_1 0x2B 68 | #define R44_INPUT_BOOST_MIXER_2 0x2C 69 | #define R45_BYPASS_1 0x2D 70 | #define R46_BYPASS_2 0x2E 71 | #define R47_PWR_MGMT_3 0x2F 72 | #define R48_ADDITONAL_CTRL_4 0x30 73 | #define R49_CLASS_D_CTRL_1 0x31 74 | #define R51_CLASS_D_CTRL_3 0x33 75 | 76 | 77 | 78 | 79 | 80 | 81 | typedef struct R0_LEFT_INPUT_VOLUME_t 82 | { 83 | uint16_t LINVOL :6; //Bits 5:0 84 | uint16_t LIZC :1; //Bits 6 85 | uint16_t LINMUTE :1; //Bits 7 86 | uint16_t IPUV :1; //Bits 8 87 | 88 | } __attribute__((packed, aligned(2))) R0_LEFT_INPUT_VOLUME_t; 89 | 90 | 91 | typedef struct R1_RIGHT_INPUT_VOLUME_t 92 | { 93 | uint16_t RINVOL :6; //Bits 5:0 94 | uint16_t RIZC :1; //Bits 6 95 | uint16_t RINMUTE :1; //Bits 7 96 | uint16_t IPUV :1; //Bits 8 97 | 98 | } __attribute__((packed, aligned(2))) R1_RIGHT_INPUT_VOLUME_t; 99 | 100 | 101 | typedef struct R2_LOUT1_VOLUME_t 102 | { 103 | uint16_t LOUT1VOL :7; //Bits 6:0 104 | uint16_t LO1ZC :1; //Bits 7 105 | uint16_t OUT1VU :1; //Bits 8 106 | } __attribute__((packed, aligned(2))) R2_LOUT1_VOLUME_t; 107 | 108 | typedef struct R3_ROUT1_VOLUME_t 109 | { 110 | uint16_t ROUT1VOL :7; //Bits 6:0 111 | uint16_t RO1ZC :1; //Bits 7 112 | uint16_t OUT1VU :1; //Bits 8 113 | } __attribute__((packed, aligned(2))) R3_ROUT1_VOLUME_t; 114 | 115 | typedef struct R4_CLOCKING_1_t{ 116 | 117 | uint16_t ADCDIV :3; //Bits 8:6 118 | uint16_t DACDIV :3; //Bits 5:3 119 | uint16_t SYSCLKDIV :2; //Bits 2:1 120 | uint16_t CLKSEL :1; //Bits 0 121 | } __attribute__((packed, aligned(2))) R4_CLOCKING_1_t; 122 | 123 | typedef struct R5_ADC_DAC_CONTROL_CTR1_t{ 124 | 125 | uint16_t ADCHPD :1; //Bits 0 126 | uint16_t DEEMPH :2; //Bits 2:1 127 | uint16_t DACMU :1; //Bits 3 128 | uint16_t R5RES_4 :1; 129 | uint16_t ADCPOL :2; //Bits 6:5 130 | uint16_t DACDIV2 :1; //Bits 7 131 | uint16_t R5RES_8 :1; 132 | }__attribute__((packed, aligned(2))) R5_ADC_DAC_CONTROL_CTR1_t ; 133 | 134 | typedef struct R6_ADC_DAC_CONTROL_CTR2_t{ 135 | 136 | uint16_t DACSLOPE :1; //Bits 1 137 | uint16_t DACMR :1; //Bits 2 138 | uint16_t DACSMM :1; //Bits 3 139 | uint16_t DACPOL :2; //Bits 6:5 140 | }__attribute__((packed, aligned(2))) R6_ADC_DAC_CONTROL_CTR2_t; 141 | 142 | typedef struct R7_AUDIO_INTERFACE_t{ 143 | uint16_t FORMAT :2; // bit 1:0 144 | uint16_t WL :2; // bit 1:0 145 | uint16_t LRP :1; // bit 4 146 | uint16_t DLRSWAP :1; // bit 5 147 | uint16_t MS :1; // bit 6 148 | uint16_t BCLKINV :1; // bit 7 149 | uint16_t ALRSWAP :1; // bit 8 150 | }__attribute__((packed, aligned(2))) R7_AUDIO_INTERFACE_t; 151 | 152 | typedef struct R8_CLOCKING_2_t{ 153 | uint16_t BCLKDIV :4; // bit 3:0 154 | uint16_t DCLKDIV :3; // bit 2:0 155 | }__attribute__((packed, aligned(2))) R8_CLOCKING_2_t; 156 | 157 | typedef struct R9_AUDIO_INTERFACE_t{ 158 | uint16_t LOOPBACK :1; // bit 0 159 | uint16_t ADCCOMP :2; // bits 1:0 160 | uint16_t DACCOMP :2; // bits 1:0 161 | uint16_t WL8 :1; // bit 5 162 | uint16_t ALRCGPIO :1; // bit 6 163 | }__attribute__((packed, aligned(2))) R9_AUDIO_INTERFACE_t; 164 | 165 | typedef struct R10_LEFT_DAC_VOLUME_t{ 166 | uint16_t LDACVOL :8; // bit 7:0 167 | uint16_t DACVU :1; // bit 8 168 | }__attribute__((packed, aligned(2))) R10_LEFT_DAC_VOLUME_t; 169 | 170 | typedef struct R11_RIGHT_DAC_VOLUME_t{ 171 | uint16_t RDACVOL :8; // bit 7:0 172 | uint16_t DACVU :1; // bit 8 173 | }__attribute__((packed, aligned(2))) R11_RIGHT_DAC_VOLUME_t; 174 | 175 | // typedef struct R15_RESET_t{}; 176 | 177 | typedef struct R16_3D_CONTROL_t{ 178 | uint16_t D3EN :1; // bit 0 179 | uint16_t D3DEPTH :4; // bits 3:0 180 | uint16_t D3LC :1; // bit 5 181 | uint16_t D3UC :1; // bit 6 182 | }__attribute__((packed, aligned(2))) R16_3D_CONTROL_t; 183 | 184 | typedef struct R17_ALC1_t{ 185 | uint16_t ALCL :4; // bits 3:0 186 | uint16_t MAXGAIN :3; // bits 2:0 187 | uint16_t ALCSEL :2; // bits 1:0 188 | }__attribute__((packed, aligned(2))) R17_ALC1_t; 189 | 190 | typedef struct R18_ALC2_t{ 191 | uint16_t HLD :4; // bits 3:0 192 | uint16_t MINGAIN :3; // bits 2:0 193 | }__attribute__((packed, aligned(2))) R18_ALC2_t; 194 | 195 | typedef struct R19_ALC3_t{ 196 | uint16_t ATK :4; // bits 3:0 197 | uint16_t DCY :4; // bits 3:0 198 | }__attribute__((packed, aligned(2))) R19_ALC3_t; 199 | 200 | typedef struct R20_NOISE_GATE_t{ 201 | uint16_t NGAT :1; // bit 0 202 | uint16_t NGTH :5; // bit 4:0 203 | }__attribute__((packed, aligned(2))) R20_NOISE_GATE_t; 204 | 205 | typedef struct R21_LEFT_ADC_VOLUME_t{ 206 | uint16_t LADCVOL :8; // bits 7:0 207 | uint16_t ADCVU :1; // bit 8 208 | }__attribute__((packed, aligned(2))) R21_LEFT_ADC_VOLUME_t; 209 | 210 | typedef struct R22_RIGHT_ADC_VOLUME_t{ 211 | uint16_t RADCVOL :8; // bits 7:0 212 | uint16_t ADCVU :1; // bit 8 213 | }__attribute__((packed, aligned(2))) R22_RIGHT_ADC_VOLUME_t; 214 | 215 | typedef struct R23_ADDITIONAL_CONTROL_1_t{ 216 | uint16_t TOEN :1; // bit 1 217 | uint16_t TOCLKSEL :1; // bit 2 218 | uint16_t DATSEL :2; // bits 1:0 219 | uint16_t DMONOMIX :1; // bit 4 220 | uint16_t VSEL :2; // bits 1:0 221 | uint16_t TSDEN :1; // bit 8 222 | }__attribute__((packed, aligned(2))) R23_ADDITIONAL_CONTROL_1_t; 223 | 224 | typedef struct R24_ADDITIONAL_CONTROL_2_t{ 225 | uint16_t LRCM :1; // bit 2 226 | uint16_t TRIS :1; // bit 3 227 | uint16_t HPSWPOL :1; // bit 5 228 | uint16_t HPSWEN :1; // bit 6 229 | }__attribute__((packed, aligned(2))) R24_ADDITIONAL_CONTROL_2_t; 230 | 231 | typedef struct R25_PWR_MGMT_1_t{ 232 | uint16_t DIGENB :1; // bit 0 233 | uint16_t MICB :1; // bit 1 234 | uint16_t ADCR :1; // bit 2 235 | uint16_t ADCL :1; // bit 3 236 | uint16_t AINR :1; // bit 4 237 | uint16_t AINL :1; // bit 5 238 | uint16_t VREF :1; // bit 6 239 | uint16_t VMIDSEL :2; // bit 1:0 240 | }__attribute__((packed, aligned(2))) R25_PWR_MGMT_1_t; 241 | 242 | typedef struct R26_PWR_MGMT_2_t{ 243 | uint16_t PLL_EN :1; // bit 0 244 | uint16_t OUT3 :1; // bit 1 245 | uint16_t SPKR :1; // bit 3 246 | uint16_t SPKL :1; // bit 4 247 | uint16_t ROUT1 :1; // bit 5 248 | uint16_t LOUT1 :1; // bit 6 249 | uint16_t DACR :1; // bit 7 250 | uint16_t DACL :1; // bit 8 251 | }__attribute__((packed, aligned(2))) R26_PWR_MGMT_2_t; 252 | 253 | typedef struct R27_ADDITIONAL_CONTROL_3_t{ 254 | uint16_t ADC_ALC_SR :3; // bits 2:0 255 | uint16_t OUT3CAP :1; // bit 3 256 | uint16_t VROI :1; // bit 6 257 | }__attribute__((packed, aligned(2))) R27_ADDITIONAL_CONTROL_3_t; 258 | 259 | typedef struct R28_ANTI_POP_1_t{ 260 | uint16_t HPSTBY :1; // bit 0 261 | uint16_t SOFT_ST :1; // bit 2 262 | uint16_t BUFIOEN :1; // bit 3 263 | uint16_t BUFDCOPEN :1; // bit 4 264 | uint16_t POBCTRL :1; // bit 7 265 | }__attribute__((packed, aligned(2))) R28_ANTI_POP_1_t; 266 | 267 | typedef struct R29_ANTI_POP_2_t{ 268 | uint16_t DRES :2; // bits 1:0 269 | uint16_t DISOP :1; // bit 5 270 | }__attribute__((packed, aligned(2))) R29_ANTI_POP_2_t; 271 | 272 | typedef struct R32_ADCL_SIGNAL_PATH_t{ 273 | uint16_t LMIC2B :1; // bit 3 274 | uint16_t LMICBOOST :2; // bits 1:0 275 | uint16_t LMP2 :1; // bit 6 276 | uint16_t LMP3 :1; // bit 7 277 | uint16_t LMN1 :1; // bit 8 278 | }__attribute__((packed, aligned(2))) R32_ADCL_SIGNAL_PATH_t; 279 | 280 | typedef struct R33_ADCR_SIGNAL_PATH_t{ 281 | uint16_t RMIC2B :1; // bit 3 282 | uint16_t RMICBOOST :2; // bits 1:0 283 | uint16_t RMP2 :1; // bit 6 284 | uint16_t RMP3 :1; // bit 7 285 | uint16_t RMN1 :1; // bit 8 286 | }__attribute__((packed, aligned(2))) R33_ADCR_SIGNAL_PATH_t; 287 | 288 | typedef struct R34_LEFT_OUT_MIX_1_t{ 289 | uint16_t LI2LOVOL :3; // bits 2:0 290 | uint16_t LI2LO :1; // bit 7 291 | uint16_t LD2LO :1; // bit 8 292 | }__attribute__((packed, aligned(2))) R34_LEFT_OUT_MIX_1_t; 293 | 294 | typedef struct R37_RIGHT_OUT_MIX_2_t{ 295 | uint16_t RI2ROVOL :3; // bits 2:0 296 | uint16_t RI2RO :1; // bit 7 297 | uint16_t RD2RO :1; // bit 8 298 | }__attribute__((packed, aligned(2))) R37_RIGHT_OUT_MIX_2_t; 299 | 300 | typedef struct R38_MONO_OUT_MIX_1_t{ 301 | uint16_t L2MO :1; // bit 7 302 | }__attribute__((packed, aligned(2))) R38_MONO_OUT_MIX_1_t; 303 | 304 | typedef struct R39_MONO_OUT_MIX_2_t{ 305 | uint16_t R2MO :1; // bit 7 306 | }__attribute__((packed, aligned(2))) R39_MONO_OUT_MIX_2_t; 307 | 308 | typedef struct R40_LOUT2_VOLUME_t{ 309 | uint16_t SPKLVOL :7; // bits 6:0 310 | uint16_t SPKLZC :1; // bit 7 311 | uint16_t SPKVU :1; // bit 8 312 | }__attribute__((packed, aligned(2))) R40_LOUT2_VOLUME_t; 313 | 314 | typedef struct R41_ROUT2_VOLUME_t{ 315 | uint16_t SPKRVOL :7; // bits 6:0 316 | uint16_t SPKLZC :1; // bit 7 317 | uint16_t SPKVU :1; // bit 8 318 | }__attribute__((packed, aligned(2))) R41_ROUT2_VOLUME_t; 319 | 320 | typedef struct R42_MONOOUT_VOLUME_t{ 321 | uint16_t MOUTVOL :1; //bit 6 322 | }__attribute__((packed, aligned(2))) R42_MONOOUT_VOLUME_t; 323 | 324 | typedef struct R43_INPUT_BOOST_MIXER_1_t{ 325 | uint16_t LIN2BOOST :3; // bits 2:0 326 | uint16_t LIN3BOOST :3; // bits 2:0 327 | }__attribute__((packed, aligned(2))) R43_INPUT_BOOST_MIXER_1_t; 328 | 329 | typedef struct R44_INPUT_BOOST_MIXER_2_t{ 330 | uint16_t RIN2BOOST :3; // bits 2:0 331 | uint16_t RIN3BOOST :3; // bits 2:0 332 | }__attribute__((packed, aligned(2))) R44_INPUT_BOOST_MIXER_2_t; 333 | 334 | typedef struct R45_BYPASS_1_t{ 335 | uint16_t LB2LOVOL :3; // bits 2:0 336 | uint16_t LB2LO :1; // bit 5 337 | }__attribute__((packed, aligned(2))) R45_BYPASS_1_t; 338 | 339 | typedef struct R46_BYPASS_2_t{ 340 | uint16_t RB2ROVOL :3; // bits 2:0 341 | uint16_t RB2RO :1; // bit 5 342 | }__attribute__((packed, aligned(2))) R46_BYPASS_2_t; 343 | 344 | typedef struct R47_PWR_MGMT_3_t{ 345 | uint16_t ROMIX :1; // bit 2 346 | uint16_t LOMIX :1; // bit 3 347 | uint16_t RMIC :1; // bit 4 348 | uint16_t LMIC :1; // bit 5 349 | }__attribute__((packed, aligned(2))) R47_PWR_MGMT_3_t; 350 | 351 | typedef struct R48_ADDITIONAL_CONTROL_4_t{ 352 | uint16_t MBSEL :1; // bit 0 353 | uint16_t TSENSEN :1; // bit 1 354 | uint16_t HPSEL :2; // bits 1:0 355 | uint16_t GPIOSEL :3; // bits 2:0 356 | uint16_t GPIOPOL :1; // bit 7 357 | }__attribute__((packed, aligned(2))) R48_ADDITIONAL_CONTROL_4_t; 358 | 359 | typedef struct R49_CLASS_D_CONTROL_1_t{ 360 | uint16_t SPK_OP_EN :2; // bits 1:0 361 | }__attribute__((packed, aligned(2))) R49_CLASS_D_CONTROL_1_t; 362 | 363 | typedef struct R51_CLASS_D_CONTROL_3_t{ 364 | uint16_t ACGAIN :3; // bits 2:0 365 | uint16_t DCGAIN :3; // bits 2:0 366 | }__attribute__((packed, aligned(2))) R51_CLASS_D_CONTROL_3_t; 367 | 368 | typedef struct R52_PLL_N_t{ 369 | uint16_t PLLN :4; // bits 3:0 370 | uint16_t PLLRESCALE :1; // bit 4 371 | uint16_t SDM :1; // bit 5 372 | uint16_t OPCLKDIV :3; // bits 2:0 373 | }__attribute__((packed, aligned(2))) R52_PLL_N_t; 374 | 375 | typedef struct R53_PLL_K_1_t{ 376 | uint16_t PLLK :8; // bits 23:16 377 | }__attribute__((packed, aligned(2))) R53_PLL_K_1_t; 378 | 379 | typedef struct R54_PLL_K_2_t{ 380 | uint16_t PLLK :8; // bits 15:8 381 | }__attribute__((packed, aligned(2))) R54_PLL_K_2_t; 382 | 383 | typedef struct R55_PLL_K_3_t{ 384 | uint16_t PLLK :8; // bits 7:0 385 | }__attribute__((packed, aligned(2))) R55_PLL_K_3_t; 386 | 387 | esp_err_t wm8960_init(); 388 | 389 | esp_err_t wm8960_set_vol(int vol); 390 | 391 | esp_err_t wm8960_set_mute(bool mute); 392 | 393 | #endif -------------------------------------------------------------------------------- /components/a2dp_sink/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "bt_app_core.c" 2 | "bt_app_av.c" 3 | INCLUDE_DIRS "." 4 | REQUIRES bt) 5 | 6 | -------------------------------------------------------------------------------- /components/a2dp_sink/bt_app_av.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "esp_log.h" 15 | 16 | #include "bt_app_core.h" 17 | #include "bt_app_av.h" 18 | #include "esp_bt_main.h" 19 | #include "esp_bt_device.h" 20 | #include "esp_gap_bt_api.h" 21 | #include "esp_a2dp_api.h" 22 | #include "esp_avrc_api.h" 23 | 24 | #include "freertos/FreeRTOS.h" 25 | #include "freertos/task.h" 26 | #include "driver/i2s.h" 27 | 28 | #include "sys/lock.h" 29 | 30 | // AVRCP used transaction label 31 | #define APP_RC_CT_TL_GET_CAPS (0) 32 | #define APP_RC_CT_TL_GET_META_DATA (1) 33 | #define APP_RC_CT_TL_RN_TRACK_CHANGE (2) 34 | #define APP_RC_CT_TL_RN_PLAYBACK_CHANGE (3) 35 | #define APP_RC_CT_TL_RN_PLAY_POS_CHANGE (4) 36 | 37 | /* a2dp event handler */ 38 | static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param); 39 | /* avrc CT event handler */ 40 | static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param); 41 | /* avrc TG event handler */ 42 | static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param); 43 | 44 | static uint32_t s_pkt_cnt = 0; 45 | static esp_a2d_audio_state_t s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED; 46 | static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"}; 47 | static const char *s_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"}; 48 | static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap; 49 | static _lock_t s_volume_lock; 50 | static xTaskHandle s_vcs_task_hdl = NULL; 51 | static uint8_t s_volume = 0; 52 | static bool s_volume_notify; 53 | 54 | /* callback for A2DP sink */ 55 | void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) 56 | { 57 | switch (event) { 58 | case ESP_A2D_CONNECTION_STATE_EVT: 59 | case ESP_A2D_AUDIO_STATE_EVT: 60 | case ESP_A2D_AUDIO_CFG_EVT: 61 | case ESP_A2D_PROF_STATE_EVT: { 62 | bt_app_work_dispatch(bt_av_hdl_a2d_evt, event, param, sizeof(esp_a2d_cb_param_t), NULL); 63 | break; 64 | } 65 | default: 66 | ESP_LOGE(BT_AV_TAG, "Invalid A2DP event: %d", event); 67 | break; 68 | } 69 | } 70 | 71 | void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len) 72 | { 73 | write_ringbuf(data, len); 74 | if (++s_pkt_cnt % 100 == 0) { 75 | ESP_LOGI(BT_AV_TAG, "Audio packet count %u", s_pkt_cnt); 76 | } 77 | } 78 | 79 | void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param) 80 | { 81 | esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param); 82 | uint8_t *attr_text = (uint8_t *) malloc (rc->meta_rsp.attr_length + 1); 83 | memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length); 84 | attr_text[rc->meta_rsp.attr_length] = 0; 85 | 86 | rc->meta_rsp.attr_text = attr_text; 87 | } 88 | 89 | void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) 90 | { 91 | switch (event) { 92 | case ESP_AVRC_CT_METADATA_RSP_EVT: 93 | bt_app_alloc_meta_buffer(param); 94 | /* fall through */ 95 | case ESP_AVRC_CT_CONNECTION_STATE_EVT: 96 | case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: 97 | case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: 98 | case ESP_AVRC_CT_REMOTE_FEATURES_EVT: 99 | case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: { 100 | bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL); 101 | break; 102 | } 103 | default: 104 | ESP_LOGE(BT_RC_CT_TAG, "Invalid AVRC event: %d", event); 105 | break; 106 | } 107 | } 108 | 109 | void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param) 110 | { 111 | switch (event) { 112 | case ESP_AVRC_TG_CONNECTION_STATE_EVT: 113 | case ESP_AVRC_TG_REMOTE_FEATURES_EVT: 114 | case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: 115 | case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: 116 | case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: 117 | case ESP_AVRC_TG_SET_PLAYER_APP_VALUE_EVT: 118 | bt_app_work_dispatch(bt_av_hdl_avrc_tg_evt, event, param, sizeof(esp_avrc_tg_cb_param_t), NULL); 119 | break; 120 | default: 121 | ESP_LOGE(BT_RC_TG_TAG, "Invalid AVRC event: %d", event); 122 | break; 123 | } 124 | } 125 | 126 | static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param) 127 | { 128 | ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event); 129 | esp_a2d_cb_param_t *a2d = NULL; 130 | switch (event) { 131 | case ESP_A2D_CONNECTION_STATE_EVT: { 132 | a2d = (esp_a2d_cb_param_t *)(p_param); 133 | uint8_t *bda = a2d->conn_stat.remote_bda; 134 | ESP_LOGI(BT_AV_TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]", 135 | s_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); 136 | if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { 137 | esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); 138 | bt_i2s_task_shut_down(); 139 | } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){ 140 | esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); 141 | bt_i2s_task_start_up(); 142 | } 143 | break; 144 | } 145 | case ESP_A2D_AUDIO_STATE_EVT: { 146 | a2d = (esp_a2d_cb_param_t *)(p_param); 147 | ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", s_a2d_audio_state_str[a2d->audio_stat.state]); 148 | s_audio_state = a2d->audio_stat.state; 149 | if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) { 150 | s_pkt_cnt = 0; 151 | } 152 | break; 153 | } 154 | case ESP_A2D_AUDIO_CFG_EVT: { 155 | a2d = (esp_a2d_cb_param_t *)(p_param); 156 | ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type %d", a2d->audio_cfg.mcc.type); 157 | // for now only SBC stream is supported 158 | if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) { 159 | int sample_rate = 16000; 160 | char oct0 = a2d->audio_cfg.mcc.cie.sbc[0]; 161 | if (oct0 & (0x01 << 6)) { 162 | sample_rate = 32000; 163 | } else if (oct0 & (0x01 << 5)) { 164 | sample_rate = 44100; 165 | } else if (oct0 & (0x01 << 4)) { 166 | sample_rate = 48000; 167 | } 168 | i2s_set_clk(0, sample_rate, 16, 2); 169 | 170 | ESP_LOGI(BT_AV_TAG, "Configure audio player %x-%x-%x-%x", 171 | a2d->audio_cfg.mcc.cie.sbc[0], 172 | a2d->audio_cfg.mcc.cie.sbc[1], 173 | a2d->audio_cfg.mcc.cie.sbc[2], 174 | a2d->audio_cfg.mcc.cie.sbc[3]); 175 | ESP_LOGI(BT_AV_TAG, "Audio player configured, sample rate=%d", sample_rate); 176 | } 177 | break; 178 | } 179 | case ESP_A2D_PROF_STATE_EVT: { 180 | a2d = (esp_a2d_cb_param_t *)(p_param); 181 | if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { 182 | ESP_LOGI(BT_AV_TAG,"A2DP PROF STATE: Init Compl\n"); 183 | } else { 184 | ESP_LOGI(BT_AV_TAG,"A2DP PROF STATE: Deinit Compl\n"); 185 | } 186 | break; 187 | } 188 | default: 189 | ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); 190 | break; 191 | } 192 | } 193 | 194 | static void bt_av_new_track(void) 195 | { 196 | // request metadata 197 | uint8_t attr_mask = ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_GENRE; 198 | esp_avrc_ct_send_metadata_cmd(APP_RC_CT_TL_GET_META_DATA, attr_mask); 199 | 200 | // register notification if peer support the event_id 201 | if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, 202 | ESP_AVRC_RN_TRACK_CHANGE)) { 203 | esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_TRACK_CHANGE, ESP_AVRC_RN_TRACK_CHANGE, 0); 204 | } 205 | } 206 | 207 | static void bt_av_playback_changed(void) 208 | { 209 | if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, 210 | ESP_AVRC_RN_PLAY_STATUS_CHANGE)) { 211 | esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAYBACK_CHANGE, ESP_AVRC_RN_PLAY_STATUS_CHANGE, 0); 212 | } 213 | } 214 | 215 | static void bt_av_play_pos_changed(void) 216 | { 217 | if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, 218 | ESP_AVRC_RN_PLAY_POS_CHANGED)) { 219 | esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAY_POS_CHANGE, ESP_AVRC_RN_PLAY_POS_CHANGED, 10); 220 | } 221 | } 222 | 223 | void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter) 224 | { 225 | switch (event_id) { 226 | case ESP_AVRC_RN_TRACK_CHANGE: 227 | bt_av_new_track(); 228 | break; 229 | case ESP_AVRC_RN_PLAY_STATUS_CHANGE: 230 | ESP_LOGI(BT_AV_TAG, "Playback status changed: 0x%x", event_parameter->playback); 231 | bt_av_playback_changed(); 232 | break; 233 | case ESP_AVRC_RN_PLAY_POS_CHANGED: 234 | ESP_LOGI(BT_AV_TAG, "Play position changed: %d-ms", event_parameter->play_pos); 235 | bt_av_play_pos_changed(); 236 | break; 237 | } 238 | } 239 | 240 | static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param) 241 | { 242 | ESP_LOGD(BT_RC_CT_TAG, "%s evt %d", __func__, event); 243 | esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(p_param); 244 | switch (event) { 245 | case ESP_AVRC_CT_CONNECTION_STATE_EVT: { 246 | uint8_t *bda = rc->conn_stat.remote_bda; 247 | ESP_LOGI(BT_RC_CT_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]", 248 | rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); 249 | 250 | if (rc->conn_stat.connected) { 251 | // get remote supported event_ids of peer AVRCP Target 252 | esp_avrc_ct_send_get_rn_capabilities_cmd(APP_RC_CT_TL_GET_CAPS); 253 | } else { 254 | // clear peer notification capability record 255 | s_avrc_peer_rn_cap.bits = 0; 256 | } 257 | break; 258 | } 259 | case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: { 260 | ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state); 261 | break; 262 | } 263 | case ESP_AVRC_CT_METADATA_RSP_EVT: { 264 | ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text); 265 | free(rc->meta_rsp.attr_text); 266 | break; 267 | } 268 | case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: { 269 | ESP_LOGI(BT_RC_CT_TAG, "AVRC event notification: %d", rc->change_ntf.event_id); 270 | bt_av_notify_evt_handler(rc->change_ntf.event_id, &rc->change_ntf.event_parameter); 271 | break; 272 | } 273 | case ESP_AVRC_CT_REMOTE_FEATURES_EVT: { 274 | ESP_LOGI(BT_RC_CT_TAG, "AVRC remote features %x, TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag); 275 | break; 276 | } 277 | case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: { 278 | ESP_LOGI(BT_RC_CT_TAG, "remote rn_cap: count %d, bitmask 0x%x", rc->get_rn_caps_rsp.cap_count, 279 | rc->get_rn_caps_rsp.evt_set.bits); 280 | s_avrc_peer_rn_cap.bits = rc->get_rn_caps_rsp.evt_set.bits; 281 | bt_av_new_track(); 282 | bt_av_playback_changed(); 283 | bt_av_play_pos_changed(); 284 | break; 285 | } 286 | default: 287 | ESP_LOGE(BT_RC_CT_TAG, "%s unhandled evt %d", __func__, event); 288 | break; 289 | } 290 | } 291 | 292 | static void volume_set_by_controller(uint8_t volume) 293 | { 294 | ESP_LOGI(BT_RC_TG_TAG, "Volume is set by remote controller %d%%\n", (uint32_t)volume * 100 / 0x7f); 295 | _lock_acquire(&s_volume_lock); 296 | s_volume = volume; 297 | _lock_release(&s_volume_lock); 298 | } 299 | 300 | static void volume_set_by_local_host(uint8_t volume) 301 | { 302 | ESP_LOGI(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f); 303 | _lock_acquire(&s_volume_lock); 304 | s_volume = volume; 305 | _lock_release(&s_volume_lock); 306 | 307 | if (s_volume_notify) { 308 | esp_avrc_rn_param_t rn_param; 309 | rn_param.volume = s_volume; 310 | esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_CHANGED, &rn_param); 311 | s_volume_notify = false; 312 | } 313 | } 314 | 315 | static void volume_change_simulation(void *arg) 316 | { 317 | ESP_LOGI(BT_RC_TG_TAG, "start volume change simulation"); 318 | 319 | for (;;) { 320 | vTaskDelay(10000 / portTICK_RATE_MS); 321 | 322 | uint8_t volume = (s_volume + 5) & 0x7f; 323 | volume_set_by_local_host(volume); 324 | } 325 | } 326 | 327 | static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param) 328 | { 329 | ESP_LOGD(BT_RC_TG_TAG, "%s evt %d", __func__, event); 330 | esp_avrc_tg_cb_param_t *rc = (esp_avrc_tg_cb_param_t *)(p_param); 331 | switch (event) { 332 | case ESP_AVRC_TG_CONNECTION_STATE_EVT: { 333 | uint8_t *bda = rc->conn_stat.remote_bda; 334 | ESP_LOGI(BT_RC_TG_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]", 335 | rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); 336 | if (rc->conn_stat.connected) { 337 | // create task to simulate volume change 338 | xTaskCreate(volume_change_simulation, "vcsT", 2048, NULL, 5, &s_vcs_task_hdl); 339 | } else { 340 | vTaskDelete(s_vcs_task_hdl); 341 | ESP_LOGI(BT_RC_TG_TAG, "Stop volume change simulation"); 342 | } 343 | break; 344 | } 345 | case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: { 346 | ESP_LOGI(BT_RC_TG_TAG, "AVRC passthrough cmd: key_code 0x%x, key_state %d", rc->psth_cmd.key_code, rc->psth_cmd.key_state); 347 | break; 348 | } 349 | case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: { 350 | ESP_LOGI(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100/ 0x7f); 351 | volume_set_by_controller(rc->set_abs_vol.volume); 352 | break; 353 | } 354 | case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: { 355 | ESP_LOGI(BT_RC_TG_TAG, "AVRC register event notification: %d, param: 0x%x", rc->reg_ntf.event_id, rc->reg_ntf.event_parameter); 356 | if (rc->reg_ntf.event_id == ESP_AVRC_RN_VOLUME_CHANGE) { 357 | s_volume_notify = true; 358 | esp_avrc_rn_param_t rn_param; 359 | rn_param.volume = s_volume; 360 | esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_INTERIM, &rn_param); 361 | } 362 | break; 363 | } 364 | case ESP_AVRC_TG_REMOTE_FEATURES_EVT: { 365 | ESP_LOGI(BT_RC_TG_TAG, "AVRC remote features %x, CT features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.ct_feat_flag); 366 | break; 367 | } 368 | default: 369 | ESP_LOGE(BT_RC_TG_TAG, "%s unhandled evt %d", __func__, event); 370 | break; 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /components/a2dp_sink/bt_app_av.h: -------------------------------------------------------------------------------- 1 | /* 2 | This example code is in the Public Domain (or CC0 licensed, at your option.) 3 | 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #ifndef __BT_APP_AV_H__ 10 | #define __BT_APP_AV_H__ 11 | 12 | #include 13 | #include "esp_a2dp_api.h" 14 | #include "esp_avrc_api.h" 15 | 16 | #define BT_AV_TAG "BT_AV" 17 | #define BT_RC_TG_TAG "RCTG" 18 | #define BT_RC_CT_TAG "RCCT" 19 | 20 | /** 21 | * @brief callback function for A2DP sink 22 | */ 23 | void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); 24 | 25 | /** 26 | * @brief callback function for A2DP sink audio data stream 27 | */ 28 | void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len); 29 | 30 | /** 31 | * @brief callback function for AVRCP controller 32 | */ 33 | void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param); 34 | 35 | /** 36 | * @brief callback function for AVRCP target 37 | */ 38 | void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param); 39 | 40 | #endif /* __BT_APP_AV_H__*/ 41 | -------------------------------------------------------------------------------- /components/a2dp_sink/bt_app_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | This example code is in the Public Domain (or CC0 licensed, at your option.) 3 | 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/xtensa_api.h" 13 | #include "freertos/FreeRTOSConfig.h" 14 | #include "freertos/FreeRTOS.h" 15 | #include "freertos/queue.h" 16 | #include "freertos/task.h" 17 | #include "esp_log.h" 18 | #include "bt_app_core.h" 19 | #include "driver/i2s.h" 20 | #include "freertos/ringbuf.h" 21 | 22 | static void bt_app_task_handler(void *arg); 23 | static bool bt_app_send_msg(bt_app_msg_t *msg); 24 | static void bt_app_work_dispatched(bt_app_msg_t *msg); 25 | 26 | static xQueueHandle s_bt_app_task_queue = NULL; 27 | static xTaskHandle s_bt_app_task_handle = NULL; 28 | static xTaskHandle s_bt_i2s_task_handle = NULL; 29 | static RingbufHandle_t s_ringbuf_i2s = NULL;; 30 | 31 | bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback) 32 | { 33 | ESP_LOGD(BT_APP_CORE_TAG, "%s event 0x%x, param len %d", __func__, event, param_len); 34 | 35 | bt_app_msg_t msg; 36 | memset(&msg, 0, sizeof(bt_app_msg_t)); 37 | 38 | msg.sig = BT_APP_SIG_WORK_DISPATCH; 39 | msg.event = event; 40 | msg.cb = p_cback; 41 | 42 | if (param_len == 0) { 43 | return bt_app_send_msg(&msg); 44 | } else if (p_params && param_len > 0) { 45 | if ((msg.param = malloc(param_len)) != NULL) { 46 | memcpy(msg.param, p_params, param_len); 47 | /* check if caller has provided a copy callback to do the deep copy */ 48 | if (p_copy_cback) { 49 | p_copy_cback(&msg, msg.param, p_params); 50 | } 51 | return bt_app_send_msg(&msg); 52 | } 53 | } 54 | 55 | return false; 56 | } 57 | 58 | static bool bt_app_send_msg(bt_app_msg_t *msg) 59 | { 60 | if (msg == NULL) { 61 | return false; 62 | } 63 | 64 | if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) { 65 | ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__); 66 | return false; 67 | } 68 | return true; 69 | } 70 | 71 | static void bt_app_work_dispatched(bt_app_msg_t *msg) 72 | { 73 | if (msg->cb) { 74 | msg->cb(msg->event, msg->param); 75 | } 76 | } 77 | 78 | static void bt_app_task_handler(void *arg) 79 | { 80 | bt_app_msg_t msg; 81 | for (;;) { 82 | if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) { /*xqueue returns pdTRUE if item received*/ 83 | ESP_LOGD(BT_APP_CORE_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event); 84 | switch (msg.sig) { 85 | case BT_APP_SIG_WORK_DISPATCH: 86 | bt_app_work_dispatched(&msg); 87 | break; 88 | default: 89 | ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: %d", __func__, msg.sig); 90 | break; 91 | } // switch (msg.sig) 92 | 93 | if (msg.param) { 94 | free(msg.param); 95 | } 96 | } 97 | } 98 | } 99 | 100 | void bt_app_task_start_up(void) 101 | { 102 | s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t)); 103 | xTaskCreate(bt_app_task_handler, "BtAppT", 3072, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle); 104 | return; 105 | } 106 | 107 | void bt_app_task_shut_down(void) 108 | { 109 | if (s_bt_app_task_handle) { 110 | vTaskDelete(s_bt_app_task_handle); 111 | s_bt_app_task_handle = NULL; 112 | } 113 | if (s_bt_app_task_queue) { 114 | vQueueDelete(s_bt_app_task_queue); 115 | s_bt_app_task_queue = NULL; 116 | } 117 | } 118 | 119 | static void bt_i2s_task_handler(void *arg) 120 | { 121 | uint8_t *data = NULL; 122 | size_t item_size = 0; 123 | size_t bytes_written = 0; 124 | 125 | for (;;) { 126 | data = (uint8_t *)xRingbufferReceive(s_ringbuf_i2s, &item_size, (portTickType)portMAX_DELAY); 127 | if (item_size != 0){ 128 | i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY); 129 | vRingbufferReturnItem(s_ringbuf_i2s,(void *)data); 130 | } 131 | } 132 | } 133 | 134 | void bt_i2s_task_start_up(void) 135 | { 136 | s_ringbuf_i2s = xRingbufferCreate(8 * 1024, RINGBUF_TYPE_BYTEBUF); 137 | if(s_ringbuf_i2s == NULL){ 138 | return; 139 | } 140 | 141 | xTaskCreate(bt_i2s_task_handler, "BtI2ST", 1024, NULL, configMAX_PRIORITIES - 3, &s_bt_i2s_task_handle); 142 | return; 143 | } 144 | 145 | void bt_i2s_task_shut_down(void) 146 | { 147 | if (s_bt_i2s_task_handle) { 148 | vTaskDelete(s_bt_i2s_task_handle); 149 | s_bt_i2s_task_handle = NULL; 150 | } 151 | 152 | if (s_ringbuf_i2s) { 153 | vRingbufferDelete(s_ringbuf_i2s); 154 | s_ringbuf_i2s = NULL; 155 | } 156 | } 157 | 158 | size_t write_ringbuf(const uint8_t *data, size_t size) 159 | { 160 | BaseType_t done = xRingbufferSend(s_ringbuf_i2s, (void *)data, size, (portTickType)portMAX_DELAY); 161 | if(done){ 162 | return size; 163 | } else { 164 | return 0; 165 | } 166 | } -------------------------------------------------------------------------------- /components/a2dp_sink/bt_app_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | This example code is in the Public Domain (or CC0 licensed, at your option.) 3 | 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #ifndef __BT_APP_CORE_H__ 10 | #define __BT_APP_CORE_H__ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #define BT_APP_CORE_TAG "BT_APP_CORE" 17 | 18 | #define BT_APP_SIG_WORK_DISPATCH (0x01) 19 | 20 | /** 21 | * @brief handler for the dispatched work 22 | */ 23 | typedef void (* bt_app_cb_t) (uint16_t event, void *param); 24 | 25 | /* message to be sent */ 26 | typedef struct { 27 | uint16_t sig; /*!< signal to bt_app_task */ 28 | uint16_t event; /*!< message event id */ 29 | bt_app_cb_t cb; /*!< context switch callback */ 30 | void *param; /*!< parameter area needs to be last */ 31 | } bt_app_msg_t; 32 | 33 | /** 34 | * @brief parameter deep-copy function to be customized 35 | */ 36 | typedef void (* bt_app_copy_cb_t) (bt_app_msg_t *msg, void *p_dest, void *p_src); 37 | 38 | /** 39 | * @brief work dispatcher for the application task 40 | */ 41 | bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback); 42 | 43 | void bt_app_task_start_up(void); 44 | 45 | void bt_app_task_shut_down(void); 46 | 47 | void bt_i2s_task_start_up(void); 48 | 49 | void bt_i2s_task_shut_down(void); 50 | 51 | size_t write_ringbuf(const uint8_t *data, size_t size); 52 | 53 | #endif /* __BT_APP_CORE_H__ */ 54 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Controls 7 | 93 | 94 | 95 |
96 |

Set bluetooth name

97 | 98 | 99 |
100 | 101 | 102 |
103 | 104 |
105 | 114 | 115 | -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" 2 | "tuning_http_server.c" 3 | "wifi_handler.c" 4 | INCLUDE_DIRS ".") 5 | spiffs_create_partition_image(www "${CMAKE_CURRENT_SOURCE_DIR}/../frontend" FLASH_IN_PROJECT) 6 | -------------------------------------------------------------------------------- /main/Kconfig: -------------------------------------------------------------------------------- 1 | menu "Wall-E Configuration" 2 | 3 | menu "WiFi Config" 4 | config WIFI_SSID 5 | string "WiFi SSID" 6 | default "myssid" 7 | help 8 | SSID (network name) for the example to connect to. 9 | 10 | config WIFI_PASSWORD 11 | string "WiFi Password" 12 | default "mypassword" 13 | help 14 | WiFi password (WPA or WPA2) for the example to use. 15 | 16 | config MAXIMUM_RETRY 17 | int "Maximum retry" 18 | default 5 19 | help 20 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 21 | endmenu 22 | 23 | menu "mDNS Config" 24 | config MDNS_HOST_NAME 25 | string "mDNS host name (< 15 chars)" 26 | default "walle-tuning" 27 | help 28 | mDNS hostname used to access the tuning webpage, it should be less than 15 chars 29 | endmenu 30 | 31 | endmenu 32 | -------------------------------------------------------------------------------- /main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "A2DP Example Configuration" 2 | 3 | choice EXAMPLE_A2DP_SINK_OUTPUT 4 | prompt "A2DP Sink Output" 5 | default EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S 6 | help 7 | Select to use Internal DAC or external I2S driver 8 | 9 | config EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC 10 | bool "Internal DAC" 11 | help 12 | Select this to use Internal DAC sink output 13 | 14 | config EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S 15 | bool "External I2S Codec" 16 | help 17 | Select this to use External I2S sink output 18 | 19 | endchoice 20 | 21 | config EXAMPLE_I2S_LRCK_PIN 22 | int "I2S LRCK (WS) GPIO" 23 | default 22 24 | depends on EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S 25 | help 26 | GPIO number to use for I2S LRCK(WS) Driver. 27 | 28 | config EXAMPLE_I2S_BCK_PIN 29 | int "I2S BCK GPIO" 30 | default 26 31 | depends on EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S 32 | help 33 | GPIO number to use for I2S BCK Driver. 34 | 35 | config EXAMPLE_I2S_DATA_PIN 36 | int "I2S DATA GPIO" 37 | default 25 38 | depends on EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S 39 | help 40 | GPIO number to use for I2S Data Driver. 41 | 42 | endmenu 43 | -------------------------------------------------------------------------------- /main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) 5 | 6 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | #include "nvs.h" 8 | #include "nvs_flash.h" 9 | #include "esp_system.h" 10 | #include "esp_log.h" 11 | #include "esp_err.h" 12 | #include "esp_bt.h" 13 | #include "bt_app_core.h" 14 | #include "bt_app_av.h" 15 | #include "esp_bt_main.h" 16 | #include "esp_bt_device.h" 17 | #include "esp_gap_bt_api.h" 18 | #include "esp_a2dp_api.h" 19 | #include "esp_avrc_api.h" 20 | #include "driver/i2s.h" 21 | #include "wm8960.h" 22 | #include "tuning_http_server.h" 23 | #include "driver/i2c.h" 24 | 25 | void app_main(void) 26 | { 27 | wm8960_init(); 28 | 29 | /* Initialize NVS — it is used to store PHY calibration data */ 30 | esp_err_t err = nvs_flash_init(); 31 | if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { 32 | ESP_ERROR_CHECK(nvs_flash_erase()); 33 | err = nvs_flash_init(); 34 | } 35 | 36 | i2s_config_t i2s_config = { 37 | #ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC 38 | .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, 39 | #else 40 | .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX 41 | #endif 42 | .sample_rate = 48800, 43 | .bits_per_sample = 16, 44 | .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels 45 | .communication_format = I2S_COMM_FORMAT_STAND_MSB, 46 | .dma_buf_count = 6, 47 | .dma_buf_len = 60, 48 | .intr_alloc_flags = 0, //Default interrupt priority 49 | .tx_desc_auto_clear = true //Auto clear tx descriptor on underflow 50 | }; 51 | 52 | 53 | i2s_driver_install(0, &i2s_config, 0, NULL); 54 | #ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC 55 | i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); 56 | i2s_set_pin(0, NULL); 57 | #else 58 | i2s_pin_config_t pin_config = { 59 | .mck_io_num = 0, 60 | .bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN, 61 | .ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN, 62 | .data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN, 63 | .data_in_num = -1 //Not used 64 | }; 65 | 66 | i2s_set_pin(0, &pin_config); 67 | #endif 68 | 69 | 70 | ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); 71 | 72 | esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); 73 | if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { 74 | ESP_LOGE(BT_AV_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err)); 75 | return; 76 | } 77 | 78 | if ((err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { 79 | ESP_LOGE(BT_AV_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(err)); 80 | return; 81 | } 82 | 83 | if ((err = esp_bluedroid_init()) != ESP_OK) { 84 | ESP_LOGE(BT_AV_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(err)); 85 | return; 86 | } 87 | 88 | if ((err = esp_bluedroid_enable()) != ESP_OK) { 89 | ESP_LOGE(BT_AV_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(err)); 90 | return; 91 | } 92 | 93 | start_tuning_http_server(); 94 | } 95 | 96 | -------------------------------------------------------------------------------- /main/tuning_http_server.c: -------------------------------------------------------------------------------- 1 | #include "tuning_http_server.h" 2 | #include 3 | 4 | #include 5 | // #include "main.c" 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | // #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "nvs.h" 15 | #include "nvs_flash.h" 16 | #include "esp_system.h" 17 | #include "esp_log.h" 18 | 19 | #include "esp_bt.h" 20 | #include "bt_app_core.h" 21 | #include "bt_app_av.h" 22 | #include "esp_bt_main.h" 23 | #include "esp_bt_device.h" 24 | #include "esp_gap_bt_api.h" 25 | #include "esp_a2dp_api.h" 26 | #include "esp_avrc_api.h" 27 | #include "driver/i2s.h" 28 | 29 | 30 | static bt_name_t bt_name_obj = {.bt_name = "no name"}; 31 | static const char *TAG = "tuning_http_server"; 32 | static char scratch[SCRATCH_BUFSIZE]; 33 | 34 | bool temp_count = 0; 35 | 36 | bt_name_t read_bt_name() 37 | { 38 | return bt_name_obj; 39 | } 40 | 41 | 42 | 43 | 44 | 45 | /* event for handler "bt_av_hdl_stack_up */ 46 | enum { 47 | BT_APP_EVT_STACK_UP = 0, 48 | }; 49 | 50 | /* handler for bluetooth stack enabled events */ 51 | void bt_av_hdl_stack_evt(uint16_t event, void *p_param); 52 | 53 | void start_bluetooth() 54 | { 55 | ESP_LOGE(BT_AV_TAG, "startblu main.c %s",bt_name_obj.bt_name); 56 | 57 | /* create application task */ 58 | bt_app_task_start_up(); 59 | 60 | /* Bluetooth device name, connection mode and profile set up */ 61 | bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL); 62 | 63 | #if (CONFIG_BT_SSP_ENABLED == true) 64 | /* Set default parameters for Secure Simple Pairing */ 65 | esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; 66 | esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; 67 | esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); 68 | #endif 69 | 70 | /* 71 | * Set default parameters for Legacy Pairing 72 | * Use fixed pin code 73 | */ 74 | esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED; 75 | esp_bt_pin_code_t pin_code; 76 | pin_code[0] = '1'; 77 | pin_code[1] = '2'; 78 | pin_code[2] = '3'; 79 | pin_code[3] = '4'; 80 | esp_bt_gap_set_pin(pin_type, 4, pin_code); 81 | } 82 | 83 | void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) 84 | { 85 | switch (event) { 86 | case ESP_BT_GAP_AUTH_CMPL_EVT: { 87 | if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { 88 | ESP_LOGI(BT_AV_TAG, "authentication success: %s", param->auth_cmpl.device_name); 89 | esp_log_buffer_hex(BT_AV_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN); 90 | } else { 91 | ESP_LOGE(BT_AV_TAG, "authentication failed, status:%d", param->auth_cmpl.stat); 92 | } 93 | break; 94 | } 95 | 96 | #if (CONFIG_BT_SSP_ENABLED == true) 97 | case ESP_BT_GAP_CFM_REQ_EVT: 98 | ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); 99 | esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); 100 | break; 101 | case ESP_BT_GAP_KEY_NOTIF_EVT: 102 | ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); 103 | break; 104 | case ESP_BT_GAP_KEY_REQ_EVT: 105 | ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); 106 | break; 107 | #endif 108 | 109 | default: { 110 | ESP_LOGI(BT_AV_TAG, "event: %d", event); 111 | break; 112 | } 113 | } 114 | return; 115 | } 116 | void bt_av_hdl_stack_evt(uint16_t event, void *p_param) 117 | { 118 | 119 | ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event); 120 | switch (event) { 121 | case BT_APP_EVT_STACK_UP: { 122 | 123 | nvs_handle set_str_handle; 124 | nvs_open("storage", NVS_READWRITE, &set_str_handle); 125 | size_t required_size =100; 126 | char* bluetooth_name = malloc(required_size); 127 | nvs_get_str(set_str_handle, "string_buffer",bluetooth_name,&required_size); 128 | ESP_LOGE(BT_AV_TAG, "NVS_DATA %s",bluetooth_name); 129 | 130 | 131 | 132 | // /* set up device name */ 133 | // char *dev_name = bt_name_obj.bt_name; 134 | 135 | esp_bt_dev_set_device_name(bluetooth_name); 136 | nvs_close(set_str_handle); 137 | 138 | esp_bt_gap_register_callback(bt_app_gap_cb); 139 | 140 | /* initialize AVRCP controller */ 141 | esp_avrc_ct_init(); 142 | esp_avrc_ct_register_callback(bt_app_rc_ct_cb); 143 | /* initialize AVRCP target */ 144 | assert (esp_avrc_tg_init() == ESP_OK); 145 | esp_avrc_tg_register_callback(bt_app_rc_tg_cb); 146 | 147 | esp_avrc_rn_evt_cap_mask_t evt_set = {0}; 148 | esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_SET, &evt_set, ESP_AVRC_RN_VOLUME_CHANGE); 149 | assert(esp_avrc_tg_set_rn_evt_cap(&evt_set) == ESP_OK); 150 | 151 | /* initialize A2DP sink */ 152 | esp_a2d_register_callback(&bt_app_a2d_cb); 153 | esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); 154 | esp_a2d_sink_init(); 155 | 156 | /* set discoverable and connectable mode, wait to be connected */ 157 | esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); 158 | break; 159 | } 160 | default: 161 | ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); 162 | break; 163 | } 164 | } 165 | 166 | 167 | 168 | static void initialise_mdns(void) 169 | { 170 | mdns_init(); 171 | mdns_hostname_set(MDNS_HOST_NAME); 172 | mdns_instance_name_set(MDNS_INSTANCE); 173 | 174 | mdns_txt_item_t serviceTxtData[] = { 175 | {"board", "esp32"}, 176 | {"path", "/"} 177 | }; 178 | 179 | ESP_ERROR_CHECK(mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 180 | sizeof(serviceTxtData) / sizeof(serviceTxtData[0]))); 181 | } 182 | 183 | static esp_err_t init_fs(void) 184 | { 185 | esp_vfs_spiffs_conf_t conf = { 186 | .base_path = WEB_MOUNT_POINT, 187 | .partition_label = NULL, 188 | .max_files = 5, 189 | .format_if_mount_failed = false 190 | }; 191 | esp_err_t ret = esp_vfs_spiffs_register(&conf); 192 | 193 | if (ret != ESP_OK) { 194 | if (ret == ESP_FAIL) { 195 | ESP_LOGE(TAG, "Failed to mount or format filesystem"); 196 | } else if (ret == ESP_ERR_NOT_FOUND) { 197 | ESP_LOGE(TAG, "Failed to find SPIFFS partition"); 198 | } else { 199 | ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); 200 | } 201 | return ESP_FAIL; 202 | } 203 | 204 | size_t total = 0, used = 0; 205 | ret = esp_spiffs_info(NULL, &total, &used); 206 | if (ret != ESP_OK) { 207 | ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret)); 208 | } else { 209 | ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); 210 | } 211 | return ESP_OK; 212 | } 213 | 214 | /* Set HTTP response content type according to file extension */ 215 | static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath) 216 | { 217 | const char *type = "text/plain"; 218 | if (CHECK_FILE_EXTENSION(filepath, ".html")) { 219 | type = "text/html"; 220 | } else if (CHECK_FILE_EXTENSION(filepath, ".js")) { 221 | type = "application/javascript"; 222 | } else if (CHECK_FILE_EXTENSION(filepath, ".css")) { 223 | type = "text/css"; 224 | } else if (CHECK_FILE_EXTENSION(filepath, ".png")) { 225 | type = "image/png"; 226 | } else if (CHECK_FILE_EXTENSION(filepath, ".ico")) { 227 | type = "image/x-icon"; 228 | } else if (CHECK_FILE_EXTENSION(filepath, ".svg")) { 229 | type = "text/xml"; 230 | } 231 | return httpd_resp_set_type(req, type); 232 | } 233 | 234 | /* Send HTTP response with the contents of the requested file */ 235 | static esp_err_t rest_common_get_handler(httpd_req_t *req) 236 | { 237 | char filepath[FILE_PATH_MAX] = WEB_MOUNT_POINT; 238 | 239 | if (strlen(req->uri) > 0 && req->uri[strlen(req->uri) - 1] == '/') 240 | { 241 | strlcat(filepath, "/index.html", sizeof(filepath)); 242 | } 243 | else 244 | { 245 | strlcat(filepath, req->uri, sizeof(filepath)); 246 | } 247 | 248 | int fd = open(filepath, O_RDONLY, 0); 249 | if (fd == -1) { 250 | ESP_LOGE(TAG, "Failed to open file : %s", filepath); 251 | /* Respond with 500 Internal Server Error */ 252 | httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file"); 253 | return ESP_FAIL; 254 | } 255 | 256 | set_content_type_from_file(req, filepath); 257 | 258 | char *chunk = scratch; 259 | memset(scratch, '\0', SCRATCH_BUFSIZE); 260 | ssize_t read_bytes; 261 | do { 262 | /* Read file in chunks into the scratch buffer */ 263 | read_bytes = read(fd, chunk, SCRATCH_BUFSIZE); 264 | if (read_bytes == -1) { 265 | ESP_LOGE(TAG, "Failed to read file : %s", filepath); 266 | } else if (read_bytes > 0) { 267 | /* Send the buffer contents as HTTP response chunk */ 268 | if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) { 269 | close(fd); 270 | ESP_LOGE(TAG, "File sending failed!"); 271 | /* Abort sending file */ 272 | httpd_resp_sendstr_chunk(req, NULL); 273 | /* Respond with 500 Internal Server Error */ 274 | httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file"); 275 | return ESP_FAIL; 276 | } 277 | } 278 | } while (read_bytes > 0); 279 | /* Close file after sending complete */ 280 | close(fd); 281 | ESP_LOGI(TAG, "File sending complete"); 282 | /* Respond with an empty chunk to signal HTTP response completion */ 283 | httpd_resp_send_chunk(req, NULL, 0); 284 | return ESP_OK; 285 | } 286 | 287 | /* Simple handler for on button press handling */ 288 | static esp_err_t click_post_handler(httpd_req_t *req) 289 | { 290 | extern bool temp_count; 291 | 292 | if (req->content_len == 0) 293 | { 294 | httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "invalid json"); 295 | return ESP_OK; 296 | } 297 | char *buffer = malloc(req->content_len) + 1; 298 | memset(buffer, 0, req->content_len + 1); 299 | 300 | httpd_req_recv(req, buffer, req->content_len); 301 | 302 | 303 | cJSON *json = cJSON_Parse(buffer); 304 | // for extracting data 305 | if (json) 306 | { 307 | cJSON *received_message = cJSON_GetObjectItemCaseSensitive(json, "motion"); 308 | if (received_message) 309 | { 310 | 311 | if (temp_count != 0){ 312 | bt_app_task_shut_down(); 313 | bt_i2s_task_shut_down(); 314 | } 315 | temp_count = 1; 316 | 317 | esp_err_t err1 = nvs_flash_init(); 318 | nvs_handle set_str_handle; 319 | err1 = nvs_open("storage", NVS_READWRITE, &set_str_handle); 320 | char *response_string = malloc(strlen(received_message->valuestring) + 200); 321 | sprintf(response_string, "Motion is : %s", received_message->valuestring); 322 | 323 | bt_name_obj.bt_name = received_message->valuestring; 324 | 325 | char *string_buffer = bt_name_obj.bt_name; 326 | 327 | err1 = nvs_set_str(set_str_handle, "string_buffer", (const char*)string_buffer); 328 | err1 = nvs_commit(set_str_handle); 329 | // nvs_close(set_str_handle); 330 | 331 | size_t required_size = 100; 332 | char* bluetooth_name = malloc(required_size); 333 | nvs_get_str(set_str_handle, "string_buffer",bluetooth_name,&required_size); 334 | nvs_close(set_str_handle); 335 | // nvs_get_str(set_str_handle, "string_buffer", bluetooth_name, &required_size); 336 | ESP_LOGE(TAG, "NVS_DATA %s",bluetooth_name); 337 | 338 | ESP_LOGI(TAG, "BT_NAME_obj_passed %s", bt_name_obj.bt_name); 339 | 340 | start_bluetooth(); 341 | cJSON_AddStringToObject(json, "response", response_string); 342 | char *response_payload = cJSON_Print(json); 343 | cJSON_Delete(json); 344 | httpd_resp_set_type(req, "application/json"); 345 | httpd_resp_send(req, response_payload, strlen(response_payload)); 346 | free(response_payload); 347 | return ESP_OK; 348 | } 349 | httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "message missing"); 350 | cJSON_Delete(json); 351 | 352 | return ESP_OK; 353 | } 354 | 355 | httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "invalid json"); 356 | return ESP_OK; 357 | } 358 | 359 | static esp_err_t start_tuning_http_server_private() 360 | { 361 | httpd_handle_t server = NULL; 362 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); 363 | config.uri_match_fn = httpd_uri_match_wildcard; 364 | 365 | ESP_LOGI(TAG, "Starting HTTP Server"); 366 | if (httpd_start(&server, &config) != ESP_OK) 367 | { 368 | ESP_LOGE(TAG, "start server failed"); 369 | return ESP_FAIL; 370 | } 371 | 372 | httpd_uri_t click_post_uri = { 373 | .uri = "/api/motion", 374 | .method = HTTP_POST, 375 | .handler = click_post_handler, 376 | .user_ctx = NULL 377 | }; 378 | 379 | 380 | httpd_uri_t common_get_uri = { 381 | .uri = "/*", 382 | .method = HTTP_GET, 383 | .handler = rest_common_get_handler, 384 | .user_ctx = NULL 385 | }; 386 | if(httpd_register_uri_handler(server, &common_get_uri) != ESP_OK) 387 | { 388 | ESP_LOGE(TAG, "register get uri failed"); 389 | return ESP_FAIL; 390 | } 391 | 392 | if (httpd_register_uri_handler(server, &click_post_uri) != ESP_OK) 393 | { 394 | ESP_LOGE(TAG, "register post uri failed"); 395 | return ESP_FAIL; 396 | } 397 | 398 | return ESP_OK; 399 | } 400 | 401 | 402 | 403 | 404 | 405 | void start_tuning_http_server() 406 | { 407 | 408 | ESP_ERROR_CHECK(nvs_flash_init()); 409 | ESP_ERROR_CHECK(esp_netif_init()); 410 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 411 | initialise_mdns(); 412 | netbiosns_init(); 413 | netbiosns_set_name(MDNS_HOST_NAME); 414 | 415 | connect_to_wifi(); 416 | ESP_ERROR_CHECK(init_fs()); 417 | ESP_ERROR_CHECK(start_tuning_http_server_private()); 418 | 419 | vTaskDelete(NULL); 420 | 421 | } 422 | -------------------------------------------------------------------------------- /main/tuning_http_server.h: -------------------------------------------------------------------------------- 1 | #ifndef TUNING_HTTP_SERVER_H 2 | #define TUNING_HTTP_SERVER_H 3 | 4 | #include 5 | #include 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | #include "driver/gpio.h" 9 | #include "esp_spiffs.h" 10 | #include "nvs_flash.h" 11 | #include "esp_netif.h" 12 | #include "esp_event.h" 13 | #include "esp_log.h" 14 | #include "mdns.h" 15 | #include "lwip/apps/netbiosns.h" 16 | #include "esp_http_server.h" 17 | #include "esp_system.h" 18 | #include "esp_vfs.h" 19 | #include "cJSON.h" 20 | #include "sdkconfig.h" 21 | #include "wifi_handler.h" 22 | 23 | #define MDNS_INSTANCE "walle pid tuning web server" 24 | #define MDNS_HOST_NAME CONFIG_MDNS_HOST_NAME 25 | #define WEB_MOUNT_POINT "/www" 26 | #define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128) 27 | #define SCRATCH_BUFSIZE (10240) 28 | #define CHECK_FILE_EXTENSION(filename, ext) (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0) 29 | 30 | typedef struct bluetooth_name 31 | { 32 | char *bt_name; 33 | } bt_name_t; 34 | 35 | bt_name_t read_bt_name(); 36 | void start_bluetooth(); 37 | 38 | void start_tuning_http_server(); 39 | 40 | #endif -------------------------------------------------------------------------------- /main/wifi_handler.c: -------------------------------------------------------------------------------- 1 | #include "wifi_handler.h" 2 | 3 | static EventGroupHandle_t s_wifi_event_group; 4 | static const char *TAG = "wifi station"; 5 | static int s_retry_num = 0; 6 | 7 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 8 | { 9 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) 10 | { 11 | esp_wifi_connect(); 12 | } 13 | else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) 14 | { 15 | if (s_retry_num < MAXIMUM_RETRY) { 16 | esp_wifi_connect(); 17 | s_retry_num++; 18 | ESP_LOGI(TAG, "retry to connect to the AP"); 19 | } else { 20 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 21 | } 22 | ESP_LOGI(TAG,"connect to the AP fail"); 23 | } 24 | else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 25 | { 26 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 27 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 28 | s_retry_num = 0; 29 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 30 | } 31 | } 32 | 33 | void wifi_init_sta(void) 34 | { 35 | s_wifi_event_group = xEventGroupCreate(); 36 | 37 | ESP_ERROR_CHECK(esp_netif_init()); 38 | 39 | esp_netif_create_default_wifi_sta(); 40 | 41 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 42 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 43 | 44 | esp_event_handler_instance_t instance_any_id; 45 | esp_event_handler_instance_t instance_got_ip; 46 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 47 | ESP_EVENT_ANY_ID, 48 | &event_handler, 49 | NULL, 50 | &instance_any_id)); 51 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 52 | IP_EVENT_STA_GOT_IP, 53 | &event_handler, 54 | NULL, 55 | &instance_got_ip)); 56 | 57 | wifi_config_t wifi_config = { 58 | .sta = { 59 | .ssid = WIFI_SSID, 60 | .password = WIFI_PASS, 61 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 62 | * However these modes are deprecated and not advisable to be used. Incase your Access point 63 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 64 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 65 | 66 | .pmf_cfg = { 67 | .capable = true, 68 | .required = false 69 | }, 70 | }, 71 | }; 72 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 73 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); 74 | ESP_ERROR_CHECK(esp_wifi_start() ); 75 | 76 | ESP_LOGI(TAG, "wifi_init_sta finished."); 77 | 78 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 79 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 80 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 81 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 82 | pdFALSE, 83 | pdFALSE, 84 | portMAX_DELAY); 85 | 86 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 87 | * happened. */ 88 | if (bits & WIFI_CONNECTED_BIT) 89 | { 90 | ESP_LOGI(TAG, "connected to ap SSID: %s", WIFI_SSID); 91 | } 92 | else if (bits & WIFI_FAIL_BIT) 93 | { 94 | ESP_LOGI(TAG, "Failed to connect to SSID: %s", WIFI_SSID); 95 | } 96 | else 97 | { 98 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 99 | } 100 | 101 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 102 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 103 | vEventGroupDelete(s_wifi_event_group); 104 | } 105 | 106 | void connect_to_wifi() 107 | { 108 | esp_err_t ret = nvs_flash_init(); 109 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) 110 | { 111 | ESP_ERROR_CHECK(nvs_flash_erase()); 112 | ret = nvs_flash_init(); 113 | } 114 | ESP_ERROR_CHECK(ret); 115 | 116 | ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); 117 | wifi_init_sta(); 118 | } 119 | -------------------------------------------------------------------------------- /main/wifi_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFI_HANDLER_H 2 | #define WIFI_HANDLER_H 3 | 4 | #include 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | #include "freertos/event_groups.h" 8 | #include "esp_system.h" 9 | #include "esp_wifi.h" 10 | #include "esp_event.h" 11 | #include "esp_log.h" 12 | #include "nvs_flash.h" 13 | #include "lwip/err.h" 14 | #include "lwip/sys.h" 15 | #include "sdkconfig.h" 16 | 17 | #define WIFI_CONNECTED_BIT BIT0 18 | #define WIFI_FAIL_BIT BIT1 19 | 20 | #define WIFI_SSID CONFIG_WIFI_SSID 21 | #define WIFI_PASS CONFIG_WIFI_PASSWORD 22 | #define MAXIMUM_RETRY CONFIG_MAXIMUM_RETRY 23 | 24 | void connect_to_wifi(); 25 | 26 | #endif -------------------------------------------------------------------------------- /partition_table.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap 3 | nvs, data, nvs, 0x9000, 0x6000, 4 | phy_init, data, phy, 0xf000, 0x1000, 5 | factory, app, factory, 0x10000, 2M, 6 | www, data, spiffs, , 100K, 7 | -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # Override some defaults so BT stack is enabled and 2 | # Classic BT is enabled and BT_DRAM_RELEASE is disabled 3 | CONFIG_BT_ENABLED=y 4 | CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n 5 | CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y 6 | CONFIG_BTDM_CTRL_MODE_BTDM=n 7 | CONFIG_BT_BLUEDROID_ENABLED=y 8 | CONFIG_BT_CLASSIC_ENABLED=y 9 | CONFIG_BT_A2DP_ENABLE=y 10 | CONFIG_BT_SPP_ENABLED=n 11 | CONFIG_BT_BLE_ENABLED=n 12 | CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 13 | CONFIG_SPIFFS_OBJ_NAME_LEN=64 14 | CONFIG_FATFS_LONG_FILENAME=y 15 | CONFIG_FATFS_LFN_HEAP=y 16 | CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y 17 | CONFIG_PARTITION_TABLE_CUSTOM=y 18 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table.csv" 19 | CONFIG_PARTITION_TABLE_FILENAME="partition_table.csv" 20 | --------------------------------------------------------------------------------