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