Compare commits

...

358 Commits

Author SHA1 Message Date
Philippe G
c36348e99d limit sockets used by HTTP server
seems that ctrl and msg sockets are not needed
2021-11-15 10:59:06 -08:00
Philippe G
faa9976d3d switching or losing server connection was exhausting sockets - release 2021-11-13 19:17:50 -08:00
Philippe G
1dbffe6753 no EXTRAM attribute + cosmetics - release 2021-11-13 16:43:51 -08:00
Philippe G
8e5dbd2144 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-11-03 22:00:16 -07:00
Philippe G
974ff5fa68 strncpy is not safe + memory optimization 2021-11-03 22:00:07 -07:00
philippe44
4dce4b307b Update README.md 2021-10-31 20:27:30 -07:00
philippe44
4aa54537e5 Update README.md 2021-10-31 20:10:31 -07:00
philippe44
d27a430664 Update README.md 2021-10-31 20:05:23 -07:00
philippe44
0fa41e5ef7 Update README.md 2021-10-31 20:03:06 -07:00
philippe44
2146014f04 Update README.md 2021-10-31 18:26:43 -07:00
philippe44
7d409668c8 Update README.md 2021-10-31 18:24:23 -07:00
Philippe G
0ebce7472b Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-10-31 16:13:55 -07:00
Philippe G
5f5466fcb0 add "MISO" to system's spi_config 2021-10-31 16:13:42 -07:00
philippe44
71911914cb Update README.md 2021-10-31 15:59:00 -07:00
Philippe G
4f6dcc2cc7 add SPI ethernet 2021-10-31 14:45:35 -07:00
philippe44
f4615462c7 Update README.md 2021-10-30 18:34:56 -07:00
philippe44
6033c7fc14 Update README.md 2021-10-30 18:25:22 -07:00
Philippe G
a98b1d00b0 Ethernet + AirPlay fixes 2021-10-30 17:51:22 -07:00
philippe44
1e45348e4a Update README.md 2021-10-24 11:15:06 -07:00
Philippe G
96a05d8a6b fix opus error -136 (increase pseudo-stack) - release 2021-10-24 11:12:28 -07:00
philippe44
7a9f3e0781 Update README.md 2021-10-24 00:00:43 -07:00
Philippe G
3575245324 extra callback in preset caused NULL call - release 2021-10-20 21:24:36 -07:00
Philippe G
c36d663a7f release 2021-10-07 11:23:17 -07:00
Philippe G
13294ddd0c Continue searching for STA in AP mode when SSID has been set 2021-10-05 12:22:46 -07:00
philippe44
449ef32a4e Update README.md 2021-10-02 12:46:52 -07:00
Philippe G
50390dbc61 Fix i2s mode for 32 bits & SPDIF - release 2021-09-24 18:19:50 -07:00
Philippe G
fc5f3f5ac9 remove extra UNLOCK_S in opus - release 2021-09-18 09:30:03 -07:00
Philippe G
6f4ed0679e fix 32 bits sample size L/R swap - release
issue is in esp-idf
2021-09-11 21:40:10 -07:00
Philippe G
511df0b4b8 release 2021-08-19 19:13:35 -07:00
Philippe G
6682ee947e Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-08-19 19:12:09 -07:00
Philippe G
813e7ee712 use polling for gpio 36 & 39 - release 2021-08-19 19:11:39 -07:00
philippe44
6c9537e259 Update README.md 2021-08-19 19:10:39 -07:00
Philippe G
ac9ad2eee2 send ANIC when grfe's transitiion is not 'c' - release 2021-08-14 11:39:24 -07:00
Philippe G
f38840bbe0 release 2021-08-09 18:02:57 -07:00
Philippe G
fd56f649ab Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-08-09 18:01:53 -07:00
Philippe G
c4bd320afe close memory stream before returning on parsing error - release 2021-08-09 18:01:48 -07:00
philippe44
5e8978d1af Update README.md 2021-08-06 09:34:52 -07:00
philippe44
6e082a5654 Update README.md 2021-08-04 13:28:15 -07:00
philippe44
5af3250aea Update README.md 2021-08-04 13:27:21 -07:00
Philippe G
527187b2f8 release 2021-08-03 22:04:29 -07:00
Philippe G
102d2f6af5 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-08-03 22:02:55 -07:00
philippe44
4edd429b0a Update README.md 2021-08-03 21:59:31 -07:00
Philippe G
35099d1131 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-08-03 21:51:36 -07:00
Philippe G
ce9c3952e8 clean A1S, add ES8388 and generic codecs, add MCLK, clean Kconfig - release 2021-08-03 21:51:33 -07:00
philippe44
4ee6352185 Update README.md 2021-08-01 22:39:22 -07:00
philippe44
3a720a1e7d Update README.md 2021-07-31 00:32:03 -07:00
Philippe G
987fa5d18c plugin clarification 2021-07-29 07:50:59 -07:00
Philippe G
23c936ec93 release 2021-07-24 22:31:58 -07:00
Philippe G
d4f10a761e Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-07-24 22:29:08 -07:00
Philippe G
d68d163538 fix bits_per_sample for 32 bit + resilient cli_socket handling - release 2021-07-24 22:29:04 -07:00
philippe44
94580c6771 Update README.md 2021-07-21 20:04:33 -07:00
philippe44
2717277c6e Update README.md 2021-07-21 20:03:59 -07:00
philippe44
b6d537a207 Stop building A1S
As there are multiple version os ESP32-A1S, it should be set by dac_config now
2021-07-21 19:54:12 -07:00
Sebastien
cf047c8098 Fix WiFi signal strength icons - release 2021-07-09 11:18:01 -04:00
Philippe G
21f3768ada telnet / escape - release 2021-07-04 11:53:26 -07:00
Philippe G
9cb18fa980 look for eol in telnet - release 2021-07-03 21:32:16 -07:00
Philippe G
d4f6289500 wait for BT disable to avoid crash - release 2021-07-01 13:27:55 -07:00
Philippe G
21c3ce1fba damn release 2021-07-01 01:23:42 -07:00
Philippe G
48e8525ba9 make equalizer a string + make sure output-i2s is exited
- Can't really use BLOB (creates issue with HTTP visualizer)
- Player was stuck after WiFi loss b/c with some race conditions, BT deinit crashes and creates the reboot wanted after 5*5 failures. But when BT does not crash, reboot was not happening and player was stuck with slimproto not exited and player not rebooted
2021-07-01 01:20:59 -07:00
Philippe G
0db9631700 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-06-29 12:51:39 -07:00
Philippe G
192cb975e2 store equalizer - release 2021-06-29 12:51:36 -07:00
philippe44
d1f6085199 Update README.md 2021-06-24 15:20:54 -07:00
Philippe G
afaa5323d7 fix README 2021-06-22 21:23:57 -07:00
philippe44
f92447e9b9 Merge pull request #110 from wizmo2/controls_fix
fix audio control in bt and airplay
2021-06-22 21:21:40 -07:00
wizmo2
22f8d1d88b Update bt_app_sink.c 2021-06-17 14:48:58 -04:00
wizmo2
8136b7fd9a Fix indent 2021-06-17 14:47:33 -04:00
Wizmo2
e588deb3af fix audio control in bt and airplay 2021-06-16 21:53:44 -04:00
Christian Herzog
38ec8ac6f8 fix A1S ali link
fixes https://github.com/sle118/squeezelite-esp32/issues/104
2021-06-07 09:11:03 +02:00
Philippe G
791167f794 amp gpio polarity parse error - release 2021-05-24 10:38:33 -07:00
Philippe G
c9b859ef8c use visualizer for BT and AirPlay, regardless of buffer fullness 2021-05-23 11:26:08 -07:00
Philippe G
db74419bd7 correct threshold 2021-05-18 17:25:22 -07:00
philippe44
265403d364 Merge pull request #102 from wizmo2/battery_atten
Add Attenuation parameter to battery service
2021-05-18 17:22:30 -07:00
wizmo2
d3dd8b9078 Update battery.c 2021-05-13 20:00:02 -04:00
Wizmo2
de41fb9583 update ReadMe 2021-05-13 19:56:00 -04:00
Wizmo2
2699216d25 Add atten to battery service 2021-05-13 19:53:55 -04:00
philippe44
c7b1c7cd83 Update README.md 2021-05-11 07:47:58 -07:00
philippe44
f51af21b90 Update README.md 2021-05-11 07:43:21 -07:00
philippe44
5ec63f400c reverse SBR logic 2021-05-11 07:18:34 -07:00
Philippe G
1753e11698 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-05-11 07:16:39 -07:00
Philippe G
58c6ca059b reverse SBR enablement logic 2021-05-11 07:16:36 -07:00
philippe44
5ef63dc3e4 enable AAC SBR - release 2021-05-10 21:33:57 -07:00
philippe44
468c847499 SBR still enabled in default build - release 2021-05-10 21:29:55 -07:00
Philippe G
8c0e766cd7 optimizations - release 2021-05-10 19:41:56 -07:00
Philippe G
0f792d71ee optimization for AAC-SBR 2021-05-09 23:17:51 -07:00
Philippe G
1fc7675c14 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-05-08 23:39:35 -07:00
Philippe G
7b439ae6ee For AAC, do not enable SBR by default 2021-05-08 23:39:30 -07:00
philippe44
c5d7fd521d Update README.md 2021-05-07 14:14:06 -07:00
philippe44
a856b26181 Update README.md 2021-05-07 14:13:29 -07:00
philippe44
a949ec2d24 Update README.md 2021-05-07 14:12:23 -07:00
philippe44
5ec5236991 Update README.md 2021-04-30 11:45:18 -07:00
Philippe G
d4cd400cd9 refactor display(er) to welcome led extension 2021-04-28 18:03:27 -07:00
Philippe G
64bb5f018b Windows CRLF! 2021-04-28 18:00:05 -07:00
Philippe G
593927aac3 plugin update now on cmake (except repo.xml) 2021-04-28 17:29:28 -07:00
philippe44
51761d0890 Merge pull request #96 from michaelherger/firmware-proxy
Don't filter by HTTP verb - older firmwares are using GET rather than HEAD
2021-04-26 22:34:47 -07:00
Michael Herger
5c56abfe75 Don't filter by HTTP verb - older firmwares are using GET rather than HEAD 2021-04-27 07:31:24 +02:00
philippe44
9ac7c5bbeb Merge pull request #95 from michaelherger/firmware-proxy
Fix backwards compatibility with "older" firmwares
2021-04-26 22:09:05 -07:00
Michael Herger
be28555a40 Fix backwards compatibility with "older" firmwares checking for the -99 magic number, rather than check.bin 2021-04-27 07:06:47 +02:00
Philippe G
1d32479bc4 less verbose 2021-04-25 22:29:41 -07:00
philippe44
c83ddc4adc Merge pull request #90 from michaelherger/firmware-proxy
Firmware proxy
2021-04-25 22:12:15 -07:00
Michael Herger
5a7cf9b8fe Merge commit '387276f2f33a0fb9dde01434387aac9cdc9a8472' into firmware-proxy
# Conflicts:
#	components/wifi-manager/webapp/webapp.cmake
#	components/wifi-manager/webapp/webpack.c
#	components/wifi-manager/webapp/webpack.h
#	components/wifi-manager/webapp/webpack/dist/index.html
#	components/wifi-manager/webapp/webpack/dist/index.html.br
#	components/wifi-manager/webapp/webpack/dist/index.html.gz
#	components/wifi-manager/webapp/webpack/dist/js/index.18c3b7.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/index.abeafc.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/index.cf3fe8.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/node-modules.18c3b7.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/node-modules.abeafc.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/node-modules.cf3fe8.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/node-modules.cf3fe8.bundle.js.br
#	components/wifi-manager/webapp/webpack/dist/js/node-modules.cf3fe8.bundle.js.gz
#	components/wifi-manager/webapp/webpack/dist/js/runtime.18c3b7.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/runtime.18c3b7.bundle.js.br
#	components/wifi-manager/webapp/webpack/dist/js/runtime.18c3b7.bundle.js.gz
#	components/wifi-manager/webapp/webpack/dist/js/runtime.abeafc.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/runtime.abeafc.bundle.js.br
#	components/wifi-manager/webapp/webpack/dist/js/runtime.abeafc.bundle.js.gz
#	components/wifi-manager/webapp/webpack/dist/js/runtime.cf3fe8.bundle.js
#	components/wifi-manager/webapp/webpack/dist/js/runtime.cf3fe8.bundle.js.br
#	components/wifi-manager/webapp/webpack/dist/js/runtime.cf3fe8.bundle.js.gz
2021-04-26 07:04:53 +02:00
Michael Herger
190326726c Add firmware upload handler to SqueezeESP32 plugin
* upload firmware image: `curl -vF 'data=@./someFirmwareImage.bin' http://localhost:9000/plugins/SqueezeESP32/firmware/upload` (or the JS equivalent, using multipart form data
* receive response: `{"url":"http://192.168.0.63:9000/plugins/SqueezeESP32/firmware/squeezelite-esp32-upload-b0w7mn.bin", "size":2463375}`
* install firmware from temporary URL returned
* uploads are removed after 15 minutes or upon LMS restart
2021-04-25 01:26:10 +02:00
Sébastien
387276f2f3 adjust artifact prefix 2021-04-21 14:42:54 -04:00
Sébastien
87b8c46263 drum roll. V1 release 2021-04-21 14:06:16 -04:00
Sebastien
d7aef11856 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2021-04-21 14:01:27 -04:00
Sebastien
be842d235d update to version 1 2021-04-21 14:01:23 -04:00
Philippe G
d49ad66177 version 1 2021-04-21 10:55:46 -07:00
Sebastien
ca28669b0a Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2021-04-21 13:27:36 -04:00
Sebastien
45f480d948 NVS ui fine-tuning, site icon now works 2021-04-21 13:27:32 -04:00
Philippe G
852e312879 Correct AAC handling of different AOT 2021-04-20 19:38:01 -07:00
Philippe G
c79cf5b58f more AAC SBR combinations 2021-04-19 22:47:35 -07:00
Michael Herger
1a4a8ba559 Allow firmware installation from LMS' player settings page 2021-04-17 19:09:31 +02:00
Michael Herger
7ad39a02f5 Extend firmware download handler to serve locally built custom firmware, too.
Just save it as `squeezelite-esp32-custom.bin` in the firmware update folder (LMS Cache/updates) and paste http://yourlms:9000/plugins/SqueezeESP32/firmware/custom.bin in the firmware URL box.
2021-04-17 07:19:04 +02:00
Michael Herger
f96d06912f Fix LMS plugin availability check. As I removed the download by ID, this needs a tweak on the UI/JS side. 2021-04-17 06:32:09 +02:00
Michael Herger
36571d3dad Improve firmware download proxy
* initialize firmware pre-fetching when a player connects
* get firmware based on the player's version string as returned by `status.json`
* keep firmware file per platform/branch/resolution combination to support different squeezelite-ESP32 players in an installation
* remove handler to get firmware by numeric ID rather than filename
2021-04-17 06:27:25 +02:00
Michael Herger
b075bbaea3 Add link to the ESP32 WiFi Manager to the settings page in LMS. 2021-04-17 06:27:25 +02:00
Philippe G
ec7982dbfc Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-04-16 11:44:44 -07:00
Philippe G
afd76bdfbb do not fail when external dac has no I2C! 2021-04-16 11:43:02 -07:00
philippe44
89ecd19d32 Update README.md 2021-04-15 15:56:34 -07:00
Philippe G
8904ec1afd remove extra bt_sink NVS initialization 2021-04-14 23:56:25 -07:00
Sebastien
5120029643 Firmware update improvements - include webpack build files 2021-04-14 21:09:45 -04:00
Sebastien
4a529d6fbd Firmware update UI revamp with support for local proxy 2021-04-14 18:16:18 -04:00
Sebastien
afe697e4b1 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2021-04-14 18:15:38 -04:00
Philippe G
879b1f9107 handle abs & local BT volume - release 2021-04-13 18:04:23 -07:00
Sebastien
5a7d4fd535 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2021-04-12 12:12:00 -04:00
Sebastien
d61c650f39 UI change of the update mechanism 2021-04-12 12:11:54 -04:00
Philippe G
f096ee269e clean TAS57xx - release 2021-04-10 12:44:04 -07:00
Philippe G
0610e1a2bc include wm8978 - release 2021-04-10 11:57:05 -07:00
philippe44
68db286777 Merge pull request #89 from wizmo2/wm8978
Add support for WM8978 i2c dac
2021-04-10 11:50:59 -07:00
Wizmo2
5075878f05 Updates to driver for rebase 2021-04-10 08:59:45 -04:00
Wizmo2
d965187d2c Merge branch 'wm8978' of https://github.com/wizmo2/squeezelite-esp32 into wm8978 2021-04-10 08:44:49 -04:00
wizmo2
e25b098678 Update wm8978.c 2021-04-10 08:44:13 -04:00
Wizmo2
a3b23bffc2 Config changes for WM8978 support 2021-04-10 08:44:13 -04:00
Wizmo2
dbc7a6b14e Add support for WM8978 i2c dac 2021-04-10 08:44:13 -04:00
Sebastien
a105f7fd99 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2021-04-09 13:45:59 -04:00
Philippe G
1b39a4f7c9 DAC refactoring 2021-04-08 21:37:24 -07:00
Philippe G
cac6306a04 activate SBR mode in AAC 2021-04-07 00:45:21 -07:00
Wizmo2
749d71a36f Merge branch 'wm8978' of https://github.com/wizmo2/squeezelite-esp32 into wm8978 2021-04-06 11:15:45 -04:00
wizmo2
4d40355d5c Update wm8978.c 2021-04-06 11:14:21 -04:00
Wizmo2
c311faa90f Config changes for WM8978 support 2021-04-05 21:39:16 -04:00
Wizmo2
0821551a2f Add support for WM8978 i2c dac 2021-04-05 14:34:38 -04:00
Philippe G
3a2bfe470f show absolute battery level 2021-04-04 16:06:31 -07:00
Philippe G
f6b55c5ac9 voltage 2021-04-04 15:45:22 -07:00
Michael Herger
f9e97036cf Firmware proxy (#88)
* Add support for a firmware download proxy. This should help in situations where the player's firmware can't handle https correctly.

Two possibilities:
* full path to image: http://yourlms:9000/plugins/SqueezeESP32/firmware/ESP32-A1S.32.634.master-cmake/squeezelite-esp32-master-cmake-ESP32-A1S-32-V0.634.bin
* use Github's asset ID: http://yourlms:9000/plugins/SqueezeESP32/firmware/34298863

The former is more prone to issues related to the path. A change in the schema could break the matching regex.
The latter is simpler to use if you know the ID. But the ID is not easily available to the user. And it requires one more lookup in the plugin to get from the ID to the download path.

* Add support for proxying firmware downloads through LMS

* add magic asset ID -99 to allow the front-end to check whether the plugin does support download proxying
* web manager is expecting `lms_port` and `lms_ip` in `status.json`. If that's available, check whether plugin does support firmware downloading. If that's the case, download firmwares through LMS
* plugin would cache firmware images. In case of multiple images the file would be served directly from LMS.

* Add firmware pre-caching

* keep track of the most recently requested firmware build type
* poll Github for releases every ~6h
* download new firmware file for the same player model used before

Factor out firmware handling code to its own module.

Co-authored-by: Michael Herger <michael@herger.net>
2021-04-04 13:02:12 -04:00
Sebastien
4444fed343 Merge with LMS OTA proxy 2021-04-03 21:16:00 -04:00
Michael Herger
bc0d104290 Add support for a firmware download proxy (#85)
* Add support for a firmware download proxy. This should help in situations where the player's firmware can't handle https correctly.

Two possibilities:
* full path to image: http://yourlms:9000/plugins/SqueezeESP32/firmware/ESP32-A1S.32.634.master-cmake/squeezelite-esp32-master-cmake-ESP32-A1S-32-V0.634.bin
* use Github's asset ID: http://yourlms:9000/plugins/SqueezeESP32/firmware/34298863

The former is more prone to issues related to the path. A change in the schema could break the matching regex.
The latter is simpler to use if you know the ID. But the ID is not easily available to the user. And it requires one more lookup in the plugin to get from the ID to the download path.

* Add support for proxying firmware downloads through LMS

* add magic asset ID -99 to allow the front-end to check whether the plugin does support download proxying
* web manager is expecting `lms_port` and `lms_ip` in `status.json`. If that's available, check whether plugin does support firmware downloading. If that's the case, download firmwares through LMS
* plugin would cache firmware images. In case of multiple images the file would be served directly from LMS.

Co-authored-by: Michael Herger <michael@herger.net>
2021-04-03 21:01:40 -04:00
Philippe G
bdec9d25c6 change file permission 2021-04-03 15:14:46 -07:00
Philippe G
b25367fc0c chmod +x 2021-04-03 15:05:57 -07:00
Philippe G
603296d921 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-04-03 15:01:04 -07:00
Philippe G
a5b7d24dca chmod 2021-04-03 15:00:03 -07:00
philippe44
2733e3ec23 Merge pull request #86 from Mum-Pf/MumPf
Update README.md - Thanks!
2021-04-03 14:56:26 -07:00
Mum-Pf
9c79888b72 Update README.md
typo
2021-04-03 21:13:34 +02:00
Sebastien
2b59c38b4b Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2021-04-03 11:11:10 -04:00
Mum-Pf
a13afd76c9 Update README.md
NVS parameter for ILI9341
2021-04-02 20:18:39 +02:00
Mum-Pf
b98a481858 Update README.md
Typo... IL9341 -> ILI9341
2021-04-02 17:32:31 +02:00
philippe44
9e27a0e21d Update README.md 2021-04-01 19:20:55 -07:00
philippe44
d08f7142ae Update README.md 2021-04-01 19:20:12 -07:00
philippe44
efa3f1f07d Update README.md 2021-04-01 19:19:22 -07:00
philippe44
263679dcac Update README.md 2021-04-01 19:17:52 -07:00
Philippe G
50d7d57f48 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-03-31 22:38:00 -07:00
Philippe G
99e4b107d6 cleanup these $%@! certificates 2021-03-31 22:37:44 -07:00
Philippe G
d293de4b64 cleanup these $%@! certificates 2021-03-31 22:35:41 -07:00
philippe44
554cf89ac2 keep these $%*^¨&@ certificates 2021-03-31 22:27:03 -07:00
Philippe G
df36b65916 release 2021-03-31 21:25:30 -07:00
philippe44
d9a6b37d19 Merge pull request #68 from hubertbanas/master-cmake
Add body padding-bottom - release
2021-03-31 19:32:34 -07:00
Philippe G
0629b017b1 LMS can set player's name (only LMS scope) - release 2021-03-31 19:24:34 -07:00
Philippe G
43aa62ac56 set DEPTH in root CMake for consistency 2021-03-28 14:59:07 -07:00
Philippe G
22c2044f17 Limit rate to 96kHz in 32 bits mode + CMakeLists correction
@sle118, le tme know if the CMakeLists works for you as well. I pushed this one as I was pushing other stuff anyway
2021-03-28 13:54:45 -07:00
Sebastien
7457632990 Auto stash before merge of "master-cmake" and "origin/master-cmake" 2021-03-25 10:15:37 -04:00
philippe44
361cc08e3c release 2021-03-22 22:40:21 -07:00
Philippe G
e742905fbd release 2021-03-22 22:28:57 -07:00
Philippe G
4b719deddf Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-03-22 22:16:30 -07:00
Philippe G
b57e79ca5f need 'y' to 'Y' in sdkconfig!!!! - release 2021-03-22 22:16:27 -07:00
philippe44
6ef4c78b3b Update CrossBuild.yml 2021-03-22 22:16:02 -07:00
philippe44
f0002293a0 Update CrossBuild.yml 2021-03-22 21:51:30 -07:00
philippe44
29997c40b2 Update CrossBuild.yml 2021-03-22 21:49:52 -07:00
philippe44
ac5d54e6c1 Update CrossBuild.yml 2021-03-22 21:47:26 -07:00
philippe44
b3eae8dad1 Update CrossBuild.yml 2021-03-22 21:15:29 -07:00
philippe44
6804e81249 Update CrossBuild.yml 2021-03-22 21:12:23 -07:00
Philippe G
8639566909 update certificates for repository 2021-03-22 18:33:57 -07:00
Philippe G
87bf6255f4 tweak priorities - release 2021-03-22 18:07:09 -07:00
Philippe G
6084af8fbf optimize for 24/86/SPDIF + tweak stacks - release 2021-03-22 11:12:31 -07:00
Philippe G
8ec124c47c Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-03-21 22:22:10 -07:00
Philippe G
b8bb881820 re-activate RESAMPLE16 - release 2021-03-21 22:22:06 -07:00
philippe44
ccb4842e13 Update README.md 2021-03-21 13:35:40 -07:00
philippe44
3a7addad2e Update README.md 2021-03-21 13:16:26 -07:00
philippe44
644f4eb1e6 Update README.md 2021-03-21 13:15:15 -07:00
philippe44
00bab8f76b Update README.md 2021-03-21 13:13:43 -07:00
philippe44
72c084d7c0 Update README.md 2021-03-21 12:27:28 -07:00
philippe44
12e7d2d8fb Update README.md 2021-03-21 12:26:28 -07:00
philippe44
f5bb058541 Clean README 2021-03-21 12:20:31 -07:00
philippe44
5871252869 Update README.md 2021-03-20 19:21:38 -07:00
Philippe G
d5bf498d3d support SPDIF @ 96kHz - release 2021-03-20 19:08:42 -07:00
Philippe G
eb647aeea3 flags - release 2021-03-17 21:13:22 -07:00
Philippe G
451f187856 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-03-17 20:05:00 -07:00
Philippe G
f79c7d4ace mono channels with inversion - release 2021-03-17 20:04:53 -07:00
Sébastien
01a44be0ca Update certificates - release 2021-03-16 18:03:38 -04:00
Sébastien
4dc6424fed Add github-releases to TLS certs 2021-03-16 17:26:34 -04:00
Philippe G
a989fe06c2 add version - release 2021-03-16 13:37:43 -07:00
philippe44
1b5a877b98 Update CrossBuild.yml 2021-03-16 13:36:33 -07:00
sle118
9c9f79b0b6 Made certificate script an executable for github 2021-03-16 14:33:19 -04:00
Sébastien
6fef6d679e Update CrossBuild.yml 2021-03-16 14:26:40 -04:00
Sébastien
aa54b9dff9 Update CrossBuild.yml 2021-03-16 14:19:05 -04:00
Sébastien
05a704e7ec Update CrossBuild.yml 2021-03-16 14:16:44 -04:00
Sébastien
09e6518870 Update CrossBuild.yml 2021-03-16 14:02:54 -04:00
Sébastien
4d70d0998c Update certificates during build 2021-03-16 13:42:07 -04:00
Sébastien
9d0d957ec3 Update Certificates during build 2021-03-16 13:37:04 -04:00
philippe44
1be4c89f3c release 2021-03-15 23:20:08 -07:00
philippe44
893e67dfa4 release 2021-03-15 23:10:11 -07:00
Philippe G
eced71b2be release 2021-03-14 17:07:39 -07:00
Philippe G
1aa24393e4 release 2021-03-14 17:03:26 -07:00
philippe44
06b0d9aa3e release 2021-03-14 16:49:34 -07:00
Philippe G
6d73ba2d96 release 2021-03-14 16:28:07 -07:00
Philippe G
43785f1a7d Revert "release"
This reverts commit f772b3d07b.
2021-03-14 16:27:41 -07:00
Philippe G
f80e923c2a Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-03-14 16:27:31 -07:00
Philippe G
f772b3d07b release 2021-03-14 16:27:05 -07:00
philippe44
a6ae5d795a Update CrossBuild.yml 2021-03-14 16:23:14 -07:00
philippe44
c08286874a Update CrossBuild.yml 2021-03-14 16:22:13 -07:00
Philippe G
7b10bee68a release 2021-03-14 15:37:11 -07:00
Philippe G
d42098d1c0 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-03-14 14:44:36 -07:00
Philippe G
9563f1ed44 add 32 bits edition - release 2021-03-14 14:42:25 -07:00
philippe44
1f6b24db33 Update CrossBuild.yml 2021-03-14 14:31:15 -07:00
philippe44
5f3a22bb63 Update CrossBuild.yml 2021-03-14 14:26:09 -07:00
philippe44
574c7706a0 Update CrossBuild.yml 2021-03-14 14:18:16 -07:00
Philippe G
46a43efd70 opus & vorbis 32 bits fix 2021-03-11 17:44:23 -08:00
Philippe G
e8bba8af24 AAC 32 bits mode correction 2021-03-11 12:59:34 -08:00
Philippe G
b9466bf7b2 BT non-absolute volume handling 2021-03-07 16:33:26 -08:00
Philippe G
b0d8401274 32 bits mode for AirPlay 2021-03-06 23:04:05 -08:00
Philippe G
1063cd5899 allow -Z even w/o RESAMPLE (32 bits mode) 2021-03-06 14:02:42 -08:00
Philippe G
8f4b1022e2 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-03-05 22:57:35 -08:00
Philippe G
6d45f6d1b6 Tweak 32 bits mode (enable 20 bits spdif) 2021-03-05 22:13:20 -08:00
philippe44
6beafaa747 More bit depth comments 2021-03-05 22:07:37 -08:00
philippe44
fe80ee3c18 Update README.md 2021-03-05 21:59:51 -08:00
philippe44
a5d6600dea Update README.md 2021-03-05 21:59:22 -08:00
philippe44
8132311c75 Update README.md 2021-03-05 21:58:51 -08:00
philippe44
9ecd7e9a79 Bits depth clarification 2021-03-05 09:16:50 -08:00
Philippe G
b3ff717d32 32 bits cleanup 2021-03-04 20:30:06 -08:00
Philippe G
15f1cebcdb ignore 2021-03-04 20:27:16 -08:00
Philippe G
6d9eaf4109 add balance option - release 2021-02-25 06:10:03 -08:00
Philippe G
3f0882ead6 combined channels - release 2021-02-20 17:34:25 -08:00
Philippe G
9717f8288e preset fix - release 2021-02-12 15:13:33 -08:00
Philippe G
d253bc34e5 ILI9341 driver - release
credits Mumpf and Harry1999
2021-02-11 00:28:13 -08:00
philippe44
0f56e43451 Merge pull request #78 from Mum-Pf/MumPf
New driver for ILI9341-OLED
2021-02-11 00:22:14 -08:00
Philippe G
ed00f029a6 add preset buttons (use macro) + change name of MONO channel pseudo-gain - release 2021-02-10 23:52:53 -08:00
philippe44
ee35e1dc99 Merge pull request #64 from MatthiasLienhard/master-cmake
added power and preset buttons
2021-02-10 23:05:01 -08:00
Mum-Pf
84881ecb45 New driver for ILI9341-OLED
New driver for ILI9341-OLED-colordisplay, with rotation, flip, colordepth, gammacorrection.
Tested with 2.8inch OLED-Display ILI9241
2021-02-09 14:19:55 +01:00
Philippe G
369a9cb9bc add mono channel option - release 2021-02-06 18:02:33 -08:00
philippe44
b9deead084 Update README.md 2021-01-31 15:13:39 -08:00
philippe44
723e7442af Update README.md 2021-01-31 15:07:09 -08:00
Christian Herzog
8189a59d59 typo 2021-01-25 08:24:09 +01:00
philippe44
cc209be4f9 Update README.md 2021-01-24 00:31:08 -08:00
philippe44
1d9e8e863c Update README.md 2021-01-24 00:29:50 -08:00
philippe44
c37fd57b2c Update README.md 2021-01-24 00:29:19 -08:00
philippe44
54420387ab Update README.md 2021-01-24 00:28:56 -08:00
philippe44
173b2d13da Update README.md 2021-01-24 00:18:16 -08:00
philippe44
d60506c63f Update README.md 2021-01-23 23:52:37 -08:00
philippe44
2d80c44181 Update README.md 2021-01-23 23:51:18 -08:00
Philippe G
eb5df86733 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-01-18 22:55:30 -08:00
Philippe G
096e1d636d SSD1322 enhancement - release
at the expense of power but needed for 5.5' displays
2021-01-18 22:49:27 -08:00
philippe44
434836782c Update README.md 2021-01-16 12:02:03 -08:00
Philippe G
3b9e50ada7 alac corrected - release 2021-01-11 19:24:52 -08:00
Philippe G
78a16e41dc simplify alac writebuf alloc 2021-01-10 21:57:22 -08:00
Philippe G
2bc4a8c807 more alac fixes 2021-01-10 16:14:38 -08:00
Hubert Banas
5066351b24 Add body padding-bottom 2021-01-10 09:41:03 -05:00
Philippe G
c521fba4a6 pcm remaining bytes guardrail 2021-01-10 02:23:23 -08:00
Philippe G
174942f509 better alac management 2021-01-10 02:13:46 -08:00
Philippe G
9bf7b250e0 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-01-09 23:51:58 -08:00
Philippe G
32c2a4d68a 24àx320 VU-display fixes + alac decoder code style 2021-01-09 23:51:52 -08:00
Sebastien
64f96c68dc Update esp-idf docker image to latest v4.0 release 2021-01-09 16:54:34 -05:00
Philippe G
9807bf5476 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2021-01-08 15:05:52 -08:00
Philippe G
5a630887c4 IP reassembly flags consistents with new CONFIG 2021-01-08 15:03:01 -08:00
Sebastien
2a8fb902da set LWIP reassembly flag - release 2021-01-08 15:09:56 -05:00
Sebastien
08fcf84d6c Attempt to resolve connection dropping randomly - release 2021-01-08 14:45:42 -05:00
Sébastien
d162996042 add full build output to artifacts 2021-01-01 12:46:01 -05:00
Philippe G
7bd8c842c8 really triggering a new build - release 2020-12-28 19:17:37 -08:00
MatthiasLienard
fd36f6fc71 restored sdkconfig 2020-12-27 00:02:40 +01:00
Philippe G
fd9158e7c1 no need for aac protection - release
issue was (my bad) in LMS
2020-12-23 16:23:19 -08:00
Philippe G
effd24ccd4 aac was more complicated - best is to ignore last 128 bytes - release 2020-12-23 02:16:29 -08:00
Philippe G
1c730905ec mp4/aac decoder might overflow and lock at the end of track - release 2020-12-23 00:55:21 -08:00
Sebastien
9a8991ebf2 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-12-21 12:12:04 -05:00
Sebastien
014030513a Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-12-21 12:11:56 -05:00
Sebastien
bfb85271d4 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-12-21 11:09:02 -05:00
Sebastien
7c13c130b8 Wifi UI update 2020-12-21 11:01:22 -05:00
Matthias Lienhard
347a795b5f added power and preset buttons 2020-12-10 01:45:13 +01:00
Philippe G
1a3c888f44 correct fixed volume spectrum scale - release 2020-12-06 22:52:37 -08:00
Philippe G
90ee59754d discover buffer potential overflow 2020-12-05 16:10:53 -08:00
Philippe G
c33c6187ca remove unused code + plugin update 2020-12-03 20:56:56 -08:00
Philippe G
99019cd22f mirror make branch changes on plugin (need to point there ...) 2020-12-02 14:06:49 -08:00
Philippe G
3dc2ed9629 fix hires display - release 2020-12-02 13:58:49 -08:00
Sébastien
99722a6f94 Update Dockerfile 2020-11-22 10:13:25 -05:00
Philippe G
75580feaa0 close socket when connect() fails
might be linked to "too many CLI connection" but I'm not sure
2020-11-21 18:09:41 -08:00
Sebastien
2d573da503 Fixing #59 and unnecessary jack insertion messages-release 2020-11-21 09:26:56 -05:00
Sébastien
2687023b6f change build - all branches except master 2020-11-20 11:50:43 -05:00
Sébastien
0993186e01 Create codeql-analysis.yml 2020-11-19 07:48:20 -05:00
Sébastien
ede495a145 Added workflow build badge to readme - release 2020-11-17 14:27:48 -05:00
Sebastien
c82ae71726 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-11-17 14:00:57 -05:00
Sebastien
cd13577d93 Reduce system load when loading HTTP UI - release 2020-11-17 13:58:19 -05:00
Sébastien
1d24543359 Update Cross build with build-number@v3 - release 2020-11-17 13:42:34 -05:00
Sebastien
08f04187ae Reduce system load when loading HTTP UI - release 2020-11-17 11:41:03 -05:00
Sebastien
8fbe1159f5 Reworking BT output 2020-11-03 17:54:31 -05:00
philippe44
2c64f50093 Update README.md 2020-10-31 22:19:54 -07:00
philippe44
cb646284c6 Update README.md 2020-10-31 22:14:45 -07:00
Sébastien
72c4f810fb Release Philippe's BT fixes 2020-10-29 07:12:24 -04:00
Philippe G
6170f49673 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2020-10-28 22:38:49 -07:00
Philippe G
d486a99aa1 more BT fixes - release 2020-10-28 22:38:39 -07:00
Sébastien
f0bd81125b Update build badge-release 2020-10-28 21:12:41 -04:00
Philippe G
251882a400 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2020-10-28 17:46:55 -07:00
Philippe G
0503d03e64 fix BT source - release 2020-10-28 17:46:46 -07:00
Sebastien
fed98f470f Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-28 20:40:07 -04:00
Sebastien
95d4dbc905 update Docker image for the build 2020-10-28 20:39:11 -04:00
Sebastien
54b9da287f update Docker image for the build 2020-10-28 18:23:22 -04:00
Sebastien
67ea623ba3 Fix binary signature with build number - release 2020-10-28 10:32:51 -04:00
Sebastien
9dbddd2c18 Disable unfinished Squeezelite config section - release 2020-10-27 17:26:06 -04:00
Sebastien
72345e1394 fix power save boot loop in IDF v4.0 - release
Disabled power save none, as this causes a boot loop under the latest esp-idf v4.0 release
2020-10-27 11:31:48 -04:00
Sebastien
f3662d4ca8 Fix build file for release 2020-10-27 09:54:27 -04:00
Sebastien
386f5a8cf7 Add artifact upload on create release 2020-10-27 09:34:02 -04:00
Sebastien
d23def1c02 Add artifact upload on create release 2020-10-27 09:32:12 -04:00
Sebastien
2861940b30 More build system changes for release 2020-10-27 09:16:46 -04:00
Sebastien
14f1e00540 Increase the Log Depth on build - Release
Also update the build name format
2020-10-27 09:07:57 -04:00
Sebastien
f1232eb83a Tweak build for release 2020-10-27 08:45:54 -04:00
Sebastien
42882265dd More Actions fixes and release 2020-10-27 08:18:51 -04:00
Sebastien
56d3c86b60 Fix compile error and attempt to release with GitHub Actions 2020-10-27 08:09:49 -04:00
Sebastien
445d335974 Additional fix to GitHub Actions 2020-10-26 21:36:18 -04:00
Sebastien
4825dc6644 Update Github Build and config command 2020-10-26 18:17:17 -04:00
Sebastien
33ad6d5950 Expand GitHub Actions for build 2020-10-26 17:58:46 -04:00
Sebastien
8ad02bd24c Update build Workflow Environment variable use 2020-10-16 12:24:20 -04:00
Sebastien
412227b958 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-16 12:18:37 -04:00
Sébastien
754b864647 Update build badges 2020-10-16 11:46:03 -04:00
Sébastien
65571c4fb6 Update I2S-4MBFlash.yml 2020-10-16 11:43:50 -04:00
Sébastien
81bcc0def5 Fix I2S 4M flash build 2020-10-16 11:40:08 -04:00
Sébastien
24e060cb3b Add SqueezeAmp target build script 2020-10-16 11:38:34 -04:00
Sebastien
b46ae1e77c Add target file for SqueezeAmp 2020-10-16 11:37:53 -04:00
Sebastien
45b655c34c Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-16 11:36:08 -04:00
Sébastien
0d954b2132 Update README.md 2020-10-16 11:26:16 -04:00
Sébastien
6a215b81c7 Add more targets 2020-10-16 11:25:45 -04:00
Sebastien
bfc1544ed1 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-16 11:16:15 -04:00
Sébastien
6866216dd0 Update I2S-4MBFlash.yml 2020-10-16 11:14:52 -04:00
Sebastien
42b9a54a45 Add more targets to the GitHub workflows 2020-10-16 11:12:57 -04:00
Sébastien
f6282fb798 Update I2S-4MBFlash.yml 2020-10-16 11:10:43 -04:00
Sébastien
830f1324ec Update README.md 2020-10-16 10:36:02 -04:00
Sébastien
f8a425e188 Update README.md 2020-10-16 10:33:06 -04:00
Sébastien
117aab6bef Update README.md 2020-10-16 10:30:15 -04:00
Sebastien
c73566caef Adding github build workflow 2020-10-16 10:23:43 -04:00
Sébastien
4167abb00e Update README.md 2020-10-16 09:50:52 -04:00
Sebastien
3e74292fc0 Reposition audio config and other small fixes - release 2020-10-09 16:35:30 -04:00
Sebastien
b301376fc6 Fix update page - release
Pressing check for update no longer appends duplicate entries in the branch selection drop down
2020-10-09 06:51:32 -04:00
Sebastien
262f5ff3e7 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-09 06:46:15 -04:00
Sebastien
d379858dd2 Display config UI fixes - release
Add 2 entries as a replacement for the ST77xx driver name, remove SSD1306 default.
2020-10-08 21:09:36 -04:00
Sébastien
368832d0d5 Major UI Update - release
- Bug fixes 
- Jack doesn't show as plugged in if no jack detection is configured 
- New layout
- Updated jQuery to latest version
- Updated bootstrap to latest version
- Updated the command processing backend to support UI interactions
- Added a number of accessors to normalize read/update various configuration entries 
- Added more GPIOs to the status tab GPIO list
- Added several configuration sections for hardware and system
- Removed pop-over windows from system messages
- Added a message count pill to the status tab
- Added support for message count pill based on the highest severity 
- Updated the message list table to set colours based on messages severity
- Added command processing message area close to the action buttons to provide feedback from running the commands


** See pervious commit for changed files**
2020-10-08 17:23:57 -04:00
Sebastien
be1d841039 Major UI Update
- Bug fixes 
- Jack doesn't show as plugged in if no jack detection is configured 
- New layout
- Updated jQuery to latest version
- Updated bootstrap to latest version
- Updated the command processing backend to support UI interactions
- Added a number of accessors to normalize read/update various configuration entries 
- Added more GPIOs to the status tab GPIO list
- Added several configuration sections for hardware and system
- Removed pop-over windows from system messages
- Added a message count pill to the status tab
- Added support for message count pill based on the highest severity 
- Updated the message list table to set colours based on messages severity
- Added command processing message area close to the action buttons to provide feedback from running the commands
2020-10-08 17:19:22 -04:00
Philippe G
6ae47a908b add player type 101 - release 2020-10-06 22:28:01 -07:00
Philippe G
797a21ee9f release 2020-09-26 12:21:45 -07:00
Philippe G
7f1db60c45 fix reboot logic upon server loss - release 2020-09-25 21:50:44 -07:00
Philippe G
fb530645b8 CRLF 2020-09-25 19:01:53 -07:00
Sébastien
7f932630fc release 2020-09-13 16:47:00 -04:00
Sebastien
458efb376a Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32.git into master-cmake 2020-09-13 12:17:38 -04:00
Sebastien
3ffbe022e5 Added wifi scanmode NVS option - release
f= default, fast scan
a= All channel scan
2020-09-13 12:17:27 -04:00
Philippe G
762c529563 fix a couple of gpio names (SPI/I2C)
Also a few "style" consistency changes so that within each file, look is the same, although different files can have different styles
2020-09-12 22:58:52 -07:00
Sebastien
0c224b4b84 New config UI for Services (Airplay, bt, etc) - release 2020-09-12 23:09:38 -04:00
219 changed files with 15589 additions and 11347 deletions

156
.github/workflows/CrossBuild.yml vendored Normal file
View File

@@ -0,0 +1,156 @@
# This is a basic workflow to help you get started with Actions
name: Cross-Build
on:
push:
branches-ignore: [ master ]
pull_request:
branches-ignore: [ master ]
jobs:
job1:
name: Build Number
runs-on: ubuntu-latest
outputs:
build_number: ${{ steps.buildnumber.outputs.build_number }}
steps:
- name: Generate common build number
id: buildnumber
uses: einaregilsson/build-number@v3
with:
token: ${{secrets.github_token}}
build:
runs-on: ubuntu-latest
needs: job1
strategy:
max-parallel: 1
matrix:
node: [I2S-4MFlash, SqueezeAmp]
depth: [16, 32]
steps:
- name: Set target name
run: |
echo "TARGET_BUILD_NAME=${{ matrix.node }}" >> $GITHUB_ENV
echo "build_version_prefix=1." >> $GITHUB_ENV
- uses: actions/checkout@v2
with:
fetch-depth: 15
submodules: true
- name: Cache build
id: cache-build
uses: actions/cache@v2
with:
path: |
build
/var/lib/docker
key: ${{ runner.os }}-${{ matrix.node }}-${{ matrix.depth }}
- name: Set build parameters
run: |
git update-index --chmod=+x ./server_certs/getcert.sh
cd server_certs;./getcert.sh;cat github.pem;cd ..
shopt -s nocasematch
branch_name="${GITHUB_REF//refs\/heads\//}"
branch_name="${branch_name//[^a-zA-Z0-9\-~!@_\.]/}"
BUILD_NUMBER=${{ needs.job1.outputs.build_number }}
echo "BUILD_NUMBER=${BUILD_NUMBER}" >> $GITHUB_ENV
tag="${TARGET_BUILD_NAME}.${{matrix.depth}}.${BUILD_NUMBER}.${branch_name}"
echo "tag=${tag}" >> $GITHUB_ENV
last_commit="$(git log --pretty=format:'%s' --max-count=1)"
if [[ "$last_commit" =~ .*"Release".* ]]; then echo "release_flag=1" >> $GITHUB_ENV; else echo "release_flag=0" >> $GITHUB_ENV; fi
name="1.${BUILD_NUMBER}-${{matrix.depth}}#v4.0#${TARGET_BUILD_NAME}#${branch_name}"
artifact_prefix="squeezelite-esp32-${branch_name}-${TARGET_BUILD_NAME}-${{matrix.depth}}-${build_version_prefix}${BUILD_NUMBER}"
artifact_file_name="${artifact_prefix}.zip"
artifact_bin_file_name="${artifact_prefix}.bin"
echo "name=${name}" >> $GITHUB_ENV
echo "last_commit=${last_commit}" >> $GITHUB_ENV
echo "artifact_file_name=${artifact_file_name}" >> $GITHUB_ENV
echo "PROJECT_VER=${TARGET_BUILD_NAME}-${{ steps.buildnumber.outputs.build_number }} " >> $GITHUB_ENV
echo "artifact_bin_file_name=${artifact_bin_file_name}" >> $GITHUB_ENV
description=""
description=${description}$'------------------------------\n### Revision Log\n\n'
description="$description$(git log --pretty=format:'%h %s (%cI) <%an>' --abbrev-commit --max-count=15 | sed --r 's/(^[\*]+)/\\\1/g') "
echo 'description<<~EOD' >> $GITHUB_ENV
echo ${description}>> $GITHUB_ENV
echo '~EOD' >> $GITHUB_ENV
echo #######
echo ####### Environment
echo #######
env
echo #######
echo ####### GITHUB ENV
echo #######
cat $GITHUB_ENV
- name: Build the firmware
run: |
env | grep "artifact\|tag\|GITHUB\|version\|NUMBER\|TARGET" >${TARGET_BUILD_NAME}-env.txt
echo "${tag}" >version.txt
docker pull sle118/idf:release-v4.0
docker info
docker run --env-file=${TARGET_BUILD_NAME}-env.txt -v $PWD:/project -w /project sle118/idf:release-v4.0 /bin/bash -c "cp build-scripts/${TARGET_BUILD_NAME}-sdkconfig.defaults sdkconfig && idf.py build -DDEPTH=${{ matrix.depth }} -DBUILD_NUMBER=${BUILD_NUMBER}-${{ matrix.depth }} && zip -r build_output.zip build && zip build/${artifact_file_name} partitions*.csv build/*.bin build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/flash_project_args build/size_*.txt"
# - name: Build Mock firmware
# run: |
# mkdir -p build
# cd build
# mkdir -p partition_table
# mkdir -p bootloader
# echo "mock content"> squeezelite.bin
# echo "mock content"> recovery.bin
# echo "mock content"> ./bootloader/bootloader.bin
# echo "mock content"> ./partition_table/partition-table.bin
# echo "mock content"> flash_project_args
# echo "mock content"> size_comp1.txt
# echo "mock content"> size_comp2.txt
# echo "mock content"> ../partitions.csv
- uses: actions/upload-artifact@v2
with:
name: ${{ env.artifact_file_name }}
path: |
build/*.bin
build/bootloader/bootloader.bin
build/partition_table/partition-table.bin
build/flash_project_args
build/size_comp1.txt
build/size_comp2.txt
partitions.csv
sdkconfig
server_certs/github.pem
build_output.zip
- uses: actions/upload-artifact@v2
with:
name: ${{ env.artifact_bin_file_name }}
path: |
build/squeezelite.bin
- name: Create Release
if: env.release_flag == 1
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ env.tag }}
release_name: ${{ env.name }}
body: ${{ env.description }}
draft: false
prerelease: true
- name: Upload Release Asset - Squeezelite binary file
if: env.release_flag == 1
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/squeezelite.bin
asset_name: ${{ env.artifact_bin_file_name }}
asset_content_type: application/octet-stream
- name: Upload Release Asset - Zip file
if: env.release_flag == 1
id: upload-release-asset-zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/${{ env.artifact_file_name }}
asset_name: ${{ env.artifact_file_name }}
asset_content_type: application/octet-stream

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master-cmake ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master-cmake ]
schedule:
- cron: '19 12 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'cpp', 'javascript', 'python']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

31
.gitignore vendored
View File

@@ -69,3 +69,34 @@ squeezelite-esp32-jsonblob.zip
/flash_cmd.txt
/writeSequeezeEsp.bat
/writeSequeezeEsp.sh
all_releases.json
alltags.txt
releases.json
sdkconfig
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/settings.json
.vscode/tasks.json
components/wifi-manager/.project
components/wifi-manager/.settings/.jsdtscope
components/wifi-manager/.settings/org.eclipse.wst.jsdt.ui.superType.container
components/wifi-manager/.settings/org.eclipse.wst.jsdt.ui.superType.name
components/wifi-manager/res/backup/
*.code-workspace
test/.vscode/
node_modules/*
esp-dsp/

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
</pydev_project>

View File

@@ -1,15 +1,14 @@
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS components/platform_console/app_recovery components/platform_console/app_squeezelite )
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
add_definitions(-DMODEL_NAME=SqueezeESP32)
if(NOT DEFINED DEPTH)
set(DEPTH "16")
endif()
message(STATUS "Building RECOVERY")
project(recovery)
set_property(TARGET recovery.elf PROPERTY RECOVERY_PREFIX app_recovery )
include(squeezelite.cmake)
set(PROJECT_VER $ENV{PROJECT_VER})
#target_compile_definitions(__idf_squeezelite-ota PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE)
#target_compile_definitions(__idf_driver_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE)
#target_compile_definitions(__idf_driver_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)

View File

@@ -18,8 +18,6 @@ RUN wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a
RUN tar -xzf xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
RUN rm xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
# Patch I2S in esp-idf
RUN cp /workspace/squeezelite-esp32/idf-patch/i2s.c /workspace/esp-idf/components/driver/i2s.c
RUN rm -r /workspace/squeezelite-esp32
RUN mkdir /workspace/squeezelite-esp32

366
README.md
View File

@@ -1,20 +1,90 @@
![Cross-Build](https://github.com/sle118/squeezelite-esp32/workflows/Cross-Build/badge.svg?branch=master-cmake)
# Squeezelite-esp32
## Supported Hardware
### SqueezeAMP
Works with the SqueezeAMP see [here](https://forums.slimdevices.com/showthread.php?110926-pre-ANNOUNCE-SqueezeAMP-and-SqueezeliteESP32) and [here](https://github.com/philippe44/SqueezeAMP/blob/master/README.md). Add repository https://raw.githubusercontent.com/sle118/squeezelite-esp32/master/plugin/repo.xml to LMS if you want to have a display
## What is this
Squeezelite-esp32 is an audio software suite made to run on espressif's ESP32 wifi (b/g/n) and bluetooth chipset. It offers the following capabilities
Use the `squeezelite-esp32-SqueezeAmp-sdkconfig.defaults` configuration file.
- Stream your local music and connect to all major on-line music providers (Spotify, Deezer, Tidal, Qobuz) using [Logitech Media Server - a.k.a LMS](https://forums.slimdevices.com/) and enjoy multi-room audio synchronization. LMS can be extended by numerous plugins and can be controlled using a Web browser or dedicated applications (iPhone, Android). It can also send audio to UPnP, Sonos, ChromeCast and AirPlay speakers/devices.
- Stream from a Bluetooth device (iPhone, Android)
- Stream from an AirPlay controller (iPhone, iTunes ...) and enjoy synchronization multiroom as well (although it's AirPlay 1 only)
Depending on the hardware connected to the ESP32, you can send audio to a local DAC, to SPDIF or to a Bluetooth speaker. The bare minimum required hardware is a WROVER module with 4MB of Flash and 4MB of PSRAM (https://www.espressif.com/en/products/modules/esp32). With that module standalone, just apply power and you can stream to a Bluetooth speaker. You can also send audio to most I2S DAC as well as to SPDIF receivers using just a cable or an optical transducer.
But squeezelite-esp32 is highly extensible and you can add
- Buttons and Rotary Encoder and map/combine them to various functions (play, pause, volume, next ...)
- IR receiver (no pullup resistor or capacitor needed, just the 38kHz receiver)
- Monochrome, GrayScale or Color displays using SPI or I2C (supported drivers are SH1106, SSD1306, SSD1322, SSD1326/7, SSD1351, ST7735, ST7789 and ILI9341).
- Ethernet using a Microchip LAN8720 with RMII interface or Davicom DM9051 over SPI.
Other features include
- Resampling
- 10-bands equalizer
- Automatic initial setup using any WiFi device
- Full web interface for further configuration/management
- Firmware over-the-air update
To control the equalizer or use the display on LMS, a new player model is required and this is provided through a plugin that is part of LMS' 3rd party repositories
## Performances
*(opinions presented here so I = @philippe44)*
The main build of squeezelite-esp32 is a 16 bits internal core with all calculations in 32 bits or float precision. This is a design choice I've made to preserve CPU performances (it is already stretching a lot the esp32 chipset) and optimize memory usage as we only have 4MB of usable RAM. Some might correctly comment that the WROVER module have 8MB of RAM, but the processor is only able to address 4MB and the remaining 4MB must be paginated by smaller blocks and I don't have patience to that.
Now, when I did the porting of squeezelite to esp32, I've also made the core 16 or 32 bits compatible at compile-time. So far, it works in 32 bits but less tests have been done. You can chose to compile it in 32 bits mode. I'm not very interested above 16 bits samples because it does not bring anything (I have an engineering background in theory of information).
| Capability |16 bits|32 bits| comment |
|----------------------------|-------|-------|--------------------------------------------------------------------|
| max sampling rate | 192k | 96k | 192k is very challenging, especially when combined with display |
| max bit depth | 16 | 24 | 24 bits are truncated in 16 bits mode |
| spdif |16 bits|20 bits| |
| mp3, aac, opus, ogg/vorbis | 48k | 48k | |
| alac, flac, ogg/flac | 96k | 96k | |
| pcm, wav, aif | 192k | 96k | |
| equalizer | Y | N | 48kHz max (after resampling) - equalization skipped on >48k tracks |
| resampling | Y | N | |
| cross-fade | 10s | <5s | depends on buffer size and sampling rate |
The esp32 must run at 240 MHz, with Quad-SPI I/O at 80 MHz and a clock of 40 Mhz. Still, it's a lot to run, especially knowing that it has a serial Flash and PSRAM, so kudos to Espressif for their chipset optimization. Now, to have all the decoding, resampling, equalizing, gain, display, spectrum/vu is a very (very) delicate equilibrium between use of internal /external RAM, tasks priorities and buffer handling. It is not perfect and the more you push the system to the limit, the higher the risk that some files would not play (see below). In general, the display will always have the lowest priority and you'll notice slowdown in scrolling and VU/Spectrum refresh rates. Now, even display thread has some critical section and impacts the capabilities. For example, a 16 bits-depth color display with low SPI speed might prevent 24/96 flac to work but still work with pcm 24/96
In 16 bits mode, although 192 kHz is reported as max rate, it's highly recommended to limit reported sampling rate to 96k (-Z 96000). Note that some high-speed 24/96k on-line streams might stutter because of TCP/IP stack performances. It is usually due to the fact that the server sends small packets of data and the esp32 cannot receive encoded audio fast enough, regardless of task priority settings (I've tried to tweak that a fair bit). The best option in that case is to let LMS proxy the stream as it will provide larger chunks and a "smoother" stream that can then be handled.
Note as well that some codecs consume more CPU than others or have not been optimized as much. I've done my best to tweak these, but that level of optimization includes writing some assembly which is painful. One very demanding codec is AAC when files are encoded with SBR. It allows reconstruction of upper part of spectrum and thus higher sampling rate, but the codec spec is such that this is optional, you can decode simply lower band and accept lower sampling rate - See the AAC_DISABLE_SBR option below.
## Supported Hardware
Any esp32-based hardware with at least 4MB of flash and 4MB of PSRAM will be capable of running squeezelite-esp32 and there are various boards that include such chip. A few are mentionned below, but any should work. You can find various help & instructions [here](https://forums.slimdevices.com/showthread.php?112697-ANNOUNCE-Squeezelite-ESP32-(dedicated-thread))
**For the sake of clarity, WROOM modules DO NOT work as they don't include PSRAM. Some designs might add it externally, but it's (very) unlikely.**
### Raw WROVER module
Per above description, a [WROVER module](https://www.espressif.com/en/products/modules/esp32) is enough to run Squeezelite-esp32, but that requires a bit of tinkering to extend it to have analogue audio or hardware buttons (e.g.)
Please note that when sending to a Bluetooth speaker (source), only 44.1 kHz can be used, so you either let LMS do the resampling, but you must make sure it only sends 44.1kHz tracks or enable internal resampling (using -R) option. If you connect a DAC, choice of sample rates will depends on its capabilities. See below for more details.
Most DAC will work out-of-the-box with simply an I2S connection, but some require specific commands to be sent using I2C. See DAC option below to understand how to send these dedicated commands. There is build-in support for TAS575x, TAS5780, TAS5713 and AC101 DAC.
### SqueezeAMP
This is the main hardware companion of Squeezelite-esp32 and has been developped together. Details on capabilities can be found [here](https://forums.slimdevices.com/showthread.php?110926-pre-ANNOUNCE-SqueezeAMP-and-SqueezeliteESP32) and [here](https://github.com/philippe44/SqueezeAMP).
if you want to rebuild, use the `squeezelite-esp32-SqueezeAmp-sdkconfig.defaults` configuration file.
NB: You can use the pre-build binaries SqueezeAMP4MBFlash which has all the hardware I/O set properly. You can also use the generic binary I2S4MBFlash in which case the NVS parameters shall be set to get the exact same behavior
- set_GPIO: 12=green,13=red,34=jack,2=spkfault
- batt_config: channel=7,scale=20.24
- dac_config: model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0
- spdif_config: bck=33,ws=25,do=15
### ESP32-A1S
Works with [ESP32-A1S](https://docs.ai-thinker.com/esp32-a1s) module that includes audio codec and headset output. You still need to use a demo board like [this](https://www.aliexpress.com/item/4000765857347.html?spm=2114.12010615.8148356.11.5d963cd0j669ns) or an external amplifier if you want direct speaker connection.
Works with [ESP32-A1S](https://docs.ai-thinker.com/esp32-a1s) module that includes audio codec and headset output. You still need to use a demo board like [this](https://www.aliexpress.com/item/4001060963585.html) or an external amplifier if you want direct speaker connection. Note that there is a version with AC101 codec and another one with ES8388 (see below)
The board showed above has the following IO set
The board shown above has the following IO set
- amplifier: GPIO21
- key2: GPIO13, key3: GPIO19, key4: GPIO23, key5: GPIO18, key6: GPIO5 (to be confirmed with dip switches)
- key1: not sure, something with GPIO36
- key1: not sure, using GPIO36 in a matrix
- jack insertion: GPIO39 (inserted low)
- LED: GPIO22 (active low)
(note that GPIO need pullups)
- D4 -> GPIO22 used for green LED (active low)
- D5 -> GPIO19 (muxed with key3)
- The IO connector also brings GPIO5, GPIO18, GPIO19, GPIO21, GPIO22 and GPIO23 (don't forget it's muxed with keys!)
- The JTAG connector uses GPIO 12, 13, 14 and 15 (see dip switch) but these are also used for SD-card (and GPIO13 is key2 as well)
- It's always possible to re-use GPIOO (download at boot) and GPIO1/GPIO3 which are RX/TX of UART0 but you'll lose trace
(note that some GPIO need pullups)
So a possible config would be
- set_GPIO: 21=amp,22=green:0,39=jack:0
@@ -22,7 +92,18 @@ So a possible config would be
```
[{"gpio":5,"normal":{"pressed":"ACTRLS_TOGGLE"}},{"gpio":18,"pull":true,"shifter_gpio":5,"normal":{"pressed":"ACTRLS_VOLUP"}, "shifted":{"pressed":"ACTRLS_NEXT"}}, {"gpio":23,"pull":true,"shifter_gpio":5,"normal":{"pressed":"ACTRLS_VOLDOWN"},"shifted":{"pressed":"ACTRLS_PREV"}}]
```
for AC101
- dac_config: model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32
for ES8388
- dac_config: model=ES8388,bck=5,ws=25,do=26,sda=18,scl=23,i2c=16
### T-WATCH2020 by LilyGo
This is a fun [smartwatch](http://www.lilygo.cn/prod_view.aspx?TypeId=50036&Id=1290&FId=t3:50036:3) based on ESP32. It has a 240x240 ST7789 screen and onboard audio. Not very useful to listen to anything but it works. This is an example of a device that requires an I2C set of commands for its dac (see below). There is a build-option if you decide to rebuild everything by yourself, otherwise the I2S default option works with the following parameters
- dac_config: model=I2S,bck=26,ws=25,do=33,i2c=106,sda=21,scl=22
- dac_controlset: { "init": [ {"reg":41, "val":128}, {"reg":18, "val":255} ], "poweron": [ {"reg":18, "val":64, "mode":"or"} ], "poweroff": [ {"reg":18, "val":191, "mode":"and"} ] }
- spi_config: dc=27,data=19,clk=18
- display_config: SPI,driver=ST7789,width=240,height=240,cs=5,back=12,speed=16000000,HFlip,VFlip
### ESP32-WROVER + I2S DAC
Squeezelite-esp32 requires esp32 chipset and 4MB PSRAM. ESP32-WROVER meets these requirements. To get an audio output an I2S DAC can be used. Cheap PCM5102 I2S DACs work others may also work. PCM5012 DACs can be hooked up via:
@@ -41,6 +122,10 @@ XMT - 3.3V
Use the `squeezelite-esp32-I2S-4MFlash-sdkconfig.defaults` configuration file.
### SqueezeAmpToo !
And the super cool project https://github.com/rochuck/squeeze-amp-too
## Configuration
To access NVS, in the webUI, go to credits and select "shows nvs editor". Go into the NVS editor tab to change NFS parameters. In syntax description below \<\> means a value while \[\] describe optional parameters.
@@ -49,16 +134,31 @@ The NVS parameter "i2c_config" set the i2c's gpio used for generic purpose (e.g.
```
sda=<gpio>,scl=<gpio>[,port=0|1][,speed=<speed>]
```
<strong>Please note that you can not use the same GPIO or port as the DAC</strong>
### SPI
The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is
The esp32 has 4 SPI sub-systems, one is unaccessible so numbering is 0..2 and SPI0 is reserved for Flash/PSRAM. The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is
```
data=<gpio>,clk=<gpio>[,dc=<gpio>][,host=1|2]
data|mosi=<gpio>,clk=<gpio>[,dc=<gpio>][,host=1|2][,miso=<gpio>]
```
Default "host" is 1. The "miso" parameter is only used when SPI bus is to be shared with other peripheral (e.g. ethernet, see below), otherwise it can be omitted. Note that "data" can also be named "mosi".
### DAC/I2S
The NVS parameter "dac_config" set the gpio used for i2s communication with your DAC. You can define the defaults at compile time but nvs parameter takes precedence except for SqueezeAMP and A1S where these are forced at runtime. If your DAC also requires i2c, then you must go the re-compile route. Syntax is
The NVS parameter "dac_config" set the gpio used for i2s communication with your DAC. You can define the defaults at compile time but nvs parameter takes precedence except for SqueezeAMP and A1S where these are forced at runtime. Syntax is
```
bck=<gpio>,ws=<gpio>,do=<gpio>
bck=<gpio>,ws=<gpio>,do=<gpio>[,mck][,mute=<gpio>[:0|1][,model=TAS57xx|TAS5713|AC101|I2S][,sda=<gpio>,scl=gpio[,i2c=<addr>]]
```
if "model" is not set or is not recognized, then default "I2S" is used. The option "mck" is used for some codecs that require a master clock (although they should not). Only GPIO0 can be used as MCLK and be aware that this cannot coexit with RMII Ethernet (see ethernet section below). I2C parameters are optional and only needed if your DAC requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed.
So far, TAS57xx, TAS5713, AC101, WM8978 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power on and off using a JSON syntax:
```
{ init: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
poweron: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
poweroff: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ] }
```
This is standard JSON notation, so if you are not familiar with it, Google is your best friend. Be aware that the '...' means you can have as many entries as you want, it's not part of the syntax. Every section is optional, but it does not make sense to set i2c in the 'dac_config' parameter and not setting anything here. The parameter 'mode' allows to *or* the register with the value or to *and* it. Don't set 'mode' if you simply want to write. **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
NB: For specific builds (all except I2S), all this is ignored. For know codecs, the built-in sequences can be overwritten using dac_controlset
<strong>Please note that you can not use the same GPIO or port as the I2C</strong>
### SPDIF
The NVS parameter "spdif_config" sets the i2s's gpio needed for SPDIF.
@@ -70,16 +170,42 @@ Leave it blank to disable SPDIF usage, you can also define them at compile time
```
bck=<gpio>,ws=<gpio>,do=<gpio>
```
NB: For well-known configuration, this is ignored
To optimize speed, a bit-manipulation trick is used and as a result, the bit depth is limited to 20 bits, even in 32 bits mode. As said before, this is more than enough for any human ear. In theory, it could be extended up to 23 bits but I don't see the need. Now, you can also get SPDIF using a specialized chip that offers a I2S interface like a DAC but spits out SPDIF (optical and coax). Refers to DAC chapter then.
If you want coax, you can also use a poor-man's trick to generate signal from a 3.3V GPIO. All that does is dividing the 3.3V to generate a 0.6V peak-to-peak and then remove DC
```
100nF
GPIO ----210ohm-----------||---- coax S/PDIF signal out
|
110ohm
|
Ground -------------------------- coax signal ground
```
### Display
The NVS parameter "display_config" sets the parameters for an optional display. Syntax is
```
I2C,width=<pixels>,height=<pixels>[address=<i2c_address>][,HFlip][,VFlip][driver=SSD1306|SSD1326|SH1106]
SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,speed=<speed>][,HFlip][,VFlip][driver=SSD1306|SSD1326|SH1106]
I2C,width=<pixels>,height=<pixels>[address=<i2c_address>][,reset=<gpio>][,HFlip][,VFlip][driver=SSD1306|SSD1326[:1|4]|SSD1327|SH1106]
SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,back=<gpio>][,reset=<gpio>][,speed=<speed>][,HFlip][,VFlip][driver=SSD1306|SSD1322|SSD1326[:1|4]|SSD1327|SH1106|SSD1675|ST7735|ST7789|ILI9341[:16|18][,rotate]]
```
- back: a LED backlight used by some older devices (ST7735). It is PWM controlled for brightness
- reset: some display have a reset pin that is should normally be pulled up if unused
- VFlip and HFlip are optional can be used to change display orientation
- rotate: for non-square *drivers*, move to portrait mode. Note that *width* and *height* must be inverted then
- Default speed is 8000000 (8MHz) but SPI can work up to 26MHz or even 40MHz
- SH1106 is 128x64 monochrome I2C/SPI [here]((https://www.waveshare.com/wiki/1.3inch_OLED_HAT))
- SSD1306 is 128x32 monochrome I2C/SPI [here](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi)
- SSD1322 is 256x64 grayscale 16-levels SPI in multiple sizes [here](https://www.buydisplay.com/oled-display/oled-display-module?resolution=159) - it is very nice
- SSD1326 is 256x32 monochrome or grayscale 16-levels SPI [here](https://www.aliexpress.com/item/32833603664.html?spm=a2g0o.productlist.0.0.2d19776cyQvsBi&algo_pvid=c7a3db92-e019-4095-8a28-dfdf0a087f98&algo_expid=c7a3db92-e019-4095-8a28-dfdf0a087f98-1&btsid=0ab6f81e15955375483301352e4208&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_)
- SSD1327 is 128x128 16-level grayscale SPI [here](https://www.amazon.com/gp/product/B079N1LLG8/ref=ox_sc_act_title_1?smid=A1N6DLY3NQK2VM&psc=1) - artwork can be up to 96x96 with vertical vu-meter/spectrum
- SSD1351 is 128x128 65k/262k color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/1.5inch-rgb-oled-module.htm)
- SSD1675 is an e-ink paper and is experimental as e-ink is really not suitable for LMS du to its very low refresh rate
- ST7735 is a 128x160 65k color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/1.8inch-lcd-module.htm). This needs a backlight control
- ST7789 is a 240x320 65k (262k not enabled) color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/2inch-lcd-module.htm). It also exist with 240x240 displays. See **rotate** for use in portrait mode
- ILI9341 is another 240x320 65k (262k capable) color SPI. I've not used it much, the driver it has been provided by one external contributor to the project
Currently 128x32/64 I2C and SPI display like [this](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi) and [this](https://www.waveshare.com/wiki/1.3inch_OLED_HAT) are supported
You can tweak how the vu-meter and spectrum analyzer are displayed, as well as size of artwork through a dedicated menu in player's settings (don't forget to add the plugin).
The NVS parameter "metadata_config" sets how metadata is displayed for AirPlay and Bluetooth. Syntax is
```
@@ -91,8 +217,6 @@ The NVS parameter "metadata_config" sets how metadata is displayed for AirPlay a
- 'format' can contain free text and any of the 3 keywords %artist%, %album%, %title%. Using that format string, the keywords are replaced by their value to build the string to be displayed. Note that the plain text following a keyword that happens to be empty during playback of a track will be removed. For example, if you have set format=%artist% - %title% and there is no artist in the metadata then only <title> will be displayed not " - <title>".
You can install the excellent plugin "Music Information Screen" which is super useful to tweak the layout for these small displays.
### Infrared
You can use any IR receiver compatible with NEC protocol (38KHz). Vcc, GND and output are the only pins that need to be connected, no pullup, no filtering capacitor, it's a straight connection.
@@ -103,11 +227,11 @@ In AirPlay and Bluetooth mode, only these native remotes are supported, I've not
See "set GPIO" below to set the GPIO associated to infrared receiver (option "ir").
### Set GPIO
The parameter "set_GPIO" is use to assign GPIO to various functions.
The parameter "set_GPIO" is used to assign GPIO to various functions.
GPIO can be set to GND provide or Vcc at boot. This is convenient to power devices that consume less than 40mA from the side connector. Be careful because there is no conflict checks being made wrt which GPIO you're changing, so you might damage your board or create a conflict here.
The \<amp\> parameter can use used to assign a GPIO that will be set to 1 when playback starts. It will be reset to 0 when squeezelite becomes idle. The idle timeout is set on the squeezelite command line through -C \<timeout\>
The \<amp\> parameter can use used to assign a GPIO that will be set to active level (default 1) when playback starts. It will be reset when squeezelite becomes idle. The idle timeout is set on the squeezelite command line through -C \<timeout\>
If you have an audio jack that supports insertion (use :0 or :1 to set the level when inserted), you can specify which GPIO it's connected to. Using the parameter jack_mutes_amp allows to mute the amp when headset (e.g.) is inserted.
@@ -118,24 +242,41 @@ The \<ir\> parameter set the GPIO associated to an IR receiver. No need to add p
Syntax is:
```
<gpio>=Vcc|GND|amp|ir|jack[:0|1]|green[:0|1]|red[:0|1]|spkfault[:0|1][,<repeated sequence for next GPIO>]
<gpio>=Vcc|GND|amp[:1|0]|ir|jack[:0|1]|green[:0|1]|red[:0|1]|spkfault[:0|1][,<repeated sequence for next GPIO>]
```
You can define the defaults for jack, spkfault leds at compile time but nvs parameter takes precedence except for SqueezeAMP where these are forced at runtime.
You can define the defaults for jack, spkfault leds at compile time but nvs parameter takes precedence except for well-known configurations where these are forced at runtime.
**Note that gpio 36 and 39 are input only and cannot use interrupt. When set to jack or speaker fault, a 100ms polling checks their value but that's expensive**
### LED
See §**set_GPIO** for how to set the green and red LEDs. In addition, their brightness can be controlled using the "led_brigthness" parameter. The syntax is
```
[green=0..100][,red=0..100]
```
NB: For well-known configuration, this is ignored
### Rotary Encoder
One rotary encoder is supported, quadrature shift with press. Such encoders usually have 2 pins for encoders (A and B), and common C that must be set to ground and an optional SW pin for press. A, B and SW must be pulled up, so automatic pull-up is provided by ESP32, but you can add your own resistors. A bit of filtering on A and B (~470nF) helps for debouncing which is not made by software.
Encoder is normally hard-coded to respectively knob left, right and push on LMS and to volume down/up/play toggle on BT and AirPlay. Using the option 'volume' makes it hard-coded to volume down/up/play toggle all the time (even in LMS). The option 'longpress' allows an alternate mode when SW is long-pressed. In that mode, left is previous, right is next and press is toggle. Every long press on SW alternates between modes.
Encoder is normally hard-coded to respectively knob left, right and push on LMS and to volume down/up/play toggle on BT and AirPlay. Using the option 'volume' makes it hard-coded to volume down/up/play toggle all the time (even in LMS). The option 'longpress' allows an alternate mode when SW is long-pressed. In that mode, left is previous, right is next and press is toggle. Every long press on SW alternates between modes (the main mode actual behavior depends on 'volume').
There is also the possibility to use 'knobonly' option (exclusive with 'volume' and 'longpress'). This mode attempts to offer a single knob full navigation which is a bit contorded due to LMS UI's principles. Left, Right and Press obey to LMS's navigation rules and especially Press always goes to lower submenu item, even when navigating in the Music Library. That causes a challenge as there is no 'Play', 'Back' or 'Pause' button. Workaround are as of below:
- longpress is 'Play'
- double press is 'Back' (Left in LMS's terminology).
- a quick left-right movement on the encoder is 'Pause'
The speed of double click (or left-right) can be set using the optional parameter of 'knobonly'. This is not a perfect solution, and other ideas are welcome. Be aware that the longer you set double click speed, the less responsive the interface will be. The reason is that I need to wait for that delay before deciding if it's a single or double click. It can also make menu navigation "hesitations" being easily interpreted as 'Pause'
Use parameter rotary_config with the following syntax:
```
A=<gpio>,B=<gpio>[,SW=gpio>[,volume][,longpress]]
A=<gpio>,B=<gpio>[,SW=gpio>[[,knobonly[=<ms>]]|[[,volume][,longpress]]]]
```
HW note: all gpio used for rotary have internal pull-up so normally there is no need to provide Vcc to the encoder. Nevertheless if the encoder board you're using also has its own pull-up that are stronger than ESP32's ones (which is likely the case), then there will be crosstalk between gpio, so you must bring Vcc. Look at your board schematic and you'll understand that these board pull-up create a "winning" pull-down when any other pin is grounded.
See also the "IMPORTANT NOTE" on the "Buttons" section
The SW gpio is optional, you can re-affect it to a pure button if you prefer but the volume, longpress and knobonly options make little sense as the missing switch plays an important role in these modes. You could still have the "volume" mode, but you won't be able to use it for *anything* expect volume up and down. So be aware that the use of syntax [] is a bit misleading hereabove.
See also the "IMPORTANT NOTE" on the "Buttons" section and remember that when 'lms_ctrls_raw' (see below) is activated, none of these knobonly,volume,longpress options apply, raw button codes (not actions) are simply sent to LMS
**Note that gpio 36 and 39 are input only and cannot use interrupt, so they cannot be set to A or B. When using them for SW, a 100ms polling is used which is expensive**
### Buttons
Buttons are described using a JSON string with the following syntax
```
@@ -170,10 +311,11 @@ Where (all parameters are optionals except gpio)
Where \<action\> is either the name of another configuration to load (remap) or one amongst
```
ACTRLS_NONE, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY,
ACTRLS_NONE, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY,
ACTRLS_PAUSE, ACTRLS_STOP, ACTRLS_REW, ACTRLS_FWD, ACTRLS_PREV, ACTRLS_NEXT,
BCTRLS_UP, BCTRLS_DOWN, BCTRLS_LEFT, BCTRLS_RIGHT,
KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH
BCTRLS_PS1, BCTRLS_PS2, BCTRLS_PS3, BCTRLS_PS4, BCTRLS_PS5, BCTRLS_PS6,
KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH,
```
One you've created such a string, use it to fill a new NVS parameter with any name below 16(?) characters. You can have as many of these configs as you can. Then set the config parameter "actrls_config" with the name of your default config
@@ -225,86 +367,94 @@ Below is a difficult but functional 2-buttons interface for your decoding pleasu
The benefit of the "raw" mode is that you can build a player which is as close as possible to a Boom (e.g.) but you can't use the remapping function nor longress or shift logics to do your own mapping when you have a limited set of buttons. In 'raw' mode, all you really need to define is the mapping between the gpio and the button. As far as LMS is concerned, any other option in these JSON payloads does not matter. Now, when you use BT or AirPlay, the full JSON construct described above fully applies, so the shift, longpress, remapping options still work.
**Be aware that when using non "raw" mode, the CLI (Command Line Interface) of LMS is used and *must* be available without password**
There is no good or bad option, it's your choice. Use the NVS parameter "lms_ctrls_raw" to change that option
**Note that gpio 36 and 39 are input only and cannot use interrupt. When using them for a button, a 100ms polling is started which is expensive. Long press is also likely to not work very well**
### Ethernet (coming soon)
Wired ethernet is supported by esp32 with various options but squeezelite is only supporting a Microchip LAN8720 with a RMII interface like [this](https://www.aliexpress.com/item/32858432526.html) or Davicom DM9051 over SPI like [that](https://www.amazon.com/dp/B08JLFWX9Z).
#### RMII (LAN8720)
- RMII PHY wiring is fixed and can not be changed
| GPIO | RMII Signal | Notes |
| ------ | ----------- | ------------ |
| GPIO21 | TX_EN | EMAC_TX_EN |
| GPIO19 | TX0 | EMAC_TXD0 |
| GPIO22 | TX1 | EMAC_TXD1 |
| GPIO25 | RX0 | EMAC_RXD0 |
| GPIO26 | RX1 | EMAC_RXD1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV |
- SMI (Serial Management Interface) wiring is not fixed and you can change it either in the configuration or using "eth_config" parameter with the following syntax:
```
model=lan8720,mdc=<gpio>,mdio=<gpio>[,rst=<gpio>]
```
Connecting a reset pin for the LAN8720 is optional but recommended to avoid that GPIO0 (50MHz input clock) locks the esp32 in download mode at boot time.
- Clock
The APLL of the esp32 is required for the audio codec, so we **need** a LAN8720 that provides a 50MHz clock. That clock **must** be connected to GPIO0, there is no alternative. This means that if your DAC requires an MCLK, then you are out of luck. It is not possible to have both to work together. There might be some workaround using CLK_OUT2 and GPIO3, but I don't have time for this.
#### SPI (DM9051)
Ethernet over SPI is supported as well and requires less GPIOs but is obvsiously slower. Another benefit is that the SPI bus can be shared with the display, but it's also possible to have a dedicated SPI interface. The esp32 has 4 SPI sub-systems, one is unaccessible so numbering is 0..2 and SPI0 is reserved for Flash/PSRAM. The "eth_config" parameter syntax becomes:
```
model=dm9051,cs=<gpio>,speed=<clk_in_Hz>,intr=<gpio>[,host=<-1|1|2>][,rst=<gpio>][,mosi=<gpio>,miso=<gpio>,clk=<gpio>]
```
- To use the system SPI, shared with display (see spi_config) "host" must be set to -1. Any other value will reserve the SPI interface (careful of conflict with spi_config). The default "host" is 2 to avoid conflicting wiht default "spi_config" settings.
- When not using system SPI, "mosi" for data out, "miso" for data in and "clk" **must** be set
- The esp32 has a special I/O multiplexer for faster speed (up to 80 MHz) but that requires using specific GPIOs, which depends on SPI bus (See [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) for more details)
| Pin Name | SPI2 | SPI3 |
| -------- | ---- | ---- |
| CS0* | 15 | 5 |
| SCLK | 14 | 18 |
| MISO | 12 | 19 |
| MOSI | 13 | 23 |
** THIS IS NOT AVAILABLE YET, SO MORE TO COME ON HOW TO USE WIRED ETHERNET***
### Battery / ADC
The NVS parameter "bat_config" sets the ADC1 channel used to measure battery/DC voltage. Scale is a float ratio applied to every sample of the 12 bits ADC. A measure is taken every 10s and an average is made every 5 minutes (not a sliding window). Syntax is
The NVS parameter "bat_config" sets the ADC1 channel used to measure battery/DC voltage. The "atten" value attenuates the input voltage to the ADC input (the read value maintains a 0-1V rage) where: 0=no attenuation(0..800mV), 1=2.5dB attenuation(0..1.1V), 2=6dB attenuation(0..1.35V), 3=11dB attenuation(0..2.6V). Scale is a float ratio applied to every sample of the 12 bits ADC. A measure is taken every 10s and an average is made every 5 minutes (not a sliding window). Syntax is
```
channel=0..7,scale=<scale>
channel=0..7,scale=<scale>,cells=<2|3>[,atten=<0|1|2|3>]
```
NB: Set parameter to empty to disable battery reading
## Setting up ESP-IDF
### Docker
#### **************** todo: Docker scripts needs some rework.
You can use docker to build squeezelite-esp32
First you need to build the Docker container:
```
docker build -t esp-idf .
```
Then you need to run the container:
```
docker run -i -t -v `pwd`:/workspace/squeezelite-esp32 esp-idf
```
The above command will mount this repo into the docker container and start a bash terminal
for you to then follow the below build steps
### Manual Install of ESP-IDF
<<<<<<< HEAD
Follow the instructions from https://docs.espressif.com/projects/esp-idf/en/v4.0/get-started/index.html to install the esp-idf v4.0. This is the currently supported release of the espressif software development system.
=======
<strong>Currently the master branch of this project requires this [IDF](https://github.com/espressif/esp-idf/tree/28f1cdf5ed7149d146ad5019c265c8bc3bfa2ac9) with gcc 5.2 (toolschain dated 20181001)
If you want to use a more recent version of gcc and IDF (4.0 stable), move to cmake-master branch</strong>
You can install IDF manually on Linux or Windows (using the Subsystem for Linux) following the instructions at: https://www.instructables.com/id/ESP32-Development-on-Windows-Subsystem-for-Linux/
You also need to use esp-dsp recent version or at least make sure you have this patch https://github.com/espressif/esp-dsp/pull/12/commits/8b082c1071497d49346ee6ed55351470c1cb4264
>>>>>>> refs/remotes/origin/master
## Building Squeezelite-esp32
MOST IMPORTANT: create the right default config file
- make defconfig
(Note: You can also copy over config files from the build-scripts folder to ./sdkconfig)
Then adapt the config file to your wifi/BT/I2C device (can also be done on the command line)
- make menuconfig
Then
```
idf.py -p PORT [-b BAUD] flash
idf.py -p PORT [-b BAUD] monitor
```
NB: Set parameter to empty to disable battery reading. For well-known configuration, this is ignored (except for SqueezeAMP where number of cells is required)
# Configuration
1/ setup WiFi
## Setup WiFi
- Boot the esp, look for a new wifi access point showing up and connect to it. Default build ssid and passwords are "squeezelite"/"squeezelite".
- Once connected, navigate to 192.168.4.1
- Wait for the list of access points visible from the device to populate in the web page.
- Choose an access point and enter any credential as needed
- Once connection is established, note down the address the device received; this is the address you will use to configure it going forward
2/ setup squeezelite command line (optional)
## Setup squeezelite command line (optional)
At this point, the device should have disabled its built-in access point and should be connected to a known WiFi network.
- navigate to the address that was noted in step #1
- Using the list of predefined options, hoose the mode in which you want squeezelite to start
- Using the list of predefined options, choose the mode in which you want squeezelite to start
- Generate the command
- Add or change any additional command line option (for example player name, etc)
- Activate squeezelite execution: this tells the device to automatiaclly run the command at start
- Update the configuration
- click on the "start toggle" button. This will force a reboot.
- The toggle switch should be set to 'ON' to ensure that squeezelite is active after booting
- The toggle switch should be set to 'ON' to ensure that squeezelite is active after booting (you might have to fiddle with it a few times)
- You can enable accessto NVS parameters under 'credits'
3/ Updating Squeezelite
## Monitor
In addition of the esp-idf serial link monitor option, you can also enable a telnet server (see NVS parameters) where you'll have access to a ton of logs of what's happening inside the WROVER.
## Update Squeezelite
- From the firmware tab, click on "Check for Updates"
- Look for updated binaries
- Select a line
- Click on "Flash!"
- The system will reboot into recovery mode (if not already in that mode), wipe the squeezelite partition and download/flash the selected version
- You can choose a local file or have a local webserver
3/ Recovery
## Recovery
- From the firmware tab, click on the "Recovery" button. This will reboot the ESP32 into recovery, where additional configuration options are available from the NVS editor
# Additional command line notes, configured from the http configuration
## Additional configuration notes (from the Web UI)
The squeezelite options are very similar to the regular Linux ones. Differences are :
- the output is -o ["BT -n '<sinkname>' "] | [I2S]
@@ -320,12 +470,46 @@ See squeezlite command line, but keys options are
- Z <rate> : tell LMS what is the max sample rate supported before LMS resamples
- R (see above)
- r "<minrate>-<maxrate>"
- C <sec> : set timeout to switch off amp gpio
- W : activate WAV and AIFF header parsing
# Building everything yourself
## Setting up ESP-IDF
### Docker
You can use docker to build squeezelite-esp32 (optional)
First you need to build the Docker container:
```
docker build -t esp-idf .
```
Then you need to run the container:
```
docker run -i -t -v `pwd`:/workspace/squeezelite-esp32 esp-idf
```
The above command will mount this repo into the docker container and start a bash terminal
for you to then follow the below build steps
## Additional misc notes to do you build
- as of this writing, ESP-IDF has a bug int he way the PLL values are calculated for i2s, so you *must* use the i2s.c file in the patch directory
### Manual Install of ESP-IDF
You can install IDF manually on Linux or Windows (using the Subsystem for Linux) following the instructions at: https://www.instructables.com/id/ESP32-Development-on-Windows-Subsystem-for-Linux/ or see here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/windows-setup.html for a direct install.
**Use the esp-idf 4.0 https://github.com/espressif/esp-idf/tree/release/v4.0 and a recent add esp-dsp (after 08/2020)**
## Building Squeezelite-esp32
When initially cloning the repo, make sure you do it recursively. For example: `git clone --recursive https://github.com/sle118/squeezelite-esp32.git`
Don't forget to choose one of the config files in build_scripts/ and rename it sdkconfig.defaults or sdkconfig as many important WiFi/BT options are set there. **The codecs libraries will not be rebuilt by these scripts (it's a tedious process - see below)**
Create and tweak your config using `idf.py menuconfig` then build binaries using `idf.py all`. It will build the recovery and the application (squeezelite). then use `idf.py flash` to write everything. Otherwise, if you just want to download squeezelite, do (assuming you have set ESPPORT (e.g. COM10) and ESPBAUD (e.g. 921600)
```
<path_to_your_python>/python.exe <path_to_your_esptool>/esptool.py -p %ESPPORT% -b %ESPBAUD% --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x150000 build/squeezelite.bin
```
Use `idf.py monitor` to monitor the application (see esp-idf documentation)
Note: You can use `idf.py build -DDEPTH=32` to build the 32 bits version and add the `-DVERSION=<your_version>` to add a custom version name (it will be 0.0-<your_version>). If you want to change the whole version string, see squeezelite.h. You can also disable the SBR extension of AAC codecs as it consumes a lot of CPU and might overload the esp32. Use `-DAAC_DISABLE_SBR=1` for that
If you have already cloned the repository and you are getting compile errors on one of the submodules (e.g. telnet), run the following git command in the root of the repository location: `git submodule update --init --recursive`
### Rebuild codecs (highly recommended to NOT try that)
- for codecs libraries, add -mlongcalls if you want to rebuild them, but you should not (use the provided ones in codecs/lib). if you really want to rebuild them, open an issue
- libmad, libflac (no esp's version), libvorbis (tremor - not esp's version), alac work
- libfaad does not really support real time, but if you want to try
- libfaad does not really support real time, but if you want to try (but using helixaac is a better option)
- -O3 -DFIXED_POINT -DSMALL_STACK
- change ac_link in configure and case ac_files, remove ''
- compiler but in cfft.c and cffti1, must disable optimization using
@@ -337,20 +521,8 @@ See squeezlite command line, but keys options are
- per mad & few others, edit configure and change $ac_link to add -c (faking link)
- change ac_files to remove ''
- add DEPS_CFLAGS and DEPS_LIBS to avoid pkg-config to be required
- stack consumption can be very high with some codec variants, so set NONTHREADSAFE_PSEUDOSTACK and GLOBAL_STACK_SIZE=32000 and unset VAR_ARRAYS in config.h
- better use helixaac
- set IDF_PATH=/home/esp-idf
- set ESPPORT=COM9
- update flash partition size
- other compiler #define
- use no resampling or set RESAMPLE (soxr) or set RESAMPLE16 for fast fixed 16 bits resampling
- use LOOPBACK (mandatory)
- use BYTES_PER_FRAME=4 (8 is not fully functionnal)
- LINKALL (mandatory)
- NO_FAAD unless you want to us faad, which currently overloads the CPU
- TREMOR_ONLY (mandatory)
- When initially cloning the repo, make sure you do it recursively. For example:
- git clone --recursive https://github.com/sle118/squeezelite-esp32.git
- If you have already cloned the repository and you are getting compile errors on one of the submodules (e.g. telnet), run the following git command in the root of the repository location
- git submodule update --init --recursive
- stack consumption can be very high with some codec variants, so set NONTHREADSAFE_PSEUDOSTACK and GLOBAL_STACK_SIZE=48000 and unset VAR_ARRAYS in config.h
- libmad has been patched to avoid using a lot of stack and is not provided here. There is an issue with sync detection in 1.15.1b from where the original stack patch was done but since a few fixes have been made wrt sync detection. This 1.15.1b-10 found on debian fixes the issue where mad thinks it has reached sync but has not and so returns a wrong sample rate. It comes at the expense of 8KB (!) of code where a simple check in squeezelite/mad.c that next_frame[0] is 0xff and next_frame[1] & 0xf0 is 0xf0 does the trick ...
# Footnotes
(1) SPDIF is made by tricking the I2S bus but this consumes a fair bit of CPU as it multiplies by four the throughput on the i2s bus. To optimize some computation, the parity of the spdif frames must always be 0, so at least one bit has to be available to force it. As SPDIF samples are 20+4 bits length maximum, the LSB is used for that purpose, so the bit 24 is randomly toggling. It does not matter for 16 bits samples but it has been chosen to truncate the last 4 bits for 24 bits samples. I'm sure that some smart dude can further optimize spdif_convert() and use the user bit instead. You're welcome to do a PR but, as said above, I (philippe44) am not interested by 24 bits mental illness :-) and I've already made an effort to provide 20 bits which already way more what's needed :-)

View File

@@ -29,7 +29,10 @@ CONFIG_SPDIF_DO_IO=-1
CONFIG_DAC_CONFIG="model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32"
CONFIG_MUTE_GPIO=-1
CONFIG_MUTE_GPIO_LEVEL=-1
CONFIG_RELEASE_API="https://api.github.com/repos/sle118/squeezelite-esp32/releases"
CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
CONFIG_PROJECT_NAME="Squeezelite ESP32-A1S"
CONFIG_FW_PLATFORM_NAME="ESP32-A1S"
CONFIG_IDF_TARGET_ESP32=y
CONFIG_IDF_TARGET="esp32"
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000
@@ -180,13 +183,9 @@ CONFIG_BT_A2DP_ENABLE=y
# CONFIG_BT_SPP_ENABLED is not set
# CONFIG_BT_HFP_ENABLE is not set
CONFIG_BT_SSP_ENABLED=y
<<<<<<< HEAD
# CONFIG_BT_BLE_ENABLED is not set
CONFIG_BT_STACK_NO_LOG=y
=======
CONFIG_BT_BLE_ENABLED=n
CONFIG_BT_BLE_SMP_ENABLE=y
CONFIG_BT_STACK_NO_LOG=n
@@ -356,7 +355,6 @@ CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL=2
>>>>>>> refs/remotes/origin/master
CONFIG_BT_ACL_CONNECTIONS=4
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y
@@ -630,19 +628,9 @@ CONFIG_LWIP_MAX_SOCKETS=16
# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set
CONFIG_LWIP_SO_REUSE=y
CONFIG_LWIP_SO_REUSE_RXTOALL=y
<<<<<<< HEAD
# CONFIG_LWIP_SO_RCVBUF is not set
# CONFIG_LWIP_IP_REASSEMBLY is not set
# CONFIG_LWIP_IP_FRAG is not set
# CONFIG_LWIP_STATS is not set
# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set
=======
CONFIG_LWIP_IP_REASSEMBLY=y
>>>>>>> refs/remotes/origin/master
#CONFIG_LWIP_IP_REASSEMBLY is not set
CONFIG_LWIP_IP6_REASSEMBLY=y
CONFIG_LWIP_IP4_REASSEMBLY=y
CONFIG_LWIP_ESP_GRATUITOUS_ARP=y
CONFIG_LWIP_GARP_TMR_INTERVAL=60
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32

View File

@@ -34,7 +34,10 @@ CONFIG_SPDIF_WS_IO=-1
CONFIG_SPDIF_DO_IO=-1
CONFIG_MUTE_GPIO=-1
CONFIG_MUTE_GPIO_LEVEL=-1
CONFIG_RELEASE_API="https://api.github.com/repos/sle118/squeezelite-esp32/releases"
CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
CONFIG_PROJECT_NAME="Squeezelite-ESP32"
CONFIG_FW_PLATFORM_NAME="I2S-4MFlash"
#
# SDK tool configuration
#
@@ -179,183 +182,10 @@ CONFIG_BT_A2DP_ENABLE=y
# CONFIG_BT_SPP_ENABLED is not set
# CONFIG_BT_HFP_ENABLE is not set
CONFIG_BT_SSP_ENABLED=y
<<<<<<< HEAD
# CONFIG_BT_BLE_ENABLED is not set
CONFIG_BT_STACK_NO_LOG=y
=======
CONFIG_BT_BLE_ENABLED=n
CONFIG_BT_BLE_SMP_ENABLE=y
CONFIG_BT_LOG_HCI_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_HCI_TRACE_LEVEL=2
CONFIG_BT_LOG_BTM_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BTM_TRACE_LEVEL=2
CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_L2CAP_TRACE_LEVEL=2
CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL=2
CONFIG_BT_LOG_SDP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_SDP_TRACE_LEVEL=2
CONFIG_BT_LOG_GAP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_GAP_TRACE_LEVEL=2
CONFIG_BT_LOG_BNEP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BNEP_TRACE_LEVEL=2
CONFIG_BT_LOG_PAN_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_PAN_TRACE_LEVEL=2
CONFIG_BT_LOG_A2D_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_A2D_TRACE_LEVEL=2
CONFIG_BT_LOG_AVDT_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_AVDT_TRACE_LEVEL=2
CONFIG_BT_LOG_AVCT_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_AVCT_TRACE_LEVEL=2
CONFIG_BT_LOG_AVRC_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_AVRC_TRACE_LEVEL=2
CONFIG_BT_LOG_MCA_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_MCA_TRACE_LEVEL=2
CONFIG_BT_LOG_HID_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_HID_TRACE_LEVEL=2
CONFIG_BT_LOG_APPL_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_APPL_TRACE_LEVEL=2
CONFIG_BT_LOG_GATT_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_GATT_TRACE_LEVEL=2
CONFIG_BT_LOG_SMP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_SMP_TRACE_LEVEL=2
CONFIG_BT_LOG_BTIF_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BTIF_TRACE_LEVEL=2
CONFIG_BT_LOG_BTC_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BTC_TRACE_LEVEL=2
CONFIG_BT_LOG_OSI_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_OSI_TRACE_LEVEL=2
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL=2
>>>>>>> refs/remotes/origin/master
CONFIG_BT_STACK_NO_LOG=n
CONFIG_BT_ACL_CONNECTIONS=4
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y
@@ -507,11 +337,6 @@ CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=40
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=12
# CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED is not set
# CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED is not set
CONFIG_ESP32_WIFI_NVS_ENABLED=y
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
@@ -633,19 +458,13 @@ CONFIG_LWIP_MAX_SOCKETS=16
# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set
CONFIG_LWIP_SO_REUSE=y
CONFIG_LWIP_SO_REUSE_RXTOALL=y
<<<<<<< HEAD
# CONFIG_LWIP_SO_RCVBUF is not set
# CONFIG_LWIP_IP_FRAG is not set
# CONFIG_LWIP_IP_REASSEMBLY is not set
# CONFIG_LWIP_STATS is not set
# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set
=======
CONFIG_LWIP_IP_REASSEMBLY=y
>>>>>>> refs/remotes/origin/master
#CONFIG_LWIP_IP_REASSEMBLY is not set
CONFIG_LWIP_IP6_REASSEMBLY=y
CONFIG_LWIP_IP4_REASSEMBLY=y
CONFIG_LWIP_ESP_GRATUITOUS_ARP=y
CONFIG_LWIP_GARP_TMR_INTERVAL=60
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,10 @@ CONFIG_SPDIF_NUM=0
CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0"
CONFIG_MUTE_GPIO_LEVEL=-1
CONFIG_PROJECT_NAME="SqueezeAmp"
CONFIG_FW_PLATFORM_NAME="SqueezeAmp"
CONFIG_RELEASE_API="https://api.github.com/repos/sle118/squeezelite-esp32/releases"
CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
#
# SDK tool configuration
#
@@ -180,183 +183,8 @@ CONFIG_BT_A2DP_ENABLE=y
# CONFIG_BT_SPP_ENABLED is not set
# CONFIG_BT_HFP_ENABLE is not set
CONFIG_BT_SSP_ENABLED=y
<<<<<<< HEAD
# CONFIG_BT_BLE_ENABLED is not set
CONFIG_BT_STACK_NO_LOG=y
=======
CONFIG_BT_BLE_ENABLED=n
CONFIG_BT_BLE_SMP_ENABLE=y
CONFIG_BT_LOG_HCI_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_HCI_TRACE_LEVEL=2
CONFIG_BT_LOG_BTM_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BTM_TRACE_LEVEL=2
CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_L2CAP_TRACE_LEVEL=2
CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL=2
CONFIG_BT_LOG_SDP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_SDP_TRACE_LEVEL=2
CONFIG_BT_LOG_GAP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_GAP_TRACE_LEVEL=2
CONFIG_BT_LOG_BNEP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BNEP_TRACE_LEVEL=2
CONFIG_BT_LOG_PAN_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_PAN_TRACE_LEVEL=2
CONFIG_BT_LOG_A2D_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_A2D_TRACE_LEVEL=2
CONFIG_BT_LOG_AVDT_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_AVDT_TRACE_LEVEL=2
CONFIG_BT_LOG_AVCT_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_AVCT_TRACE_LEVEL=2
CONFIG_BT_LOG_AVRC_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_AVRC_TRACE_LEVEL=2
CONFIG_BT_LOG_MCA_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_MCA_TRACE_LEVEL=2
CONFIG_BT_LOG_HID_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_HID_TRACE_LEVEL=2
CONFIG_BT_LOG_APPL_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_APPL_TRACE_LEVEL=2
CONFIG_BT_LOG_GATT_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_GATT_TRACE_LEVEL=2
CONFIG_BT_LOG_SMP_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_SMP_TRACE_LEVEL=2
CONFIG_BT_LOG_BTIF_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BTIF_TRACE_LEVEL=2
CONFIG_BT_LOG_BTC_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BTC_TRACE_LEVEL=2
CONFIG_BT_LOG_OSI_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_OSI_TRACE_LEVEL=2
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_WARNING=y
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL=2
>>>>>>> refs/remotes/origin/master
CONFIG_BT_STACK_NO_LOG=n
CONFIG_BT_ACL_CONNECTIONS=4
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y
@@ -630,19 +458,9 @@ CONFIG_LWIP_MAX_SOCKETS=16
# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set
CONFIG_LWIP_SO_REUSE=y
CONFIG_LWIP_SO_REUSE_RXTOALL=y
<<<<<<< HEAD
# CONFIG_LWIP_SO_RCVBUF is not set
# CONFIG_LWIP_IP_FRAG is not set
# CONFIG_LWIP_IP_REASSEMBLY is not set
# CONFIG_LWIP_STATS is not set
# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set
=======
CONFIG_LWIP_IP_REASSEMBLY=y
>>>>>>> refs/remotes/origin/master
#CONFIG_LWIP_IP_REASSEMBLY is not set
CONFIG_LWIP_IP6_REASSEMBLY=y
CONFIG_LWIP_IP4_REASSEMBLY=y
CONFIG_LWIP_ESP_GRATUITOUS_ARP=y
CONFIG_LWIP_GARP_TMR_INTERVAL=60
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32

File diff suppressed because it is too large Load Diff

View File

@@ -1,840 +0,0 @@
#
# Automatically generated file. DO NOT EDIT.
# Espressif IoT Development Framework (ESP-IDF) Project Configuration
#
CONFIG_IDF_TARGET_ESP32=y
CONFIG_IDF_TARGET="esp32"
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000
#
# SDK tool configuration
#
CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-"
CONFIG_APP_COMPILE_TIME_DATE=y
# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set
# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set
CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y
# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set
CONFIG_BOOTLOADER_LOG_LEVEL=3
CONFIG_BOOTLOADER_SPI_WP_PIN=7
CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y
# CONFIG_BOOTLOADER_FACTORY_RESET is not set
# CONFIG_BOOTLOADER_APP_TEST is not set
CONFIG_BOOTLOADER_WDT_ENABLE=y
# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set
CONFIG_BOOTLOADER_WDT_TIME_MS=9000
# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set
# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set
# CONFIG_SECURE_BOOT_ENABLED is not set
# CONFIG_SECURE_FLASH_ENC_ENABLED is not set
CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set
# CONFIG_ESPTOOLPY_FLASHMODE_DIO is not set
# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set
CONFIG_ESPTOOLPY_FLASHMODE="dio"
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set
# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set
# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set
CONFIG_ESPTOOLPY_FLASHFREQ="80m"
# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y
CONFIG_ESPTOOLPY_BEFORE_RESET=y
# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set
CONFIG_ESPTOOLPY_BEFORE="default_reset"
CONFIG_ESPTOOLPY_AFTER_RESET=y
# CONFIG_ESPTOOLPY_AFTER_NORESET is not set
CONFIG_ESPTOOLPY_AFTER="hard_reset"
# CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B is not set
# CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B is not set
CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y
# CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B is not set
# CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B is not set
# CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB is not set
# CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER is not set
CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200
CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y
CONFIG_LOGGING_SLIMPROTO="info"
CONFIG_LOGGING_STREAM="info"
CONFIG_LOGGING_DECODE="info"
CONFIG_LOGGING_OUTPUT="info"
# CONFIG_SQUEEZEAMP is not set
# CONFIG_A1S is not set
CONFIG_BASIC_I2C_BT=y
CONFIG_I2S_NUM=0
CONFIG_I2S_BCK_IO=33
CONFIG_I2S_WS_IO=25
CONFIG_I2S_DO_IO=32
CONFIG_I2S_DI_IO=-1
CONFIG_SDIF_NUM=0
CONFIG_SPDIF_BCK_IO=33
CONFIG_SPDIF_WS_IO=25
CONFIG_SPDIF_DO_IO=15
CONFIG_A2DP_SINK_NAME="SMSL BT4.2"
CONFIG_A2DP_DEV_NAME="Squeezelite"
CONFIG_A2DP_CONTROL_DELAY_MS=500
CONFIG_A2DP_CONNECT_TIMEOUT_MS=1000
CONFIG_BT_SINK=y
CONFIG_BT_NAME="ESP32-BT"
CONFIG_BT_SINK_PIN=1234
CONFIG_AIRPLAY_SINK=y
CONFIG_AIRPLAY_NAME="ESP32-AirPlay"
CONFIG_AIRPLAY_PORT="5000"
CONFIG_DISPLAY_CONFIG=""
CONFIG_I2C_CONFIG=""
CONFIG_SPI_CONFIG=""
CONFIG_SET_GPIO=""
CONFIG_ROTARY_ENCODER=""
CONFIG_LED_GREEN_GPIO=-1
CONFIG_LED_RED_GPIO=-1
CONFIG_JACK_GPIO=-1
CONFIG_SPKFAULT_GPIO=-1
CONFIG_BAT_CHANNEL=-1
CONFIG_WIFI_MANAGER_TASK_PRIORITY=5
CONFIG_WIFI_MANAGER_MAX_RETRY=2
CONFIG_DEFAULT_AP_SSID="squeezelite"
CONFIG_DEFAULT_AP_PASSWORD="squeezelite"
CONFIG_DEFAULT_AP_CHANNEL=1
CONFIG_DEFAULT_AP_IP="192.168.4.1"
CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30"
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE is not set
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set
# CONFIG_COMPILER_CXX_EXCEPTIONS is not set
CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set
# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set
# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set
# CONFIG_COMPILER_STACK_CHECK is not set
# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set
# CONFIG_COMPILER_DISABLE_GCC8_WARNINGS is not set
# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
CONFIG_ESP32_APPTRACE_DEST_NONE=y
# CONFIG_ESP32_APPTRACE_ENABLE is not set
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
CONFIG_BT_ENABLED=y
# CONFIG_BTDM_CTRL_MODE_BLE_ONLY is not set
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
# CONFIG_BTDM_CTRL_MODE_BTDM is not set
CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN=2
CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN=0
# CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI is not set
CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_PCM=y
CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF=1
# CONFIG_BTDM_CTRL_AUTO_LATENCY_EFF is not set
CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=0
CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=2
CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=0
CONFIG_BTDM_CTRL_PINNED_TO_CORE_0=y
# CONFIG_BTDM_CTRL_PINNED_TO_CORE_1 is not set
CONFIG_BTDM_CTRL_PINNED_TO_CORE=0
CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y
# CONFIG_BTDM_CTRL_HCI_MODE_UART_H4 is not set
CONFIG_BTDM_MODEM_SLEEP=y
CONFIG_BTDM_MODEM_SLEEP_MODE_ORIG=y
# CONFIG_BTDM_MODEM_SLEEP_MODE_EVED is not set
CONFIG_BTDM_LPCLK_SEL_MAIN_XTAL=y
CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF=1
# CONFIG_BTDM_COEX_BT_OPTIONS is not set
CONFIG_BT_BLUEDROID_ENABLED=y
# CONFIG_BT_NIMBLE_ENABLED is not set
# CONFIG_BT_CONTROLLER_ONLY is not set
CONFIG_BT_BTC_TASK_STACK_SIZE=3072
CONFIG_BT_BLUEDROID_PINNED_TO_CORE_0=y
# CONFIG_BT_BLUEDROID_PINNED_TO_CORE_1 is not set
CONFIG_BT_BLUEDROID_PINNED_TO_CORE=0
CONFIG_BT_BTU_TASK_STACK_SIZE=4096
# CONFIG_BT_BLUEDROID_MEM_DEBUG is not set
CONFIG_BT_CLASSIC_ENABLED=y
CONFIG_BT_A2DP_ENABLE=y
# CONFIG_BT_SPP_ENABLED is not set
# CONFIG_BT_HFP_ENABLE is not set
CONFIG_BT_SSP_ENABLED=y
CONFIG_BT_BLE_ENABLED=y
CONFIG_BT_GATTS_ENABLE=y
# CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set
CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_AUTO=y
CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE=0
CONFIG_BT_GATTC_ENABLE=y
# CONFIG_BT_GATTC_CACHE_NVS_FLASH is not set
CONFIG_BT_BLE_SMP_ENABLE=y
# CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set
CONFIG_BT_STACK_NO_LOG=y
CONFIG_BT_ACL_CONNECTIONS=4
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y
# CONFIG_BT_BLE_HOST_QUEUE_CONG_CHECK is not set
CONFIG_BT_SMP_ENABLE=y
CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT=30
CONFIG_BT_RESERVE_DRAM=0xdb5c
# CONFIG_BLE_MESH is not set
# CONFIG_ADC_FORCE_XPD_FSM is not set
CONFIG_ADC_DISABLE_DAC=y
# CONFIG_SPI_MASTER_IN_IRAM is not set
CONFIG_SPI_MASTER_ISR_IN_IRAM=y
# CONFIG_SPI_SLAVE_IN_IRAM is not set
CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
# CONFIG_EFUSE_CUSTOM_TABLE is not set
# CONFIG_EFUSE_VIRTUAL is not set
# CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set
CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y
# CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set
CONFIG_EFUSE_MAX_BLK_LEN=192
# CONFIG_ESP_TLS_SERVER is not set
CONFIG_ESP32_REV_MIN_0=y
# CONFIG_ESP32_REV_MIN_1 is not set
# CONFIG_ESP32_REV_MIN_2 is not set
# CONFIG_ESP32_REV_MIN_3 is not set
CONFIG_ESP32_REV_MIN=0
CONFIG_ESP32_DPORT_WORKAROUND=y
# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set
# CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_SPIRAM_BOOT_INIT=y
# CONFIG_SPIRAM_USE_MEMMAP is not set
# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_TYPE_AUTO=y
# CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set
# CONFIG_SPIRAM_TYPE_ESPPSRAM64 is not set
CONFIG_SPIRAM_SIZE=-1
# CONFIG_SPIRAM_SPEED_40M is not set
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_MEMTEST=y
CONFIG_SPIRAM_CACHE_WORKAROUND=y
CONFIG_SPIRAM_BANKSWITCH_ENABLE=y
CONFIG_SPIRAM_BANKSWITCH_RESERVE=8
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=256
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
# CONFIG_SPIRAM_OCCUPY_HSPI_HOST is not set
CONFIG_SPIRAM_OCCUPY_VSPI_HOST=y
# CONFIG_SPIRAM_OCCUPY_NO_HOST is not set
CONFIG_D0WD_PSRAM_CLK_IO=17
CONFIG_D0WD_PSRAM_CS_IO=16
CONFIG_D2WD_PSRAM_CLK_IO=9
CONFIG_D2WD_PSRAM_CS_IO=10
CONFIG_PICO_PSRAM_CS_IO=10
# CONFIG_ESP32_MEMMAP_TRACEMEM is not set
# CONFIG_ESP32_MEMMAP_TRACEMEM_TWOBANKS is not set
# CONFIG_ESP32_TRAX is not set
CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0
# CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO is not set
CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y
CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4
# CONFIG_ESP32_ULP_COPROC_ENABLED is not set
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0
# CONFIG_ESP32_PANIC_PRINT_HALT is not set
CONFIG_ESP32_PANIC_PRINT_REBOOT=y
# CONFIG_ESP32_PANIC_SILENT_REBOOT is not set
# CONFIG_ESP32_PANIC_GDBSTUB is not set
CONFIG_ESP32_DEBUG_OCDAWARE=y
# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set
CONFIG_ESP32_BROWNOUT_DET=y
CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y
# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set
# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set
CONFIG_ESP32_BROWNOUT_DET_LVL=0
CONFIG_ESP32_REDUCE_PHY_TX_POWER=y
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y
# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set
# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set
# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set
CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y
# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set
# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set
# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set
CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024
CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP32_XTAL_FREQ_40=y
# CONFIG_ESP32_XTAL_FREQ_26 is not set
# CONFIG_ESP32_XTAL_FREQ_AUTO is not set
CONFIG_ESP32_XTAL_FREQ=40
# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set
# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set
# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set
CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL=5
# CONFIG_PM_ENABLE is not set
CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y
CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y
CONFIG_ADC_CAL_LUT_ENABLE=y
# CONFIG_ESP_TIMER_PROFILING is not set
CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_ESP_IPC_TASK_STACK_SIZE=1024
CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584
CONFIG_ESP_CONSOLE_UART_DEFAULT=y
# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set
# CONFIG_ESP_CONSOLE_UART_NONE is not set
CONFIG_ESP_CONSOLE_UART_NUM=0
CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200
CONFIG_ESP_INT_WDT=y
CONFIG_ESP_INT_WDT_TIMEOUT_MS=800
CONFIG_ESP_INT_WDT_CHECK_CPU1=y
CONFIG_ESP_TASK_WDT=y
# CONFIG_ESP_TASK_WDT_PANIC is not set
CONFIG_ESP_TASK_WDT_TIMEOUT_S=5
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ETH_PHY_INTERFACE_RMII=y
# CONFIG_ETH_PHY_INTERFACE_MII is not set
CONFIG_ETH_RMII_CLK_INPUT=y
# CONFIG_ETH_RMII_CLK_OUTPUT is not set
CONFIG_ETH_RMII_CLK_IN_GPIO=0
CONFIG_ETH_DMA_BUFFER_SIZE=512
CONFIG_ETH_DMA_RX_BUFFER_NUM=10
CONFIG_ETH_DMA_TX_BUFFER_NUM=10
# CONFIG_ETH_USE_SPI_ETHERNET is not set
# CONFIG_ESP_EVENT_LOOP_PROFILING is not set
CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
# CONFIG_HTTPD_LOG_PURGE_DATA is not set
CONFIG_OTA_ALLOW_HTTP=y
# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set
CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=12
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=40
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=12
# CONFIG_ESP32_WIFI_CSI_ENABLED is not set
# CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED is not set
# CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED is not set
CONFIG_ESP32_WIFI_RX_BA_WIN=16
CONFIG_ESP32_WIFI_NVS_ENABLED=y
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set
CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752
CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32
# CONFIG_ESP32_WIFI_DEBUG_LOG_ENABLE is not set
# CONFIG_ESP32_WIFI_IRAM_OPT is not set
# CONFIG_ESP32_WIFI_RX_IRAM_OPT is not set
CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
#CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION=y
CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
CONFIG_ESP32_PHY_MAX_TX_POWER=20
# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set
# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set
CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y
# CONFIG_ESP32_ENABLE_COREDUMP is not set
# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set
CONFIG_FATFS_CODEPAGE_437=y
# CONFIG_FATFS_CODEPAGE_720 is not set
# CONFIG_FATFS_CODEPAGE_737 is not set
# CONFIG_FATFS_CODEPAGE_771 is not set
# CONFIG_FATFS_CODEPAGE_775 is not set
# CONFIG_FATFS_CODEPAGE_850 is not set
# CONFIG_FATFS_CODEPAGE_852 is not set
# CONFIG_FATFS_CODEPAGE_855 is not set
# CONFIG_FATFS_CODEPAGE_857 is not set
# CONFIG_FATFS_CODEPAGE_860 is not set
# CONFIG_FATFS_CODEPAGE_861 is not set
# CONFIG_FATFS_CODEPAGE_862 is not set
# CONFIG_FATFS_CODEPAGE_863 is not set
# CONFIG_FATFS_CODEPAGE_864 is not set
# CONFIG_FATFS_CODEPAGE_865 is not set
# CONFIG_FATFS_CODEPAGE_866 is not set
# CONFIG_FATFS_CODEPAGE_869 is not set
# CONFIG_FATFS_CODEPAGE_932 is not set
# CONFIG_FATFS_CODEPAGE_936 is not set
# CONFIG_FATFS_CODEPAGE_949 is not set
# CONFIG_FATFS_CODEPAGE_950 is not set
CONFIG_FATFS_CODEPAGE=437
CONFIG_FATFS_LFN_NONE=y
# CONFIG_FATFS_LFN_HEAP is not set
# CONFIG_FATFS_LFN_STACK is not set
CONFIG_FATFS_FS_LOCK=0
CONFIG_FATFS_TIMEOUT_MS=10000
CONFIG_FATFS_PER_FILE_CACHE=y
CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200
CONFIG_FMB_QUEUE_LENGTH=20
CONFIG_FMB_SERIAL_TASK_STACK_SIZE=2048
CONFIG_FMB_SERIAL_BUF_SIZE=256
CONFIG_FMB_SERIAL_TASK_PRIO=10
# CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT is not set
CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20
CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20
CONFIG_FMB_CONTROLLER_STACK_SIZE=4096
CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20
CONFIG_FMB_TIMER_PORT_ENABLED=y
CONFIG_FMB_TIMER_GROUP=0
CONFIG_FMB_TIMER_INDEX=0
# CONFIG_FREERTOS_UNICORE is not set
CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
CONFIG_FREERTOS_CORETIMER_0=y
# CONFIG_FREERTOS_CORETIMER_1 is not set
CONFIG_FREERTOS_HZ=100
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y
# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set
# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set
CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1
CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y
# CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set
# CONFIG_FREERTOS_ASSERT_DISABLE is not set
CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536
CONFIG_FREERTOS_ISR_STACKSIZE=1536
# CONFIG_FREERTOS_LEGACY_HOOKS is not set
CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set
CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y
# CONFIG_FREERTOS_RUN_TIME_STATS_USING_CPU_CLK is not set
# CONFIG_FREERTOS_DEBUG_INTERNALS is not set
CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
CONFIG_HEAP_POISONING_DISABLED=y
# CONFIG_HEAP_POISONING_LIGHT is not set
# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set
CONFIG_HEAP_TRACING_OFF=y
# CONFIG_HEAP_TRACING_STANDALONE is not set
# CONFIG_HEAP_TRACING_TOHOST is not set
# CONFIG_HEAP_TRACING is not set
# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set
# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set
# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_LOG_COLORS=y
CONFIG_LWIP_LOCAL_HOSTNAME="squeezelite-esp32"
# CONFIG_LWIP_L2_TO_L3_COPY is not set
# CONFIG_LWIP_IRAM_OPTIMIZATION is not set
CONFIG_LWIP_TIMERS_ONDEMAND=y
CONFIG_LWIP_MAX_SOCKETS=16
# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set
CONFIG_LWIP_SO_REUSE=y
CONFIG_LWIP_SO_REUSE_RXTOALL=y
# CONFIG_LWIP_SO_RCVBUF is not set
CONFIG_LWIP_IP_FRAG=y
# CONFIG_LWIP_IP_REASSEMBLY is not set
# CONFIG_LWIP_STATS is not set
# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set
CONFIG_LWIP_ESP_GRATUITOUS_ARP=y
CONFIG_LWIP_GARP_TMR_INTERVAL=60
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
CONFIG_LWIP_DHCP_RESTORE_LAST_IP=y
CONFIG_LWIP_DHCPS_LEASE_UNIT=60
CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8
# CONFIG_LWIP_AUTOIP is not set
# CONFIG_LWIP_IPV6_AUTOCONFIG is not set
CONFIG_LWIP_NETIF_LOOPBACK=y
CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8
CONFIG_LWIP_MAX_ACTIVE_TCP=16
CONFIG_LWIP_MAX_LISTENING_TCP=16
CONFIG_LWIP_TCP_MAXRTX=12
CONFIG_LWIP_TCP_SYNMAXRTX=6
CONFIG_LWIP_TCP_MSS=1440
CONFIG_LWIP_TCP_MSL=60000
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=8192
CONFIG_LWIP_TCP_WND_DEFAULT=32768
CONFIG_LWIP_TCP_RECVMBOX_SIZE=32
CONFIG_LWIP_TCP_QUEUE_OOSEQ=y
# CONFIG_LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set
CONFIG_LWIP_TCP_OVERSIZE_MSS=y
# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set
# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set
# CONFIG_LWIP_WND_SCALE is not set
CONFIG_LWIP_MAX_UDP_PCBS=16
CONFIG_LWIP_UDP_RECVMBOX_SIZE=32
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072
CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set
# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set
CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF
# CONFIG_LWIP_PPP_SUPPORT is not set
# CONFIG_LWIP_MULTICAST_PING is not set
# CONFIG_LWIP_BROADCAST_PING is not set
CONFIG_LWIP_MAX_RAW_PCBS=16
CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1
CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000
# CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC is not set
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set
# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set
CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y
CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384
CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096
# CONFIG_MBEDTLS_DEBUG is not set
# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set
# CONFIG_MBEDTLS_CMAC_C is not set
CONFIG_MBEDTLS_HARDWARE_AES=y
# CONFIG_MBEDTLS_HARDWARE_MPI is not set
CONFIG_MBEDTLS_HARDWARE_SHA=y
CONFIG_MBEDTLS_HAVE_TIME=y
# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set
# CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT is not set
# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set
CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y
# CONFIG_MBEDTLS_TLS_DISABLED is not set
CONFIG_MBEDTLS_TLS_CLIENT=y
CONFIG_MBEDTLS_TLS_ENABLED=y
# CONFIG_MBEDTLS_PSK_MODES is not set
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y
CONFIG_MBEDTLS_SSL_RENEGOTIATION=y
# CONFIG_MBEDTLS_SSL_PROTO_SSL3 is not set
CONFIG_MBEDTLS_SSL_PROTO_TLS1=y
CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y
CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y
# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set
CONFIG_MBEDTLS_SSL_ALPN=y
CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y
CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y
CONFIG_MBEDTLS_AES_C=y
# CONFIG_MBEDTLS_CAMELLIA_C is not set
# CONFIG_MBEDTLS_DES_C is not set
CONFIG_MBEDTLS_RC4_DISABLED=y
# CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT is not set
# CONFIG_MBEDTLS_RC4_ENABLED is not set
# CONFIG_MBEDTLS_BLOWFISH_C is not set
# CONFIG_MBEDTLS_XTEA_C is not set
CONFIG_MBEDTLS_CCM_C=y
CONFIG_MBEDTLS_GCM_C=y
# CONFIG_MBEDTLS_RIPEMD160_C is not set
CONFIG_MBEDTLS_PEM_PARSE_C=y
CONFIG_MBEDTLS_PEM_WRITE_C=y
CONFIG_MBEDTLS_X509_CRL_PARSE_C=y
CONFIG_MBEDTLS_X509_CSR_PARSE_C=y
CONFIG_MBEDTLS_ECP_C=y
CONFIG_MBEDTLS_ECDH_C=y
CONFIG_MBEDTLS_ECDSA_C=y
CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y
CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y
CONFIG_MBEDTLS_ECP_NIST_OPTIM=y
CONFIG_MDNS_MAX_SERVICES=10
CONFIG_MQTT_PROTOCOL_311=y
CONFIG_MQTT_TRANSPORT_SSL=y
CONFIG_MQTT_TRANSPORT_WEBSOCKET=y
CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y
# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set
# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set
# CONFIG_MQTT_CUSTOM_OUTBOX is not set
CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y
# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set
# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set
# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set
# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set
CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y
# CONFIG_NEWLIB_NANO_FORMAT is not set
# CONFIG_OPENSSL_DEBUG is not set
CONFIG_OPENSSL_ASSERT_DO_NOTHING=y
# CONFIG_OPENSSL_ASSERT_EXIT is not set
CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5
CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
CONFIG_PTHREAD_STACK_MIN=768
# CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY is not set
# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set
CONFIG_PTHREAD_DEFAULT_CORE_1=y
CONFIG_PTHREAD_TASK_CORE_DEFAULT=1
CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread"
# CONFIG_SPI_FLASH_VERIFY_WRITE is not set
# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set
CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y
CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y
# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set
# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set
# CONFIG_SPI_FLASH_USE_LEGACY_IMPL is not set
CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y
CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y
CONFIG_SPIFFS_MAX_PARTITIONS=3
CONFIG_SPIFFS_CACHE=y
CONFIG_SPIFFS_CACHE_WR=y
# CONFIG_SPIFFS_CACHE_STATS is not set
CONFIG_SPIFFS_PAGE_CHECK=y
CONFIG_SPIFFS_GC_MAX_RUNS=10
# CONFIG_SPIFFS_GC_STATS is not set
CONFIG_SPIFFS_PAGE_SIZE=256
CONFIG_SPIFFS_OBJ_NAME_LEN=32
CONFIG_SPIFFS_USE_MAGIC=y
CONFIG_SPIFFS_USE_MAGIC_LENGTH=y
CONFIG_SPIFFS_META_LENGTH=4
CONFIG_SPIFFS_USE_MTIME=y
# CONFIG_SPIFFS_DBG is not set
# CONFIG_SPIFFS_API_DBG is not set
# CONFIG_SPIFFS_GC_DBG is not set
# CONFIG_SPIFFS_CACHE_DBG is not set
# CONFIG_SPIFFS_CHECK_DBG is not set
# CONFIG_SPIFFS_TEST_VISUALISATION is not set
CONFIG_NETIF_IP_LOST_TIMER_INTERVAL=120
CONFIG_TCPIP_LWIP=y
CONFIG_UNITY_ENABLE_FLOAT=y
CONFIG_UNITY_ENABLE_DOUBLE=y
# CONFIG_UNITY_ENABLE_COLOR is not set
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
# CONFIG_UNITY_ENABLE_FIXTURE is not set
# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set
CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y
CONFIG_VFS_SUPPORT_TERMIOS=y
CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1
CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128
CONFIG_WL_SECTOR_SIZE_512=y
# CONFIG_WL_SECTOR_SIZE_4096 is not set
CONFIG_WL_SECTOR_SIZE=512
# CONFIG_WL_SECTOR_MODE_PERF is not set
CONFIG_WL_SECTOR_MODE_SAFE=y
CONFIG_WL_SECTOR_MODE=1
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
CONFIG_WPA_MBEDTLS_CRYPTO=y
# CONFIG_DSP_ANSI is not set
CONFIG_DSP_OPTIMIZED=y
CONFIG_DSP_OPTIMIZATION=1
CONFIG_DSP_MAX_FFT_SIZE_512=y
# CONFIG_DSP_MAX_FFT_SIZE_1024 is not set
# CONFIG_DSP_MAX_FFT_SIZE_2048 is not set
# CONFIG_DSP_MAX_FFT_SIZE_4096 is not set
# CONFIG_DSP_MAX_FFT_SIZE_8192 is not set
# CONFIG_DSP_MAX_FFT_SIZE_16384 is not set
# CONFIG_DSP_MAX_FFT_SIZE_32768 is not set
CONFIG_DSP_MAX_FFT_SIZE=512
# CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set
# Deprecated options for backward compatibility
CONFIG_TOOLPREFIX="xtensa-esp32-elf-"
# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set
CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
CONFIG_LOG_BOOTLOADER_LEVEL=3
# CONFIG_APP_ROLLBACK_ENABLE is not set
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
CONFIG_FLASHMODE_QIO=y
# CONFIG_FLASHMODE_QOUT is not set
# CONFIG_FLASHMODE_DIO is not set
# CONFIG_FLASHMODE_DOUT is not set
# CONFIG_MONITOR_BAUD_9600B is not set
# CONFIG_MONITOR_BAUD_57600B is not set
CONFIG_MONITOR_BAUD_115200B=y
# CONFIG_MONITOR_BAUD_230400B is not set
# CONFIG_MONITOR_BAUD_921600B is not set
# CONFIG_MONITOR_BAUD_2MB is not set
# CONFIG_MONITOR_BAUD_OTHER is not set
CONFIG_MONITOR_BAUD_OTHER_VAL=115200
CONFIG_MONITOR_BAUD=115200
# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
CONFIG_OPTIMIZATION_LEVEL_RELEASE=y
# CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED is not set
CONFIG_OPTIMIZATION_ASSERTIONS_SILENT=y
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
# CONFIG_CXX_EXCEPTIONS is not set
CONFIG_STACK_CHECK_NONE=y
# CONFIG_STACK_CHECK_NORM is not set
# CONFIG_STACK_CHECK_STRONG is not set
# CONFIG_STACK_CHECK_ALL is not set
# CONFIG_STACK_CHECK is not set
# CONFIG_WARN_WRITE_STRINGS is not set
# CONFIG_DISABLE_GCC8_WARNINGS is not set
# CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY is not set
CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y
# CONFIG_BTDM_CONTROLLER_MODE_BTDM is not set
CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN=2
CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN=0
CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=0
CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=2
CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0
CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0
CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI=y
# CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4 is not set
CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=y
CONFIG_BLUEDROID_ENABLED=y
CONFIG_BTC_TASK_STACK_SIZE=3072
CONFIG_BLUEDROID_PINNED_TO_CORE_0=y
# CONFIG_BLUEDROID_PINNED_TO_CORE_1 is not set
CONFIG_BLUEDROID_PINNED_TO_CORE=0
CONFIG_BTU_TASK_STACK_SIZE=4096
# CONFIG_BLUEDROID_MEM_DEBUG is not set
CONFIG_CLASSIC_BT_ENABLED=y
CONFIG_A2DP_ENABLE=y
# CONFIG_HFP_ENABLE is not set
CONFIG_GATTS_ENABLE=y
# CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set
CONFIG_GATTS_SEND_SERVICE_CHANGE_AUTO=y
CONFIG_GATTS_SEND_SERVICE_CHANGE_MODE=0
CONFIG_GATTC_ENABLE=y
# CONFIG_GATTC_CACHE_NVS_FLASH is not set
CONFIG_BLE_SMP_ENABLE=y
# CONFIG_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set
# CONFIG_BLE_HOST_QUEUE_CONGESTION_CHECK is not set
CONFIG_SMP_ENABLE=y
CONFIG_BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT=30
CONFIG_ADC2_DISABLE_DAC=y
CONFIG_SPIRAM_SUPPORT=y
CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST=y
# CONFIG_MEMMAP_TRACEMEM is not set
# CONFIG_MEMMAP_TRACEMEM_TWOBANKS is not set
CONFIG_TRACEMEM_RESERVE_DRAM=0x0
# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set
CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y
CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4
# CONFIG_ULP_COPROC_ENABLED is not set
CONFIG_ULP_COPROC_RESERVE_MEM=0
CONFIG_BROWNOUT_DET=y
CONFIG_BROWNOUT_DET_LVL_SEL_0=y
# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set
CONFIG_BROWNOUT_DET_LVL=0
CONFIG_REDUCE_PHY_TX_POWER=y
CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y
# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set
# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set
# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set
# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set
# CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
CONFIG_MAIN_TASK_STACK_SIZE=8192
CONFIG_IPC_TASK_STACK_SIZE=1024
CONFIG_TIMER_TASK_STACK_SIZE=3584
CONFIG_CONSOLE_UART_DEFAULT=y
# CONFIG_CONSOLE_UART_CUSTOM is not set
# CONFIG_CONSOLE_UART_NONE is not set
CONFIG_CONSOLE_UART_NUM=0
CONFIG_CONSOLE_UART_BAUDRATE=115200
CONFIG_INT_WDT=y
CONFIG_INT_WDT_TIMEOUT_MS=800
CONFIG_INT_WDT_CHECK_CPU1=y
CONFIG_TASK_WDT=y
# CONFIG_TASK_WDT_PANIC is not set
CONFIG_TASK_WDT_TIMEOUT_S=5
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
# CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
CONFIG_SW_COEXIST_ENABLE=y
CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150
CONFIG_MB_MASTER_DELAY_MS_CONVERT=200
CONFIG_MB_QUEUE_LENGTH=20
CONFIG_MB_SERIAL_TASK_STACK_SIZE=2048
CONFIG_MB_SERIAL_BUF_SIZE=256
CONFIG_MB_SERIAL_TASK_PRIO=10
# CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT is not set
CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20
CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20
CONFIG_MB_CONTROLLER_STACK_SIZE=4096
CONFIG_MB_EVENT_QUEUE_TIMEOUT=20
CONFIG_MB_TIMER_PORT_ENABLED=y
CONFIG_MB_TIMER_GROUP=0
CONFIG_MB_TIMER_INDEX=0
CONFIG_SUPPORT_STATIC_ALLOCATION=y
# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set
CONFIG_TIMER_TASK_PRIORITY=1
CONFIG_TIMER_TASK_STACK_DEPTH=2432
CONFIG_TIMER_QUEUE_LENGTH=10
# CONFIG_L2_TO_L3_COPY is not set
# CONFIG_USE_ONLY_LWIP_SELECT is not set
CONFIG_ESP_GRATUITOUS_ARP=y
CONFIG_GARP_TMR_INTERVAL=60
CONFIG_TCPIP_RECVMBOX_SIZE=32
CONFIG_TCP_MAXRTX=12
CONFIG_TCP_SYNMAXRTX=6
CONFIG_TCP_MSS=1440
CONFIG_TCP_MSL=60000
CONFIG_TCP_SND_BUF_DEFAULT=8192
CONFIG_TCP_WND_DEFAULT=32768
CONFIG_TCP_RECVMBOX_SIZE=32
CONFIG_TCP_QUEUE_OOSEQ=y
# CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set
CONFIG_TCP_OVERSIZE_MSS=y
# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set
# CONFIG_TCP_OVERSIZE_DISABLE is not set
CONFIG_UDP_RECVMBOX_SIZE=32
CONFIG_TCPIP_TASK_STACK_SIZE=3072
CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set
# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set
CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF
# CONFIG_PPP_SUPPORT is not set
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
CONFIG_ESP32_PTHREAD_STACK_MIN=768
# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY is not set
# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set
CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1=y
CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=1
CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread"
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set
# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set
CONFIG_IP_LOST_TIMER_INTERVAL=120
CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y
CONFIG_SUPPORT_TERMIOS=y
# End of deprecated options

View File

@@ -1,10 +1,15 @@
idf_component_register(
INCLUDE_DIRS . ./inc inc/alac inc/FLAC inc/helix-aac inc/mad inc/ogg inc/opus inc/opusfile inc/resample16 inc/soxr inc/vorbis
INCLUDE_DIRS . ./inc inc/alac inc/FLAC inc/helix-aac inc/mad inc/ogg inc/opus inc/opusfile inc/resample16 inc/soxr inc/vorbis
)
if (DEFINED AAC_DISABLE_SBR)
add_prebuilt_library(libhelix-aac lib/libhelix-aac.a )
else ()
add_prebuilt_library(libhelix-aac lib/libhelix-aac-sbr.a )
endif()
add_prebuilt_library(libmad lib/libmad.a)
add_prebuilt_library(libFLAC lib/libFLAC.a )
add_prebuilt_library(libhelix-aac lib/libhelix-aac.a )
add_prebuilt_library(libvorbisidec lib/libvorbisidec.a )
add_prebuilt_library(libogg lib/libogg.a )
add_prebuilt_library(libalac lib/libalac.a )

View File

@@ -19,7 +19,7 @@ extern "C" {
struct alac_codec_s *alac_create_decoder(int magic_cookie_size, unsigned char *magic_cookie,
unsigned char *sample_size, unsigned *sample_rate,
unsigned char *channels);
unsigned char *channels, unsigned int *block_size);
void alac_delete_decoder(struct alac_codec_s *codec);
bool alac_to_pcm(struct alac_codec_s *codec, unsigned char* input,
unsigned char *output, char channels, unsigned *out_frames);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,3 @@
idf_component_register(SRC_DIRS . core core/ifaces fonts
INCLUDE_DIRS . fonts core
REQUIRES platform_config tools esp_common

View File

@@ -1,6 +1,7 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
* 2020 Philippe G.
* 2021 Mumpf and Harry1999
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
@@ -10,333 +11,293 @@
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include <esp_heap_caps.h>
#include <esp_log.h>
#include "gds.h"
#include "gds_private.h"
//#define SHADOW_BUFFER
#define PAGE_BLOCK 1024
#define SHADOW_BUFFER
#define USE_IRAM
#define PAGE_BLOCK 2048
#define ENABLE_WRITE 0x2c
#define MADCTL_MX 0x40
#define TFT_RGB_BGR 0x08
#define min(a,b) (((a) < (b)) ? (a) : (b))
static char TAG[] = "ILI9341";
#define L1_CMD_NOP 0X00
#define L1_CMD_SOFTWARE_RESET 0X01
#define L1_CMD_READ_DISPLAY_IDENTIFICATION_INFORMATION 0X04
#define L1_CMD_READ_DISPLAY_STATUS 0X09
#define L1_CMD_READ_DISPLAY_POWER_MODE 0X0A
#define L1_CMD_READ_DISPLAY_MADCTL 0X0B
#define L1_CMD_READ_DISPLAY_PIXEL_FORMAT 0X0C
#define L1_CMD_READ_DISPLAY_IMAGE_FORMAT 0X0D
#define L1_CMD_READ_DISPLAY_SIGNAL_MODE 0X0E
#define L1_CMD_READ_DISPLAY_SELF_DIAGNOSTIC_RESULT 0X0F
#define L1_CMD_ENTER_SLEEP_MODE 0X10
#define L1_CMD_SLEEP_OUT 0X11
#define L1_CMD_PARTIAL_MODE_ON 0X12
#define L1_CMD_NORMAL_DISPLAY_MODE_ON 0X13
#define L1_CMD_DISPLAY_INVERSION_OFF 0X20
#define L1_CMD_DISPLAY_INVERSION_ON 0X21
#define L1_CMD_GAMMA_SET 0X26
#define L1_CMD_DISPLAY_OFF 0X28
#define L1_CMD_DISPLAY_ON 0X29
#define L1_CMD_COLUMN_ADDRESS_SET 0X2A
#define L1_CMD_PAGE_ADDRESS_SET 0X2B
#define L1_CMD_MEMORY_WRITE 0X2C
#define L1_CMD_COLOR_SET 0X2D
#define L1_CMD_MEMORY_READ 0X2E
#define L1_CMD_PARTIAL_AREA 0X30
#define L1_CMD_VERTICAL_SCROLLING_DEFINITION 0X33
#define L1_CMD_TEARING_EFFECT_LINE_OFF 0X34
#define L1_CMD_TEARING_EFFECT_LINE_ON 0X35
#define L1_CMD_MEMORY_ACCESS_CONTROL 0X36
#define L1_CMD_VERTICAL_SCROLLING_START_ADDRESS 0X37
#define L1_CMD_IDLE_MODE_OFF 0X38
#define L1_CMD_IDLE_MODE_ON 0X39
#define L1_CMD_COLMOD_PIXEL_FORMAT_SET 0X3A
#define L1_CMD_WRITE_MEMORY_CONTINUE 0X3C
#define L1_CMD_READ_MEMORY_CONTINUE 0X3E
#define L1_CMD_SET_TEAR_SCANLINE 0X44
#define L1_CMD_GET_SCANLINE 0X45
#define L1_CMD_WRITE_DISPLAY_BRIGHTNESS 0X51
#define L1_CMD_READ_DISPLAY_BRIGHTNESS 0X52
#define L1_CMD_WRITE_CTRL_DISPLAY 0X53
#define L1_CMD_READ_CTRL_DISPLAY 0X54
#define L1_CMD_WRITE_CONTENT_ADAPTIVE_BRIGHTNESS_CONTROL 0X55
#define L1_CMD_READ_CONTENT_ADAPTIVE_BRIGHTNESS_CONTROL 0X56
#define L1_CMD_WRITE_CABC_MINIMUM_BRIGHTNESS 0X5E
#define L1_CMD_READ_CABC_MINIMUM_BRIGHTNESS 0X5F
#define L1_CMD_READ_ID1 0XDA
#define L1_CMD_READ_ID2 0XDB
#define L1_CMD_READ_ID3 0XDC
#define L2_CMD_RGB_INTERFACE_SIGNAL_CONTROL 0XB0
#define L2_CMD_FRAME_RATE_CONTROL_IN_NORMAL_MODE_FULL_COLORS 0XB1
#define L2_CMD_FRAME_RATE_CONTROL_IN_IDLE_MODE_8_COLORS 0XB2
#define L2_CMD_FRAME_RATE_CONTROL_IN_PARTIAL_MODE_FULL_COLORS 0XB3
#define L2_CMD_DISPLAY_INVERSION_CONTROL 0XB4
#define L2_CMD_BLANKING_PORCH_CONTROL 0XB5
#define L2_CMD_DISPLAY_FUNCTION_CONTROL 0XB6
#define L2_CMD_ENTRY_MODE_SET 0XB7
#define L2_CMD_BACKLIGHT_CONTROL_1 0XB8
#define L2_CMD_BACKLIGHT_CONTROL_2 0XB9
#define L2_CMD_BACKLIGHT_CONTROL_3 0XBA
#define L2_CMD_BACKLIGHT_CONTROL_4 0XBB
#define L2_CMD_BACKLIGHT_CONTROL_5 0XBC
#define L2_CMD_BACKLIGHT_CONTROL_7 0XBE
#define L2_CMD_BACKLIGHT_CONTROL_8 0XBF
#define L2_CMD_POWER_CONTROL_1 0XC0
#define L2_CMD_POWER_CONTROL_2 0XC1
#define L2_CMD_VCOM_CONTROL_1 0XC5
#define L2_CMD_VCOM_CONTROL_2 0XC7
#define L2_CMD_NV_MEMORY_WRITE 0XD0
#define L2_CMD_NV_MEMORY_PROTECTION_KEY 0XD1
#define L2_CMD_NV_MEMORY_STATUS_READ 0XD2
#define L2_CMD_READ_ID4 0XD3
#define L2_CMD_POSITIVE_GAMMA_CORRECTION 0XE0
#define L2_CMD_NEGATIVE_GAMMA_CORRECTION 0XE1
#define L2_CMD_DIGITAL_GAMMA_CONTROL_1 0XE2
#define L2_CMD_DIGITAL_GAMMA_CONTROL_2 0XE3
#define L2_CMD_INTERFACE_CONTROL 0XF6
/*
The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct.
*/
typedef struct {
uint8_t cmd;
uint8_t data[16];
uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;
static const lcd_init_cmd_t ili_init_cmds[]={
/* Power contorl B, power control = 0, DC_ENA = 1 */
{0xCF, {0x00, 0x83, 0X30}, 3},
/* Power on sequence control,
* cp1 keeps 1 frame, 1st frame enable
* vcl = 0, ddvdh=3, vgh=1, vgl=2
* DDVDH_ENH=1
*/
{0xED, {0x64, 0x03, 0X12, 0X81}, 4},
/* Driver timing control A,
* non-overlap=default +1
* EQ=default - 1, CR=default
* pre-charge=default - 1
*/
{0xE8, {0x85, 0x01, 0x79}, 3},
/* Power control A, Vcore=1.6V, DDVDH=5.6V */
{0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
/* Pump ratio control, DDVDH=2xVCl */
{0xF7, {0x20}, 1},
/* Driver timing control, all=0 unit */
{0xEA, {0x00, 0x00}, 2},
/* Power control 1, GVDD=4.75V */
{0xC0, {0x26}, 1},
/* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */
{0xC1, {0x11}, 1},
/* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */
{0xC5, {0x35, 0x3E}, 2},
/* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */
{0xC7, {0xBE}, 1},
/* Memory access contorl, MX=MY=0, MV=1, ML=0, BGR=1, MH=0 */
{0x36, {0x28}, 1},
/* Pixel format, 16bits/pixel for RGB/MCU interface */
{0x3A, {0x55}, 1},
/* Frame rate control, f=fosc, 70Hz fps */
{0xB1, {0x00, 0x1B}, 2},
/* Enable 3G, disabled */
{0xF2, {0x08}, 1},
/* Gamma set, curve 1 */
{0x26, {0x01}, 1},
/* Positive gamma correction */
{0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
/* Negative gamma correction */
{0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
/* Column address set, SC=0, EC=0xEF */
{0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
/* Page address set, SP=0, EP=0x013F */
{0x2B, {0x00, 0x00, 0x01, 0x3f}, 4},
/* Memory write */
{0x2C, {0}, 0},
/* Entry mode set, Low vol detect disabled, normal display */
{0xB7, {0x07}, 1},
/* Display function control */
{0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
/* Sleep out */
{0x11, {0}, 0x80},
/* Display on */
{0x29, {0}, 0x80},
{0, {0}, 0xff},
};
//To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more memory use,
//but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this.
#define PARALLEL_LINES 16
enum { ILI9341, ILI9341_24 }; //ILI9341_24 for future use...
struct PrivateSpace {
uint8_t *iRAM, *Shadowbuffer;
uint8_t ReMap, PageSize;
uint8_t Offset;
struct {
uint16_t Height, Width;
} Offset;
uint8_t MADCtl, PageSize;
uint8_t Model;
};
// Functions are not declared to minimize # of lines
static void WriteDataByte( struct GDS_Device* Device, uint8_t Data ) {
Device->WriteData( Device, &Data, 1);
static void WriteByte( struct GDS_Device* Device, uint8_t Data ) {
Device->WriteData( Device, &Data, 1 );
}
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, L1_CMD_COLUMN_ADDRESS_SET );
Device->WriteData( Device, &Start, 1 );
Device->WriteData( Device, &End, 1 );
}
static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, L1_CMD_PAGE_ADDRESS_SET );
Device->WriteData( Device, &Start, 1 );
Device->WriteData( Device, &End, 1 );
static void SetColumnAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
uint32_t Addr = __builtin_bswap16(Start) | (__builtin_bswap16(End) << 16);
Device->WriteCommand( Device, 0x2A );
Device->WriteData( Device, (uint8_t*) &Addr, 4 );
}
static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
uint32_t Addr = __builtin_bswap16(Start) | (__builtin_bswap16(End) << 16);
Device->WriteCommand( Device, 0x2B );
Device->WriteData( Device, (uint8_t*) &Addr, 4 );
}
static void Update( struct GDS_Device* Device ) {
static void Update16( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
//SetColumnAddress( Device, Private->Offset, Private->Offset + Device->Width / 4 - 1);
SetColumnAddress( Device, Private->Offset, Private->Offset + Device->Width - 1);
#ifdef SHADOW_BUFFER
uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
bool dirty = false;
uint32_t *optr = (uint32_t*) Private->Shadowbuffer, *iptr = (uint32_t*) Device->Framebuffer;
int FirstCol = Device->Width / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
for (int r = 0, page = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width always / by 2)
for (int c = Device->Width / 2 / 2; --c >= 0;) {
for (int r = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width is always a multiple of 2)
for (int c = 0; c < Device->Width / 2; c++, iptr++, optr++) {
if (*optr != *iptr) {
dirty = true;
*optr = *iptr;
if (c < FirstCol) FirstCol = c;
if (c > LastCol) LastCol = c;
if (FirstRow < 0) FirstRow = r;
LastRow = r;
}
iptr++; optr++;
}
// one line done, check for page boundary
if (++page == Private->PageSize) {
if (dirty) {
uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Private->Shadowbuffer + (r - page + 1) * Device->Width / 2);
SetRowAddress( Device, r - page + 1, r );
for (int i = page * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
//memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
Device->WriteCommand( Device, 0x5c );
Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
dirty = false;
// wait for a large enough window - careful that window size might increase by more than a line at once !
if (FirstRow < 0 || ((LastCol - FirstCol + 1) * (r - FirstRow + 1) * 4 < PAGE_BLOCK && r != Device->Height - 1)) continue;
FirstCol *= 2;
LastCol = LastCol * 2 + 1;
SetRowAddress( Device, FirstRow + Private->Offset.Height, LastRow + Private->Offset.Height);
SetColumnAddress( Device, FirstCol + Private->Offset.Width, LastCol + Private->Offset.Width );
Device->WriteCommand( Device, ENABLE_WRITE );
int ChunkSize = (LastCol - FirstCol + 1) * 2;
// own use of IRAM has not proven to be much better than letting SPI do its copy
if (Private->iRAM) {
uint8_t *optr = Private->iRAM;
for (int i = FirstRow; i <= LastRow; i++) {
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize);
optr += ChunkSize;
if (optr - Private->iRAM <= (PAGE_BLOCK - ChunkSize) && i < LastRow) continue;
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
optr = Private->iRAM;
}
page = 0;
} else for (int i = FirstRow; i <= LastRow; i++) {
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize );
}
FirstCol = Device->Width / 2; LastCol = 0;
FirstRow = -1;
}
#else
for (int r = 0; r < Device->Height; r += Private->PageSize) {
SetRowAddress( Device, r, r + Private->PageSize - 1 );
Device->WriteCommand( Device, L1_CMD_MEMORY_WRITE );
// always update by full lines
SetColumnAddress( Device, Private->Offset.Width, Device->Width - 1);
for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
int Height = min(Private->PageSize, Device->Height - r);
SetRowAddress( Device, Private->Offset.Height + r, Private->Offset.Height + r + Height - 1 );
Device->WriteCommand(Device, ENABLE_WRITE);
if (Private->iRAM) {
uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Device->Framebuffer + r * Device->Width / 2);
for (int i = Private->PageSize * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
//memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 );
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 2 );
} else {
Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
}
}
#endif
}
//Bit Name Description
//--- --------------------------- ------------------------------------------------------
//MY Row Address Order MCU to memory write/read direction.
//MX Column Address Order MCU to memory write/read direction.
//MV Row / Column Exchange MCU to memory write/read direction.
//ML Vertical Refresh Order LCD vertical refresh direction control.
//BGR RGB-BGR Order Color selector switch control
// (0=RGB color filter panel, 1=BGR color filter panel)
//MH Horizontal Refresh ORDER LCD horizontal refreshing direction control.
// Bits 17-0
// XX XX XX XX XX XX XX XX XX XX MY MX MV ML BGR MH 0 0
typedef enum {
MAC_BIT_MH=2,
MAC_BIT_BGR,
MAC_BIT_ML,
MAC_BIT_MV,
MAC_BIT_MX,
MAC_BIT_MY,
} mac_bits;
static void Update24( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
uint16_t set_mac_bit(mac_bits bit, uint16_t val){
return (1 << bit) | val;
}
uint16_t unset_mac_bit(mac_bits bit, uint16_t val){
return ~(1 << bit) & val;
#ifdef SHADOW_BUFFER
uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
int FirstCol = (Device->Width * 3) / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
for (int r = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width always / by 2)
for (int c = 0; c < (Device->Width * 3) / 2; c++, optr++, iptr++) {
if (*optr != *iptr) {
*optr = *iptr;
if (c < FirstCol) FirstCol = c;
if (c > LastCol) LastCol = c;
if (FirstRow < 0) FirstRow = r;
LastRow = r;
}
}
// do we have enough to send (cols are divided by 3/2)
if (FirstRow < 0 || ((((LastCol - FirstCol + 1) * 2 ) / 3) * (r - FirstRow + 1) * 4 < PAGE_BLOCK && r != Device->Height - 1)) continue;
FirstCol = (FirstCol * 2) / 3;
LastCol = (LastCol * 2 + 1 ) / 3;
SetRowAddress( Device, FirstRow + Private->Offset.Height, LastRow + Private->Offset.Height);
SetColumnAddress( Device, FirstCol + Private->Offset.Width, LastCol + Private->Offset.Width );
Device->WriteCommand( Device, ENABLE_WRITE );
int ChunkSize = (LastCol - FirstCol + 1) * 3;
// own use of IRAM has not proven to be much better than letting SPI do its copy
if (Private->iRAM) {
uint8_t *optr = Private->iRAM;
for (int i = FirstRow; i <= LastRow; i++) {
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize);
optr += ChunkSize;
if (optr - Private->iRAM <= (PAGE_BLOCK - ChunkSize) && i < LastRow) continue;
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
optr = Private->iRAM;
}
} else for (int i = FirstRow; i <= LastRow; i++) {
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize );
}
FirstCol = (Device->Width * 3) / 2; LastCol = 0;
FirstRow = -1;
}
#else
// always update by full lines
SetColumnAddress( Device, Private->Offset.Width, Device->Width - 1);
for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
int Height = min(Private->PageSize, Device->Height - r);
SetRowAddress( Device, Private->Offset.Height + r, Private->Offset.Height + r + Height - 1 );
Device->WriteCommand(Device, ENABLE_WRITE);
if (Private->iRAM) {
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 3 );
} else {
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
}
}
#endif
}
static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->ReMap = HFlip ? (Private->ReMap & ~(1 << MAC_BIT_MX)) : (Private->ReMap | (1 << MAC_BIT_MX));
Private->ReMap = VFlip ? (Private->ReMap | (1 << MAC_BIT_MY)) : (Private->ReMap & ~(1 << MAC_BIT_MY));
Device->WriteCommand( Device, L1_CMD_MEMORY_ACCESS_CONTROL );
Device->WriteData( Device, &Private->ReMap, 1 );
WriteDataByte(Device,0x00);
ESP_LOGI(TAG, "SetLayout 197 HFlip=%d VFlip=%d Rotate=%d (1=true)", HFlip, VFlip, Rotate);
// D/CX RDX WRX D17-8 D7 D6 D5 D4 D3 D2 D1 D0 HEX
//Command 0 1 ↑ XX 0 0 1 1 0 1 1 0 36h
//Parameter 1 1 ↑ XX MY MX MV ML BGR MH 0 0 00
//Orientation 0: MADCtl = 0x80 = 1000 0000 (MY=1)
if ((Device->Height)>(Device->Width)){ //Resolution = 320x240
Private->MADCtl = (1 << 7); // 0x80 = default (no Rotation an no Flip)
if (HFlip) { //Flip Horizontal
int a = Private->MADCtl;
Private->MADCtl = (a ^ (1 << 7));
}
if (Rotate) { //Rotate 180 degr.
int a = Private->MADCtl;
a = (a ^ (1 << 7));
Private->MADCtl = (a ^ (1 << 6));
}
if (VFlip) { //Flip Vertical
int a = Private->MADCtl;
Private->MADCtl = (a ^ (1 << 6));
}
} else { //Resolution = 240x320
Private->MADCtl = (1 << 5); // 0x20 = default (no Rotation an no Flip)
if (HFlip) { //Flip Horizontal
int a = Private->MADCtl;
Private->MADCtl = (a ^ (1 << 6));
}
if (Rotate) { //Rotate 180 degr.
int a = Private->MADCtl;
a = (a ^ (1 << 7));
Private->MADCtl = (a ^ (1 << 6));
}
if (VFlip) { //Flip Vertical
int a = Private->MADCtl;
Private->MADCtl = (a ^ (1 << 7));
}
}
ESP_LOGI(TAG, "SetLayout 255 Private->MADCtl=%hhu", Private->MADCtl);
Device->WriteCommand( Device, 0x36 );
WriteByte( Device, Private->MADCtl );
#ifdef SHADOW_BUFFER
// force a full refresh (almost ...)
memset(Private->Shadowbuffer, 0xAA, Device->FramebufferSize);
#endif
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, L1_CMD_DISPLAY_ON ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, L1_CMD_DISPLAY_OFF ); }
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x29 ); } //DISPON =0x29
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x28 ); } //DISPOFF=0x28
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
Device->WriteCommand( Device, L1_CMD_WRITE_DISPLAY_BRIGHTNESS );
uint8_t loc_contrast = (uint8_t)((float)Contrast/5.0f* 255.0f);
Device->WriteData( Device, &loc_contrast , 1 );
WriteDataByte(Device,0x00);
Device->WriteCommand( Device, 0x51 );
WriteByte( Device, Contrast );
Device->SetContrast = NULL;
GDS_SetContrast( Device, Contrast );
Device->SetContrast = SetContrast; // 0x00 value means the lowest brightness and 0xFF value means the highest brightness.
}
static bool Init( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
int Depth = (Device->Depth + 8 - 1) / 8;
// Private->Offset = (480 - Device->Width) / 4 / 2;
// find a page size that is not too small is an integer of height
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
Private->PageSize = Device->Height / (Device->Height / Private->PageSize) ;
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width * Depth));
#ifdef SHADOW_BUFFER
// Private->Shadowbuffer = malloc( Device->FramebufferSize );
// memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
Private->Shadowbuffer = malloc( Device->FramebufferSize );
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
#ifdef USE_IRAM
Private->iRAM = heap_caps_malloc( (Private->PageSize + 1) * Device->Width * Depth, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
#endif
Private->iRAM =NULL;
//Private->iRAM =heap_caps_malloc(320*PARALLEL_LINES*sizeof(uint16_t), MALLOC_CAP_DMA);
ESP_LOGI(TAG, "ILI9341 with bit default-depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM);
//ESP_LOGI(TAG, "ILI9341 with offset %u, page %u, iRAM %p", Private->Offset, Private->PageSize, Private->iRAM);
ESP_LOGI(TAG, "ILI9341 ");
// Sleepout + Booster
Device->WriteCommand( Device, 0x11 );
// need to be off and disable display RAM
Device->DisplayOff( Device );
int cmd=0;
//Send all the commands
while (ili_init_cmds[cmd].databytes!=0xff) {
Device->WriteCommand( Device, ili_init_cmds[cmd].cmd );
Device->WriteData(Device,ili_init_cmds[cmd].data,ili_init_cmds[cmd].databytes&0x1F);
if (ili_init_cmds[cmd].databytes&0x80) {
vTaskDelay(100 / portTICK_RATE_MS);
}
cmd++;
}
// set flip modes & contrast
GDS_SetContrast( Device, 0x7f );
Device->SetLayout( Device, false, false, false );
// set screen depth (16/18) *** INTERFACE PIXEL FORMAT: 0x66=18 bit; 0x55=16 bit
Device->WriteCommand( Device, 0x3A );
if (Private->Model == ILI9341_24) WriteByte( Device, Device->Depth == 24 ? 0x66 : 0x55 );
else WriteByte( Device, Device->Depth == 24 ? 0x66 : 0x55 );
ESP_LOGI(TAG, "ILI9341_Init 312 device-depth %u, 0x66/0x55=0x%X", Device->Depth, Device->Depth == 24 ? 0x66 : 0x55);
// no Display Inversion (INVOFF=0x20 INVON=0x21)
Device->WriteCommand( Device, 0x20 );
//Gamma Correction: Enable next two line and enabel one of the Test0x Section... or build you own 15 Parameter...
Device->WriteCommand( Device, 0xF2 ); WriteByte( Device, 0x03 ); // 3Gamma Function: Disable = default (0x02), Enable (0x03)
Device->WriteCommand( Device, 0x26 ); WriteByte( Device, 0x01 ); // Gamma curve selected (0x01, 0x02, 0x04, 0x08) - A maximum of 4 fixed gamma curves can be selected
//Gamma Correction Test01
Device->WriteCommand( Device, 0xE0 ); // Positive Gamma Correction (15 Parameter)
WriteByte( Device, 0x0F ); WriteByte( Device, 0x31 ); WriteByte( Device, 0x2B ); WriteByte( Device, 0x0C ); WriteByte( Device, 0x0E );
WriteByte( Device, 0x08 ); WriteByte( Device, 0x4E ); WriteByte( Device, 0xF1 ); WriteByte( Device, 0x37 ); WriteByte( Device, 0x07 );
WriteByte( Device, 0x10 ); WriteByte( Device, 0x03 ); WriteByte( Device, 0x0E ); WriteByte( Device, 0x09 ); WriteByte( Device, 0x00 );
Device->WriteCommand( Device, 0xE1 ); // Negative Gamma Correction (15 Parameter)
WriteByte( Device, 0x00 ); WriteByte( Device, 0x0E ); WriteByte( Device, 0x14 ); WriteByte( Device, 0x03 ); WriteByte( Device, 0x11 );
WriteByte( Device, 0x07 ); WriteByte( Device, 0x31 ); WriteByte( Device, 0xC1 ); WriteByte( Device, 0x48 ); WriteByte( Device, 0x08 );
WriteByte( Device, 0x0F ); WriteByte( Device, 0x0C ); WriteByte( Device, 0x31 ); WriteByte( Device, 0x36 ); WriteByte( Device, 0x0F );
// gone with the wind
Device->DisplayOn( Device );
@@ -345,21 +306,36 @@ static bool Init( struct GDS_Device* Device ) {
return true;
}
static const struct GDS_Device ILI9341 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
static const struct GDS_Device ILI9341_X = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff,
.SetLayout = SetLayout,
.Update = Update, .Init = Init,
.Update = Update16, .Init = Init,
.Mode = GDS_RGB565, .Depth = 16,
};
struct GDS_Device* ILI9341_Detect(char *Driver, struct GDS_Device* Device) {
if (!strcasestr(Driver, "ILI9341")) return NULL;
uint8_t Model;
int Depth=16; // 16bit colordepth
if (strcasestr(Driver, "ILI9341")) Model = ILI9341;
else if (strcasestr(Driver, "ILI9341_24")) Model = ILI9341_24; //for future use...
else return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = ILI9341;
Device->Depth = 4;
*Device = ILI9341_X;
sscanf(Driver, "%*[^:]:%u", &Depth); // NVS-Parameter driver=ILI9341[:16|18]
struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private;
Private->Model = Model;
ESP_LOGI(TAG, "ILI9341_Detect 391 Driver= %s Depth=%d", Driver, Depth);
if (Depth == 18) {
Device->Mode = GDS_RGB888;
Device->Depth = 24;
Device->Update = Update24;
}
if (Model == ILI9341_24) Device->SetContrast = SetContrast;
return Device;
}

View File

@@ -147,23 +147,28 @@ static bool Init( struct GDS_Device* Device ) {
Private->ReMap = 0;
Device->SetLayout( Device, false, false, false);
// set Display Enhancement
Device->WriteCommand( Device, 0xB4 );
WriteDataByte( Device, 0xA0 );
WriteDataByte( Device, 0xB5 );
// set Clocks
Device->WriteCommand( Device, 0xB3 );
WriteDataByte( Device, 0x91 );
WriteDataByte( Device, 0xB2 ); // 0x91 seems to be common but is too slow for 5.5'
// set MUX
Device->WriteCommand( Device, 0xCA );
WriteDataByte( Device, Device->Height - 1 );
// phase 1 & 2 period (needed?)
// phase 1 & 2 period
Device->WriteCommand( Device, 0xB1 );
WriteDataByte( Device, 0xE2 );
WriteDataByte( Device, 0xE3 ); // 0xE2 was recommended
// set pre-charge V (needed?°)
// set pre-charge V
Device->WriteCommand( Device, 0xBB );
WriteDataByte( Device, 0x1F );
WriteDataByte( Device, 0x0F); // 0x1F causes column interferences
// set COM deselect voltage (needed?)
// set COM deselect voltage
Device->WriteCommand( Device, 0xBE );
WriteDataByte( Device, 0x07 );

View File

@@ -238,9 +238,9 @@ void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { if (Device->SetLayout) Device->SetLayout( Device, HFlip, VFlip, Rotate ); }
void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
int GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
int GDS_GetDepth( struct GDS_Device* Device ) { return Device->Depth; }
int GDS_GetMode( struct GDS_Device* Device ) { return Device->Mode; }
int GDS_GetWidth( struct GDS_Device* Device ) { return Device ? Device->Width : 0; }
int GDS_GetHeight( struct GDS_Device* Device ) { return Device ? Device->Height : 0; }
int GDS_GetDepth( struct GDS_Device* Device ) { return Device ? Device->Depth : 0; }
int GDS_GetMode( struct GDS_Device* Device ) { return Device ? Device->Mode : 0; }
void GDS_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); }
void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); }

View File

@@ -52,15 +52,16 @@ static const char *known_drivers[] = {"SH1106",
"SSD1327",
"SSD1675",
"SSD1351",
"ST77xx",
"ST7735",
"ST7789",
"ILI9341",
NULL
};
static void displayer_task(void *args);
struct GDS_Device *display;
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect;
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, NULL };
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect;
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, NULL };
/****************************************************************************************
*
@@ -134,7 +135,7 @@ void display_init(char *welcome) {
displayer.by = 2;
displayer.pause = 3600;
displayer.speed = 33;
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", DISPLAYER_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "common_displayer", DISPLAYER_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
// set lines for "fixed" text mode
GDS_TextSetFontAuto(display, 1, GDS_FONT_LINE_1, -3);
@@ -381,14 +382,14 @@ void displayer_control(enum displayer_cmd_e cmd, ...) {
/****************************************************************************************
*
*/
bool display_is_valid_driver(char * driver){
bool display_is_valid_driver(const char * driver){
return display_conf_get_driver_name(driver)!=NULL;
}
/****************************************************************************************
*
*/
const char *display_conf_get_driver_name(char * driver){
const char *display_conf_get_driver_name(const char * driver){
for(uint8_t i=0;known_drivers[i]!=NULL && strlen(known_drivers[i])>0;i++ ){
if(strcasestr(driver,known_drivers[i])){
return known_drivers[i];

View File

@@ -32,8 +32,8 @@ enum displayer_time_e { DISPLAYER_ELAPSED, DISPLAYER_REMAINING };
enum display_bus_cmd_e { DISPLAY_BUS_TAKE, DISPLAY_BUS_GIVE };
bool (*display_bus)(void *from, enum display_bus_cmd_e cmd);
const char *display_conf_get_driver_name(char * driver);
bool display_is_valid_driver(char * driver);
const char *display_conf_get_driver_name(const char * driver);
bool display_is_valid_driver(const char * driver);
void displayer_scroll(char *string, int speed, int pause);
void displayer_control(enum displayer_cmd_e cmd, ...);

View File

@@ -7,7 +7,6 @@
*/
#include "bt_app_core.h"
#include <stdint.h>
#include "esp_system.h"
#include <string.h>

View File

@@ -15,9 +15,29 @@
#include <stdio.h>
#define BT_APP_CORE_TAG "BT_APP_CORE"
#define BT_APP_SIG_WORK_DISPATCH (0x01)
/* A2DP global state */
enum {
APP_AV_STATE_IDLE,
APP_AV_STATE_DISCOVERING,
APP_AV_STATE_DISCOVERED,
APP_AV_STATE_UNCONNECTED,
APP_AV_STATE_CONNECTING,
APP_AV_STATE_CONNECTED,
APP_AV_STATE_DISCONNECTING,
};
/* sub states of APP_AV_STATE_CONNECTED */
enum {
APP_AV_MEDIA_STATE_IDLE,
APP_AV_MEDIA_STATE_STARTING,
APP_AV_MEDIA_STATE_STARTED,
APP_AV_MEDIA_STATE_STOPPING,
APP_AV_MEDIA_STATE_WAIT_DISCONNECT
};
extern int bt_app_source_get_a2d_state();
extern int bt_app_source_get_media_state();
/**
* @brief handler for the dispatched work
*/

View File

@@ -62,14 +62,15 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param);
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param);
/* avrc TG event handler */
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param);
static void volume_set_by_local_host(uint8_t volume);
static void volume_set_by_local_host(int value, bool is_step);
static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter);
static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
static const char *s_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"};
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap;
static _lock_t s_volume_lock;
static uint8_t s_volume = 0;
static int s_volume, abs_volume, sink_volume;
static bool s_volume_notify;
static enum { AUDIO_IDLE, AUDIO_CONNECTED, AUDIO_PLAYING } s_audio = AUDIO_IDLE;
@@ -89,16 +90,14 @@ static EXT_RAM_ATTR struct {
static void bt_volume_up(bool pressed) {
if (!pressed) return;
// volume UP/DOWN buttons are not supported by iPhone/Android
volume_set_by_local_host(s_volume < 127-3 ? s_volume + 3 : 127);
volume_set_by_local_host(+3, true);
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
ESP_LOGD(BT_AV_TAG, "BT volume up %u", s_volume);
}
static void bt_volume_down(bool pressed) {
if (!pressed) return;
// volume UP/DOWN buttons are not supported by iPhone/Android
volume_set_by_local_host(s_volume > 3 ? s_volume - 3 : 0);
volume_set_by_local_host(-3, true);
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
}
@@ -134,12 +133,14 @@ static void bt_next(bool pressed) {
}
const static actrls_t controls = {
NULL, // power
bt_volume_up, bt_volume_down, // volume up, volume down
bt_toggle, bt_play, // toggle, play
bt_pause, bt_stop, // pause, stop
NULL, NULL, // rew, fwd
bt_prev, bt_next, // prev, next
NULL, NULL, NULL, NULL, // left, right, up, down
NULL, NULL, NULL, NULL, NULL, NULL, // pre1-6
bt_volume_down, bt_volume_up, bt_toggle// knob left, knob_right, knob push
};
@@ -283,6 +284,8 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
(*bt_app_a2d_cmd_cb)(BT_SINK_DISCONNECTED);
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){
abs_volume = -1;
s_volume = sink_volume;
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
(*bt_app_a2d_cmd_cb)(BT_SINK_CONNECTED);
}
@@ -295,6 +298,9 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
s_audio = AUDIO_CONNECTED;
// send memorized volume for devices that can't do absolute volume
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
// verify that we can take control
if ((*bt_app_a2d_cmd_cb)(BT_SINK_AUDIO_STARTED, s_sample_rate)) {
@@ -378,7 +384,7 @@ static void bt_av_play_pos_changed(void)
}
}
void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter)
static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter)
{
switch (event_id) {
case ESP_AVRC_RN_TRACK_CHANGE:
@@ -487,19 +493,28 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
static void volume_set_by_controller(uint8_t volume)
{
ESP_LOGD(BT_RC_TG_TAG, "Volume is set by remote controller %d%%\n", (uint32_t)volume * 100 / 0x7f);
// do not modified NVS volume
_lock_acquire(&s_volume_lock);
s_volume = volume;
s_volume = abs_volume = (volume * 100) / 127;
_lock_release(&s_volume_lock);
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, volume);
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
}
static void volume_set_by_local_host(uint8_t volume)
static void volume_set_by_local_host(int value, bool is_step)
{
ESP_LOGD(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f);
_lock_acquire(&s_volume_lock);
s_volume = volume;
_lock_release(&s_volume_lock);
_lock_acquire(&s_volume_lock);
s_volume = is_step ? s_volume + value : value;
if (s_volume > 127) s_volume = 127;
else if (s_volume < 0) s_volume = 0;
if (abs_volume >= 0) abs_volume = s_volume;
else sink_volume = s_volume;
_lock_release(&s_volume_lock);
// volume has been set by controller, do not store it in NVS
if (abs_volume < 0) {
char p[4];
config_set_value(NVS_TYPE_STR, "bt_sink_volume", itoa(s_volume, p, 10));
}
if (s_volume_notify) {
esp_avrc_rn_param_t rn_param;
@@ -525,7 +540,7 @@ static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param)
break;
}
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: {
ESP_LOGD(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100/ 0x7f);
ESP_LOGD(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (rc->set_abs_vol.volume * 100) / 127);
volume_set_by_controller(rc->set_abs_vol.volume);
break;
}
@@ -557,7 +572,7 @@ void bt_sink_init(bt_cmd_vcb_t cmd_cb, bt_data_cb_t data_cb)
cmd_handler_chain = cmd_cb;
bt_app_a2d_data_cb = data_cb;
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
esp_bt_controller_mem_release(ESP_BT_MODE_BLE);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
@@ -593,6 +608,10 @@ void bt_sink_init(bt_cmd_vcb_t cmd_cb, bt_data_cb_t data_cb)
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
#endif
char *item = config_alloc_get_default(NVS_TYPE_STR, "bt_sink_volume", "127", 0);
sink_volume = atol(item);
free(item);
/*
* Set default parameters for Legacy Pairing
*/
@@ -626,14 +645,17 @@ void bt_sink_init(bt_cmd_vcb_t cmd_cb, bt_data_cb_t data_cb)
esp_pin_code[3]='4';
}
esp_bt_gap_set_pin(pin_type, strlen(pin_code), esp_pin_code);
free(pin_code);
}
void bt_sink_deinit(void)
{
/* this still does not work, can't figure out how to stop properly this BT stack */
bt_app_task_shut_down();
ESP_LOGD(BT_AV_TAG, "bt_app_task shutdown successfully");
if (esp_bluedroid_disable() != ESP_OK) return;
// this disable has a sleep timer BTA_DISABLE_DELAY in bt_target.h and
// if we don't wait for it then disable crashes... don't know why
vTaskDelay(2*200 / portTICK_PERIOD_MS);
ESP_LOGD(BT_AV_TAG, "esp_bluedroid_disable called successfully");
if (esp_bluedroid_deinit() != ESP_OK) return;
ESP_LOGD(BT_AV_TAG, "esp_bluedroid_deinit called successfully");

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
idf_component_register( SRC_DIRS .
INCLUDE_DIRS .
PRIV_REQUIRES tools newlib console esp_common freertos
PRIV_REQUIRES tools newlib console esp_common freertos
REQUIRES nvs_flash json
)

View File

@@ -5,8 +5,9 @@ idf_component_register( SRCS
cmd_system.c
cmd_wifi.c
platform_console.c
cmd_config.c
INCLUDE_DIRS .
REQUIRES nvs_flash
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display )
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite)
target_link_libraries(${COMPONENT_LIB} "-Wl,--undefined=GDS_DrawPixelFast")
target_link_libraries(${COMPONENT_LIB} ${build_dir}/esp-idf/$<TARGET_PROPERTY:RECOVERY_PREFIX>/lib$<TARGET_PROPERTY:RECOVERY_PREFIX>.a )

View File

@@ -5,5 +5,6 @@ idf_component_register( SRC_DIRS .
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
idf_build_get_property(project_ver PROJECT_VER)
message("******************* ${project_ver}")
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
set_source_files_properties(recovery.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"recovery\"")
set_source_files_properties(recovery.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"")

View File

@@ -1,2 +1,6 @@
#pragma once
#define PROJECT_NAME "recovery"
#include "sdkconfig.h"
#ifndef CONFIG_PROJECT_NAME
#pragma message "Defaulting project name."
#define CONFIG_PROJECT_NAME "Squeezelite-ESP32"
#endif

View File

@@ -1,13 +1,15 @@
#include <stdio.h>
#include <string.h>
#include "application_name.h"
#include "esp_err.h"
#include "esp_app_format.h"
extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
.magic_word = ESP_APP_DESC_MAGIC_WORD,
.version = PROJECT_VER,
.project_name = PROJECT_NAME,
.project_name = CONFIG_PROJECT_NAME,
.idf_ver = IDF_VER,
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION

View File

@@ -1,4 +1,4 @@
idf_build_get_property(idf_path IDF_PATH)
idf_build_get_property(idf_path IDF_PATH)
idf_component_register( SRCS cmd_squeezelite.c
INCLUDE_DIRS .
PRIV_REQUIRES spi_flash bootloader_support partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display )
@@ -13,7 +13,8 @@ target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=lround")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
idf_build_get_property(project_ver PROJECT_VER)
message("******************* ${project_ver}")
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
set_source_files_properties(cmd_squeezelite.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"squeezelite\"")
set_source_files_properties(cmd_squeezelite.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\" ")

View File

@@ -1,2 +1,6 @@
#pragma once
#define PROJECT_NAME "squeezelite"
#include "sdkconfig.h"
#ifndef CONFIG_PROJECT_NAME
#pragma message "Defaulting project name."
#define CONFIG_PROJECT_NAME "Squeezelite-ESP32"
#endif

View File

@@ -1,6 +1,6 @@
#include <stdio.h>
#include <string.h>
#include "application_name.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_pthread.h"
@@ -14,7 +14,7 @@
#include "esp_app_format.h"
extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
static const char * TAG = "squeezelite_cmd";
#define SQUEEZELITE_THREAD_STACK_SIZE (6*1024)
#define SQUEEZELITE_THREAD_STACK_SIZE (4*1024)
@@ -22,7 +22,7 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
.magic_word = ESP_APP_DESC_MAGIC_WORD,
.version = PROJECT_VER,
.project_name = PROJECT_NAME,
.project_name = CONFIG_PROJECT_NAME,
.idf_ver = IDF_VER,
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
/* cmd_i2ctools.h
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include "stdbool.h"
#include "stdio.h"
#ifdef __cplusplus
extern "C" {
#endif
void register_config_cmd(void);
#ifdef __cplusplus
}
#endif

View File

@@ -17,6 +17,7 @@ extern "C" {
#include "cmd_nvs.h"
#include "cmd_i2ctools.h"
#include "cmd_ota.h"
#include "cmd_config.h"
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -330,11 +330,11 @@ static int set_value(int argc, char **argv)
const char *key = set_args.key->sval[0];
const char *type = set_args.type->sval[0];
const char *values = set_args.value->sval[0];
log_send_messaging(MESSAGING_INFO, "Setting '%s' (type %s)", key,type);
cmd_send_messaging(argv[0],MESSAGING_INFO, "Setting '%s' (type %s)", key,type);
esp_err_t err = set_value_in_nvs(key, type, values);
if (err != ESP_OK) {
log_send_messaging(MESSAGING_ERROR, "%s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "%s", esp_err_to_name(err));
return 1;
}
@@ -355,7 +355,7 @@ static int get_value(int argc, char **argv)
esp_err_t err = get_value_from_nvs(key, type);
if (err != ESP_OK) {
log_send_messaging(MESSAGING_ERROR, "%s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "%s", esp_err_to_name(err));
return 1;
}
@@ -374,7 +374,7 @@ static int erase_value(int argc, char **argv)
esp_err_t err = erase(key);
if (err != ESP_OK) {
log_send_messaging(MESSAGING_ERROR, "%s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "%s", esp_err_to_name(err));
return 1;
}
@@ -392,7 +392,7 @@ static int erase_namespace(int argc, char **argv)
esp_err_t err = erase_all(name);
if (err != ESP_OK) {
log_send_messaging(MESSAGING_ERROR, "%s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "%s", esp_err_to_name(err));
return 1;
}
@@ -411,11 +411,11 @@ static int erase_wifi_manager(int argc, char **argv)
}
nvs_close(nvs);
if (err != ESP_OK) {
log_send_messaging(MESSAGING_ERROR, "wifi manager configuration was not erase. %s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "wifi manager configuration was not erase. %s", esp_err_to_name(err));
return 1;
}
else {
log_send_messaging(MESSAGING_WARNING, "Wifi manager configuration was erased");
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Wifi manager configuration was erased");
}
return 0;
}

View File

@@ -32,19 +32,32 @@
#include "messaging.h"
#include "platform_console.h"
#include "trace.h"
#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
#pragma message("Runtime stats enabled")
#define WITH_TASKS_INFO 1
#else
#pragma message("Runtime stats disabled")
#endif
static struct {
struct arg_str *scanmode;
struct arg_end *end;
} wifi_parms_arg;
static struct {
struct arg_str *name;
struct arg_end *end;
} name_args;
static struct {
struct arg_lit *btspeaker;
struct arg_lit *airplay;
struct arg_str *telnet;
#if WITH_TASKS_INFO
struct arg_lit *stats;
#endif
struct arg_end *end;
} set_services_args;
static const char * TAG = "cmd_system";
static void register_setbtsource();
//static void register_setbtsource();
static void register_free();
static void register_setdevicename();
static void register_heap();
@@ -55,14 +68,18 @@ static void register_light_sleep();
static void register_factory_boot();
static void register_restart_ota();
static void register_update_certs();
static void register_set_services();
static void register_set_wifi_parms();
#if WITH_TASKS_INFO
static void register_tasks();
#endif
extern BaseType_t wifi_manager_task;
void register_system()
{
register_setbtsource();
register_set_wifi_parms();
// register_setbtsource();
register_free();
register_set_services();
register_heap();
register_setdevicename();
register_version();
@@ -82,7 +99,7 @@ static int get_version(int argc, char **argv)
{
esp_chip_info_t info;
esp_chip_info(&info);
log_send_messaging(MESSAGING_INFO,
cmd_send_messaging(argv[0],MESSAGING_INFO,
"IDF Version:%s\r\n"
"Chip info:\r\n"
"\tmodel:%s\r\n"
@@ -110,35 +127,32 @@ static void register_version()
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'restart' command restarts the program */
esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
{
if(is_recovery_running){
if(partition_subtype ==ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
if(is_recovery_running){
if(partition_subtype ==ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
}
else {
if(partition_subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
}
else {
if(partition_subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
}
esp_err_t err = ESP_OK;
bool bFound=false;
log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
@@ -162,9 +176,7 @@ else {
log_send_messaging(MESSAGING_ERROR,"Unable to select partition for reboot: %s",esp_err_to_name(err));
}
else{
log_send_messaging(MESSAGING_WARNING, "Application partition %s sub type %u is selected for boot", partition->label,partition_subtype);
bFound=true;
messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"Reboot failed. Cannot iterate through partitions");
bFound=true;
}
}
else
@@ -175,9 +187,8 @@ else {
ESP_LOGD(TAG, "Yielding to other processes");
taskYIELD();
if(bFound) {
log_send_messaging(MESSAGING_WARNING,"Configuration %s changes. ",config_has_changes()?"has":"does not have");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration changes. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
@@ -191,7 +202,7 @@ static int restart(int argc, char **argv)
{
log_send_messaging(MESSAGING_WARNING, "\n\nPerforming a simple restart to the currently active partition.");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
cmd_send_messaging(argv[0],MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
@@ -200,7 +211,7 @@ static int restart(int argc, char **argv)
void simple_restart()
{
log_send_messaging(MESSAGING_WARNING,"\n\n Called to perform a simple system reboot.");
log_send_messaging(MESSAGING_WARNING,"System reboot requested.");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
@@ -211,24 +222,24 @@ void simple_restart()
}
esp_err_t guided_restart_ota(){
log_send_messaging(MESSAGING_WARNING,"\n\nCalled for a reboot to OTA Application");
log_send_messaging(MESSAGING_WARNING,"System reboot to Application requested");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
esp_err_t guided_factory(){
log_send_messaging(MESSAGING_WARNING,"\n\nCalled for a reboot to recovery application");
log_send_messaging(MESSAGING_WARNING,"System reboot to recovery requested");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
static int restart_factory(int argc, char **argv)
{
log_send_messaging(MESSAGING_WARNING, "Executing guided boot into recovery");
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into recovery");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return 0; // return fail. This should never return... we're rebooting!
}
static int restart_ota(int argc, char **argv)
{
log_send_messaging(MESSAGING_WARNING, "Executing guided boot into ota app 0");
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into ota app 0");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return 0; // return fail. This should never return... we're rebooting!
}
@@ -236,7 +247,7 @@ static void register_restart()
{
const esp_console_cmd_t cmd = {
.command = "restart",
.help = "Software reset of the chip",
.help = "Reboot system",
.hint = NULL,
.func = &restart,
};
@@ -247,7 +258,7 @@ static void register_restart_ota()
{
const esp_console_cmd_t cmd = {
.command = "restart_ota",
.help = "Selects the ota app partition to boot from and performa a software reset of the chip",
.help = "Reboot system to Squeezelite",
.hint = NULL,
.func = &restart_ota,
};
@@ -259,7 +270,7 @@ static void register_factory_boot()
{
const esp_console_cmd_t cmd = {
.command = "recovery",
.help = "Resets and boot to recovery (if available)",
.help = "Reboot system to Recovery",
.hint = NULL,
.func = &restart_factory,
};
@@ -270,94 +281,10 @@ static void register_factory_boot()
static int free_mem(int argc, char **argv)
{
log_send_messaging(MESSAGING_INFO,"%d", esp_get_free_heap_size());
cmd_send_messaging(argv[0],MESSAGING_INFO,"%d", esp_get_free_heap_size());
return 0;
}
/*
static struct {
struct arg_str *a2dp_dev_name;
struct arg_str *a2dp_sink_name;
struct arg_str *wakeup_gpio_level;
struct arg_str *bt_sink_pin;
struct arg_str *enable_bt_sink;
struct arg_end *end;
} set_btsource_args;
*/
//static int do_set_btsource(int argc, char **argv)
//{
// a2dp_dev_name;
// a2dp_sink_name;
// wakeup_gpio_level;
// bt_sink_pin;
// enable_bt_sink;
// int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&deep_sleep_args);
// if (nerrors != 0) {
// return 1;
// }
// if (deep_sleep_args.wakeup_time->count) {
// uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0];
// log_send_messaging(MESSAGING_INFO, "Enabling timer wakeup, timeout=%lluus", timeout);
// ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
// }
// if (deep_sleep_args.wakeup_gpio_num->count) {
// int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
// if (!rtc_gpio_is_valid_gpio(io_num)) {
// log_send_messaging(MESSAGING_ERROR, "GPIO %d is not an RTC IO", io_num);
// return 1;
// }
// int level = 0;
// if (deep_sleep_args.wakeup_gpio_level->count) {
// level = deep_sleep_args.wakeup_gpio_level->ival[0];
// if (level != 0 && level != 1) {
// log_send_messaging(MESSAGING_ERROR, "Invalid wakeup level: %d", level);
// return 1;
// }
// }
// log_send_messaging(MESSAGING_INFO, "Enabling wakeup on GPIO%d, wakeup on %s level",
// io_num, level ? "HIGH" : "LOW");
//
// ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(1ULL << io_num, level) );
// }
// rtc_gpio_isolate(GPIO_NUM_12);
// esp_deep_sleep_start();
//return 0;
//}
static void register_setbtsource(){
// a2dp_dev_name;
// a2dp_sink_name;
// wakeup_gpio_level;
// bt_sink_pin;
// enable_bt_sink;
//
// set_btsource_args.wakeup_time =
// arg_int0("t", "time", "<t>", "Wake up time, ms");
// set_btsource_args.wakeup_gpio_num =
// arg_int0(NULL, "io", "<n>",
// "If specified, wakeup using GPIO with given number");
// set_btsource_args.wakeup_gpio_level =
// arg_int0(NULL, "io_level", "<0|1>", "GPIO level to trigger wakeup");
// set_btsource_args.end = arg_end(3);
//
// const esp_console_cmd_t cmd = {
// .command = "deep_sleep",
// .help = "Enter deep sleep mode. "
// "Two wakeup modes are supported: timer and GPIO. "
// "If no wakeup option is specified, will sleep indefinitely.",
// .hint = NULL,
// .func = &do_set_btsource,
// .argtable = &set_btsource_args
// };
// ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_free()
{
@@ -375,7 +302,7 @@ static void register_free()
static int heap_size(int argc, char **argv)
{
uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
log_send_messaging(MESSAGING_INFO, "min heap size: %u", heap_size);
cmd_send_messaging(argv[0],MESSAGING_INFO, "min heap size: %u", heap_size);
return 0;
}
cJSON * setdevicename_cb(){
@@ -392,12 +319,83 @@ static int setnamevar(char * nvsname, FILE *f, char * value){
}
return err==ESP_OK?0:1;
}
typedef enum {
SCANNING,
PROCESSING_NAME
} scanstate_t;
int set_squeezelite_player_name(FILE * f,const char * name){
char * nvs_config= config_alloc_get(NVS_TYPE_STR, "autoexec1");
char **argv = NULL;
esp_err_t err=ESP_OK;
int nerrors=0;
bool bFoundParm=false;
scanstate_t state=SCANNING;
char * newCommandLine = NULL;
char * parm = " -n ";
char * cleaned_name = strdup(name);
for(char * p=cleaned_name;*p!='\0';p++){
if(*p == ' '){
*p='_'; // no spaces allowed
}
}
if(nvs_config && strlen(nvs_config)>0){
// allocate enough memory to hold the new command line
size_t cmdLength = strlen(nvs_config) + strlen(cleaned_name) + strlen(parm) +1 ;
newCommandLine = malloc(cmdLength);
memset(newCommandLine,0x00, cmdLength);
ESP_LOGD(TAG,"Parsing command %s",nvs_config);
argv = (char **) calloc(22, sizeof(char *));
if (argv == NULL) {
FREE_AND_NULL(nvs_config);
return 1;
}
size_t argc = esp_console_split_argv(nvs_config, argv,22);
for(int i=0;i<argc;i++) {
if(i>0){
strcat(newCommandLine," ");
}
switch (state)
{
case SCANNING:
strcat(newCommandLine,argv[i]);
if(strcasecmp(argv[i],"--name")==0 || strcasecmp(argv[i],"-n")==0 ){
state = PROCESSING_NAME;
}
break;
case PROCESSING_NAME:
bFoundParm=true;
strcat(newCommandLine,cleaned_name);
state = SCANNING;
break;
default:
break;
}
}
if(!bFoundParm){
strcat(newCommandLine,parm);
strcat(newCommandLine,name);
}
fprintf(f,"Squeezelite player name changed to %s\n",newCommandLine);
if((err=config_set_value(NVS_TYPE_STR, "autoexec1",newCommandLine))!=ESP_OK){
nerrors++;
fprintf(f,"Failed updating squeezelite command. %s", esp_err_to_name(err));
}
}
FREE_AND_NULL(nvs_config);
FREE_AND_NULL(argv);
free(cleaned_name);
return nerrors;
}
static int setdevicename(int argc, char **argv)
{
char * name = NULL;
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&name_args);
if (nerrors != 0) {
return 0;
return 1;
}
/* Check "--name" option */
@@ -405,32 +403,35 @@ static int setdevicename(int argc, char **argv)
name=strdup(name_args.name->sval[0]);
}
else {
log_send_messaging(MESSAGING_ERROR,"Name must be specified.");
return 0;
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Name must be specified.");
return 1;
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
log_send_messaging(MESSAGING_ERROR,"Unable to open memory stream.");
return 0;
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
nerrors+=setnamevar("a2dp_dev_name", f, name);
nerrors+=setnamevar("airplay_name", f, name);
nerrors+=setnamevar("ap_ssid", f, name);
nerrors+=setnamevar("bt_name", f, name);
nerrors+=setnamevar("host_name", f, name);
nerrors+=set_squeezelite_player_name(f, name);
if(nerrors==0){
fprintf(f,"Device name changed to %s\n",name);
}
if(!nerrors ){
fprintf(f,"Done.\n");
}
FREE_AND_NULL(name);
fflush (f);
log_send_messaging(nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return nerrors==0;
return nerrors;
}
@@ -454,7 +455,7 @@ static void register_setdevicename()
name_args.name = arg_str0("n", "name", default_host_name, "New Name");
name_args.end = arg_end(8);
const esp_console_cmd_t set_name= {
.command = "setname",
.command = CFG_TYPE_SYST("name"),
.help="Device Name",
.hint = NULL,
.func = &setdevicename,
@@ -471,16 +472,16 @@ static int tasks_info(int argc, char **argv)
const size_t bytes_per_task = 40; /* see vTaskList description */
char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task);
if (task_list_buffer == NULL) {
log_send_messaging(MESSAGING_ERROR, "failed to allocate buffer for vTaskList output");
cmd_send_messaging(argv[0],MESSAGING_ERROR, "failed to allocate buffer for vTaskList output");
return 1;
}
log_send_messaging(MESSAGING_INFO,"Task Name\tStatus\tPrio\tHWM\tTask#"
cmd_send_messaging(argv[0],MESSAGING_INFO,"Task Name\tStatus\tPrio\tHWM\tTask#"
#ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
"\tAffinity"
#endif
"\n");
vTaskList(task_list_buffer);
log_send_messaging(MESSAGING_INFO,"%s", task_list_buffer);
cmd_send_messaging(argv[0],MESSAGING_INFO,"%s", task_list_buffer);
free(task_list_buffer);
return 0;
}
@@ -500,7 +501,7 @@ static void register_tasks()
extern esp_err_t update_certificates(bool force);
static int force_update_cert(int argc, char **argv){
return update_certificates(true)==ESP_OK;
return update_certificates(true);
}
static void register_update_certs()
@@ -534,30 +535,31 @@ static int deep_sleep(int argc, char **argv)
}
if (deep_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0];
log_send_messaging(MESSAGING_INFO, "Enabling timer wakeup, timeout=%lluus", timeout);
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
if (deep_sleep_args.wakeup_gpio_num->count) {
int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
if (!rtc_gpio_is_valid_gpio(io_num)) {
log_send_messaging(MESSAGING_ERROR, "GPIO %d is not an RTC IO", io_num);
cmd_send_messaging(argv[0],MESSAGING_ERROR, "GPIO %d is not an RTC IO", io_num);
return 1;
}
int level = 0;
if (deep_sleep_args.wakeup_gpio_level->count) {
level = deep_sleep_args.wakeup_gpio_level->ival[0];
if (level != 0 && level != 1) {
log_send_messaging(MESSAGING_ERROR, "Invalid wakeup level: %d", level);
cmd_send_messaging(argv[0],MESSAGING_ERROR, "Invalid wakeup level: %d", level);
return 1;
}
}
log_send_messaging(MESSAGING_INFO, "Enabling wakeup on GPIO%d, wakeup on %s level",
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(1ULL << io_num, level) );
}
rtc_gpio_isolate(GPIO_NUM_12);
esp_deep_sleep_start();
return 0; // this code will never run. deep sleep will cause the system to restart
}
static void register_deep_sleep()
@@ -582,7 +584,184 @@ static void register_deep_sleep()
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static int enable_disable(FILE * f,char * nvs_name, struct arg_lit *arg){
esp_err_t err = config_set_value(NVS_TYPE_STR, nvs_name, arg->count>0?"Y":"N");
const char * name = arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary;
if(err!=ESP_OK){
fprintf(f,"Error %s %s. %s\n",arg->count>0?"Enabling":"Disabling", name, esp_err_to_name(err));
}
else {
fprintf(f,"%s %s\n",arg->count>0?"Enabled":"Disabled",name);
}
return err;
}
static int do_configure_wifi(int argc, char **argv){
esp_err_t err = ESP_OK;
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&wifi_parms_arg);
if (nerrors != 0) {
return 1;
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
if(wifi_parms_arg.scanmode->count>0){
if(strcasecmp(wifi_parms_arg.scanmode->sval[0],"Comprehensive") == 0){
err = config_set_value(NVS_TYPE_STR, "wifi_smode", "A");
}
else {
err = config_set_value(NVS_TYPE_STR, "wifi_smode", "F");
}
if(err!=ESP_OK){
nerrors++;
fprintf(f,"Error setting wifi scan mode to %s. %s\n",wifi_parms_arg.scanmode->sval[0], esp_err_to_name(err));
}
else {
fprintf(f,"Wifi Scan Mode changed to %s\n",wifi_parms_arg.scanmode->sval[0]);
}
}
if(!nerrors ){
fprintf(f,"Done.\n");
}
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return nerrors;
}
static int do_set_services(int argc, char **argv)
{
esp_err_t err = ESP_OK;
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&set_services_args);
if (nerrors != 0) {
return 1;
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
nerrors += enable_disable(f,"enable_airplay",set_services_args.airplay);
nerrors += enable_disable(f,"enable_bt_sink",set_services_args.btspeaker);
if(set_services_args.telnet->count>0){
if(strcasecmp(set_services_args.telnet->sval[0],"Disabled") == 0){
err = config_set_value(NVS_TYPE_STR, "telnet_enable", "N");
}
else if(strcasecmp(set_services_args.telnet->sval[0],"Telnet Only") == 0){
err = config_set_value(NVS_TYPE_STR, "telnet_enable", "Y");
}
else if(strcasecmp(set_services_args.telnet->sval[0],"Telnet and Serial") == 0){
err = config_set_value(NVS_TYPE_STR, "telnet_enable", "D");
}
if(err!=ESP_OK){
nerrors++;
fprintf(f,"Error setting telnet service to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
}
else {
fprintf(f,"Telnet service changed to %s\n",set_services_args.telnet->sval[0]);
}
}
#if WITH_TASKS_INFO
nerrors += enable_disable(f,"stats",set_services_args.stats);
#endif
if(!nerrors ){
fprintf(f,"Done.\n");
}
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return nerrors;
}
cJSON * configure_wifi_cb(){
cJSON * values = cJSON_CreateObject();
char * p=NULL;
if ((p = config_alloc_get(NVS_TYPE_STR, "wifi_smode")) != NULL) {
cJSON_AddStringToObject(values,"scanmode",strcasecmp(p,"a") == 0 ?"Comprehensive":"Fast");
FREE_AND_NULL(p);
}
return values;
}
cJSON * set_services_cb(){
cJSON * values = cJSON_CreateObject();
char * p=NULL;
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_bt_sink")) != NULL) {
cJSON_AddBoolToObject(values,"BT_Speaker",strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
FREE_AND_NULL(p);
}
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_airplay")) != NULL) {
cJSON_AddBoolToObject(values,"AirPlay",strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
FREE_AND_NULL(p);
}
if ((p = config_alloc_get(NVS_TYPE_STR, "telnet_enable")) != NULL) {
if(strcasestr("YX",p)!=NULL){
cJSON_AddStringToObject(values,"telnet","Telnet Only");
}
else if(strcasestr("D",p)!=NULL){
cJSON_AddStringToObject(values,"telnet","Telnet and Serial");
}
else {
cJSON_AddStringToObject(values,"telnet","Disabled");
}
FREE_AND_NULL(p);
}
#if WITH_TASKS_INFO
if((p = config_alloc_get_default(NVS_TYPE_STR, "stats", "n", 0))!=NULL){
cJSON_AddBoolToObject(values,"stats",(*p == '1' || *p == 'Y' || *p == 'y')) ;
}
#endif
return values;
}
static void register_set_services(){
set_services_args.airplay = arg_lit0(NULL, "AirPlay", "AirPlay");
set_services_args.btspeaker = arg_lit0(NULL, "BT_Speaker", "Bluetooth Speaker");
set_services_args.telnet= arg_str0("t", "telnet","Disabled|Telnet Only|Telnet and Serial","Telnet server. Use only for troubleshooting");
#if WITH_TASKS_INFO
set_services_args.stats= arg_lit0(NULL, "stats", "System Statistics. Use only for troubleshooting");
#endif
set_services_args.end=arg_end(2);
const esp_console_cmd_t cmd = {
.command = CFG_TYPE_SYST("services"),
.help = "Services",
.argtable = &set_services_args,
.hint = NULL,
.func = &do_set_services,
};
cmd_to_json_with_cb(&cmd,&set_services_cb);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_set_wifi_parms(){
wifi_parms_arg.scanmode = arg_str0(NULL, "scanmode", "Fast|Comprehensive","Sets the WiFi Scan Mode. Use Comprehensive where more than one AP has the same name on different channels. This will ensure that the AP with the strongest signal is chosen.");
wifi_parms_arg.end=arg_end(2);
const esp_console_cmd_t cmd = {
.command = CFG_TYPE_SYST("wifi"),
.help = "WiFi",
.argtable = &wifi_parms_arg,
.hint = NULL,
.func = &do_configure_wifi,
};
cmd_to_json_with_cb(&cmd,&configure_wifi_cb);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'light_sleep' command puts the chip into light sleep mode */
static struct {
@@ -601,22 +780,22 @@ static int light_sleep(int argc, char **argv)
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
if (light_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * light_sleep_args.wakeup_time->ival[0];
log_send_messaging(MESSAGING_INFO, "Enabling timer wakeup, timeout=%lluus", timeout);
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
int io_count = light_sleep_args.wakeup_gpio_num->count;
if (io_count != light_sleep_args.wakeup_gpio_level->count) {
log_send_messaging(MESSAGING_INFO, "Should have same number of 'io' and 'io_level' arguments");
cmd_send_messaging(argv[0],MESSAGING_INFO, "Should have same number of 'io' and 'io_level' arguments");
return 1;
}
for (int i = 0; i < io_count; ++i) {
int io_num = light_sleep_args.wakeup_gpio_num->ival[i];
int level = light_sleep_args.wakeup_gpio_level->ival[i];
if (level != 0 && level != 1) {
log_send_messaging(MESSAGING_ERROR, "Invalid wakeup level: %d", level);
cmd_send_messaging(argv[0],MESSAGING_ERROR, "Invalid wakeup level: %d", level);
return 1;
}
log_send_messaging(MESSAGING_INFO, "Enabling wakeup on GPIO%d, wakeup on %s level",
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) );
@@ -625,7 +804,7 @@ static int light_sleep(int argc, char **argv)
ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
}
if (CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) {
log_send_messaging(MESSAGING_INFO, "Enabling UART wakeup (press ENTER to exit light sleep)");
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling UART wakeup (press ENTER to exit light sleep)");
ESP_ERROR_CHECK( uart_set_wakeup_threshold(CONFIG_ESP_CONSOLE_UART_NUM, 3) );
ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM) );
}
@@ -648,7 +827,7 @@ static int light_sleep(int argc, char **argv)
cause_str = "unknown";
printf("%d\n", cause);
}
log_send_messaging(MESSAGING_INFO, "Woke up from: %s", cause_str);
cmd_send_messaging(argv[0],MESSAGING_INFO, "Woke up from: %s", cause_str);
return 0;
}

View File

@@ -117,8 +117,10 @@ static bool wifi_join(const char *ssid, const char *pass, int timeout_ms)
initialise_wifi();
wifi_config_t wifi_config = { 0 };
strncpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
wifi_config.sta.ssid[sizeof(wifi_config.sta.ssid) - 1] = '\0';
if (pass) {
strncpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
wifi_config.sta.password[sizeof(wifi_config.sta.password) - 1] = '\0';
}
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );

View File

@@ -50,7 +50,7 @@ const char* recovery_prompt = LOG_COLOR_E "recovery-squeezelite-esp32> " LOG_RES
#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"
void run_command(char * line);
esp_err_t run_command(char * line);
#define ADD_TO_JSON(o,t,n) if (t->n) cJSON_AddStringToObject(o,QUOTE(n),t->n);
#define ADD_PARMS_TO_CMD(o,t,n) { cJSON * parms = ParmsToJSON(&t.n->hdr); if(parms) cJSON_AddItemToObject(o,QUOTE(n),parms); }
cJSON * cmdList;
@@ -97,8 +97,11 @@ cJSON * ParmsToJSON(struct arg_hdr * * argtable){
ADD_TO_JSON(entry,table[tabindex],glossary);
ADD_TO_JSON(entry,table[tabindex],longopts);
ADD_TO_JSON(entry,table[tabindex],shortopts);
cJSON_AddBoolToObject(entry, "checkbox", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0);
cJSON_AddBoolToObject(entry, "checkbox", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0 && (table[tabindex]->longopts || table[tabindex]->shortopts) );
cJSON_AddBoolToObject(entry, "remark", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0 && (!table[tabindex]->longopts && !table[tabindex]->shortopts));
cJSON_AddBoolToObject(entry, "hasvalue", table[tabindex]->flag & ARG_HASVALUE);
cJSON_AddNumberToObject(entry,"mincount",table[tabindex]->mincount);
cJSON_AddNumberToObject(entry,"maxcount",table[tabindex]->maxcount);
cJSON_AddItemToArray(arg_list, entry);
tabindex++;
}
@@ -171,7 +174,7 @@ int arg_parse_msg(int argc, char **argv, struct arg_hdr ** args){
if (f != NULL) {
arg_print_errors(f, getParmsEnd(args), argv[0]);
fflush (f);
log_send_messaging(MESSAGING_ERROR,"%s", buf);
cmd_send_messaging(argv[0],MESSAGING_ERROR,"%s", buf);
}
fclose(f);
FREE_AND_NULL(buf);
@@ -254,7 +257,7 @@ void initialize_console() {
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
/* Initialize the console */
esp_console_config_t console_config = { .max_cmdline_args = 22,
esp_console_config_t console_config = { .max_cmdline_args = 28,
.max_cmdline_length = 600,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
@@ -285,7 +288,7 @@ void console_start() {
}
else {
/* Initialize the console */
esp_console_config_t console_config = { .max_cmdline_args = 22,
esp_console_config_t console_config = { .max_cmdline_args = 28,
.max_cmdline_length = 600,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
@@ -296,8 +299,10 @@ void console_start() {
/* Register commands */
esp_console_register_help_command();
register_system();
register_config_cmd();
register_nvs();
register_wifi();
if(!is_recovery_running){
register_squeezelite();
}
@@ -364,7 +369,7 @@ void console_start() {
}
}
void run_command(char * line){
esp_err_t run_command(char * line){
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
@@ -373,13 +378,16 @@ void run_command(char * line){
ESP_LOGE(TAG,"Unrecognized command: %s", line);
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err == ESP_OK && ret != ESP_OK) {
} else if (err != ESP_OK && ret != ESP_OK) {
ESP_LOGW(TAG,"Command returned non-zero error code: 0x%x (%s)", ret,
esp_err_to_name(err));
esp_err_to_name(err));
} else if (err == ESP_OK && ret != ESP_OK) {
ESP_LOGW(TAG,"Command returned in error");
err = ESP_FAIL;
} else if (err != ESP_OK) {
ESP_LOGE(TAG,"Internal error: %s", esp_err_to_name(err));
}
return err;
}
static void * console_thread() {
if(!is_recovery_running){

View File

@@ -13,6 +13,11 @@
#ifdef __cplusplus
extern "C" {
#endif
#define CFG_TYPE_HW(a) "cfg-hw-" a
#define CFG_TYPE_AUDIO(a) "cfg-audio-" a
#define CFG_TYPE_SYST(a) "cfg-syst-" a
#define CFG_TYPE_FW(a) "cfg-fw-" a
#define CFG_TYPE_GEN(a) "cfg-gen-" a
typedef cJSON * parm_values_fn_t(void);
esp_err_t cmd_to_json(const esp_console_cmd_t *cmd);
esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t parm_values_fn);

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "test_system.c"
INCLUDE_DIRS "."
REQUIRES unity tools console json platform_console platform_config )

View File

@@ -0,0 +1,189 @@
/* test_mean.c: Implementation of a testable component.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <limits.h>
#include "unity.h"
#include "platform_console.h"
#include "platform_esp32.h"
#include "platform_config.h"
#include "string.h"
struct arg_lit *arglit;
struct arg_int *argint;
struct arg_str *argstr;
struct arg_end *end;
extern int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory);
extern void initialize_console();
extern esp_err_t run_command(char * line);
static char *buf = NULL;
static char * s_tmp_line_buf=NULL;
static size_t buf_size = 0;
static FILE * f;
static size_t argc=1;
static char ** argv=NULL;
static bool config_initialized=false;
void init_console(){
if(config_initialized) return;
initialize_console();
config_initialized=true;
}
/****************************************************************************************
*
*/
void open_mem_stream_file(){
f = open_memstream(&buf, &buf_size);
}
/****************************************************************************************
*
*/
void close_flush_all(void * argtable, int count,bool print){
fflush (f);
if(print){
printf("%s", buf);
}
fclose(f);
free(buf);
arg_freetable(argtable,count);
free(argv);
}
/****************************************************************************************
*
*/
int alloc_split_command_line(char * cmdline){
argv = (char **) calloc(22, sizeof(char *));
if(!s_tmp_line_buf){
s_tmp_line_buf= calloc(strlen(cmdline), 1);
}
strlcpy(s_tmp_line_buf, cmdline, 22);
argc = esp_console_split_argv(s_tmp_line_buf, argv,22);
return 0;
}
/****************************************************************************************
*
*/
int alloc_split_parse_command_line(char * cmdline, void ** args){
alloc_split_command_line(cmdline);
return arg_parse(argc, argv,args);
}
/****************************************************************************************
*
*/
TEST_CASE("Invalid GPIO detected", "[config][ui]")
{
char * cmdline = "test -i 55\n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(1,is_output_gpio(argtable[0], f, &out_val, true),"Invalid GPIO not detected");
TEST_ASSERT_EQUAL_INT_MESSAGE(-1,out_val,"GPIO Should be set to -1");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("Invalid int gpio: [55] is not a GPIO\n",buf,"Invalid GPIO message wrong");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("Input Only GPIO detected", "[config][ui]")
{
char * cmdline = "test -i 35\n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(1,is_output_gpio(argtable[0], f, &out_val, true),"Input only GPIO not detected");
TEST_ASSERT_EQUAL_INT_MESSAGE(-1,out_val,"GPIO Should be set to -1");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("Invalid int gpio: [35] has input capabilities only\n",buf,"Missing GPIO message wrong");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("Valid GPIO Processed", "[config][ui]")
{
char * cmdline = "test -i 33\n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(0,is_output_gpio(argtable[0], f, &out_val, true),"Valid GPIO not recognized");
TEST_ASSERT_EQUAL_INT_MESSAGE(33,out_val,"GPIO Should be set to 33");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("",buf,"Valid GPIO shouldn't produce a message");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("Missing mandatory GPIO detected", "[config][ui]")
{
char * cmdline = "test \n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(1,is_output_gpio(argtable[0], f, &out_val, true),"Missing GPIO not detected");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("Missing: int\n",buf,"Missing GPIO parameter message wrong");
TEST_ASSERT_EQUAL_INT_MESSAGE(-1,out_val,"GPIO Should be set to -1");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("Missing mandatory parameter detected", "[config][ui]")
{
char * cmdline = "test \n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(1,is_output_gpio(argtable[0], f, &out_val, true),"Missing parameter not detected");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("Missing: int\n",buf,"Missing parameter message wrong");
TEST_ASSERT_EQUAL_INT_MESSAGE(-1,out_val,"GPIO Should be set to -1");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("dac config command", "[config_cmd]")
{
config_set_value(NVS_TYPE_STR, "dac_config", "");
esp_err_t err=run_command("cfg-hw-dac\n");
char * nvs_value = config_alloc_get_str("dac_config", NULL,NULL);
TEST_ASSERT_NOT_NULL(nvs_value);
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK,err,"Running command failed");
free(nvs_value);
}

View File

@@ -196,7 +196,7 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name,
id[63] = '\0';
ctx->svc = mdnsd_register_svc(ctx->svr, id, "_raop._tcp.local", ctx->port, NULL, (const char**) txt);
pthread_create(&ctx->thread, NULL, &rtsp_thread, ctx);
#else
LOG_INFO("starting mDNS with %s", id);
ESP_ERROR_CHECK( mdns_service_add(id, "_raop", "_tcp", ctx->port, txt, sizeof(txt) / sizeof(mdns_txt_item_t)) );
@@ -228,7 +228,6 @@ void raop_delete(struct raop_ctx_s *ctx) {
ctx->running = false;
// wake-up thread by connecting socket, needed for freeBSD
sock = socket(AF_INET, SOCK_STREAM, 0);
sock = socket(AF_INET, SOCK_STREAM, 0);
getsockname(ctx->sock, (struct sockaddr *) &addr, &nlen);
connect(sock, (struct sockaddr*) &addr, sizeof(addr));
@@ -248,13 +247,16 @@ void raop_delete(struct raop_ctx_s *ctx) {
// stop broadcasting devices
mdns_service_remove(ctx->svr, ctx->svc);
mdnsd_stop(ctx->svr);
#else
// then the RTSP task
ctx->joiner = xTaskGetCurrentTaskHandle();
ctx->running = false;
// brute-force exit of accept()
shutdown(ctx->sock, SHUT_RDWR);
closesocket(ctx->sock);
ctx->running = false;
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
// wait to make sure LWIP if scheduled (avoid issue with NotifyTake)
vTaskDelay(100 / portTICK_PERIOD_MS);
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
@@ -516,7 +518,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
if ((buf = kd_lookup(headers, "Active-Remote")) != NULL) strcpy(ctx->active_remote.id, buf);
#ifdef WIN32
ctx->active_remote.handle = init_mDNS(false, ctx->host);
ctx->active_remote.handle = init_mDNS(false, ctx->host);
pthread_create(&ctx->active_remote.thread, NULL, &search_remote, ctx);
#else
ctx->active_remote.running = true;

View File

@@ -25,6 +25,11 @@
#define CONFIG_AIRPLAY_NAME "ESP32-AirPlay"
#endif
static EXT_RAM_ATTR struct raop_cb_s {
raop_cmd_vcb_t cmd;
raop_data_cb_t data;
} raop_cbs;
log_level raop_loglevel = lINFO;
log_level util_loglevel;
@@ -81,12 +86,14 @@ static void raop_next(bool pressed) {
}
const static actrls_t controls = {
NULL, // power
raop_volume_up, raop_volume_down, // volume up, volume down
raop_toggle, raop_play, // toggle, play
raop_pause, raop_stop, // pause, stop
NULL, NULL, // rew, fwd
raop_prev, raop_next, // prev, next
NULL, NULL, NULL, NULL, // left, right, up, down
NULL, NULL, NULL, NULL, NULL, NULL, // pre1-6
raop_volume_down, raop_volume_up, raop_toggle// knob left, knob_right, knob push
};
@@ -148,17 +155,27 @@ void raop_sink_deinit(void) {
}
/****************************************************************************************
* Airplay sink initialization
* Airplay sink startup
*/
void raop_sink_init(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) {
const char *hostname;
static bool raop_sink_start(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) {
const char *hostname = NULL;
char sink_name[64-6] = CONFIG_AIRPLAY_NAME;
tcpip_adapter_ip_info_t ipInfo;
tcpip_adapter_ip_info_t ipInfo = { };
struct in_addr host;
tcpip_adapter_if_t ifs[] = { TCPIP_ADAPTER_IF_ETH, TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_IF_AP };
// get various IP info
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo);
tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &hostname);
for (int i = 0; i < sizeof(ifs) / sizeof(tcpip_adapter_if_t); i++)
if (tcpip_adapter_get_ip_info(ifs[i], &ipInfo) == ESP_OK && ipInfo.ip.addr != IPADDR_ANY) {
tcpip_adapter_get_hostname(ifs[i], &hostname);
break;
}
if (!hostname) {
LOG_INFO( "no hostname/IP found, can't start AirPlay");
return false;
}
host.s_addr = ipInfo.ip.addr;
// initialize mDNS
@@ -166,7 +183,7 @@ void raop_sink_init(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) {
ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
char * sink_name_buffer= (char *)config_alloc_get(NVS_TYPE_STR,"airplay_name");
if(sink_name_buffer != NULL){
if (sink_name_buffer != NULL){
memset(sink_name, 0x00, sizeof(sink_name));
strncpy(sink_name,sink_name_buffer,sizeof(sink_name)-1 );
free(sink_name_buffer);
@@ -179,6 +196,30 @@ void raop_sink_init(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) {
esp_read_mac(mac, ESP_MAC_WIFI_STA);
cmd_handler_chain = cmd_cb;
raop = raop_create(host, sink_name, mac, 0, cmd_handler, data_cb);
return true;
}
/****************************************************************************************
* Airplay sink timer handler
*/
static void raop_start_handler( TimerHandle_t xTimer ) {
if (raop_sink_start(raop_cbs.cmd, raop_cbs.data)) {
xTimerDelete(xTimer, portMAX_DELAY);
}
}
/****************************************************************************************
* Airplay sink initialization
*/
void raop_sink_init(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) {
if (!raop_sink_start(cmd_cb, data_cb)) {
raop_cbs.cmd = cmd_cb;
raop_cbs.data = data_cb;
TimerHandle_t timer = xTimerCreate("raopStart", 5000 / portTICK_RATE_MS, pdTRUE, NULL, raop_start_handler);
xTimerStart(timer, portMAX_DELAY);
LOG_INFO( "Delaying AirPlay start");
}
}
/****************************************************************************************

View File

@@ -168,7 +168,7 @@ static void rtp_thread_func(void *arg);
#endif
/*---------------------------------------------------------------------------*/
static struct alac_codec_s* alac_init(int fmtp[32]) {
static struct alac_codec_s* alac_init(int fmtp[32]) {
struct alac_codec_s *alac;
unsigned sample_rate, block_size;
unsigned char sample_size, channels;
@@ -196,7 +196,7 @@ static struct alac_codec_s* alac_init(int fmtp[32]) {
config.maxRun = htons(fmtp[8]);
config.maxFrameBytes = htonl(fmtp[9]);
config.avgBitRate = htonl(fmtp[10]);
config.sampleRate = htonl(fmtp[11]);
config.sampleRate = htonl(fmtp[11]);
alac = alac_create_decoder(sizeof(config), (unsigned char*) &config, &sample_size, &sample_rate, &channels, &block_size);
if (!alac) {

View File

@@ -1,7 +1,7 @@
idf_component_register(SRC_DIRS .
INCLUDE_DIRS . ${IDF_PATH}/components/driver
REQUIRES json tools platform_config display
PRIV_REQUIRES soc esp32 squeezelite
)

File diff suppressed because it is too large Load Diff

View File

@@ -10,27 +10,72 @@
#include "esp_system.h"
#include "driver/i2c.h"
#include "driver/i2s.h"
#include "driver/spi_master.h"
extern const char *i2c_name_type;
extern const char *spi_name_type;
typedef struct {
int width;
int height;
int RST_pin;
int i2c_system_port;
int address;
int CS_pin;
int speed;
const char *drivername;
const char *type;
int RST_pin;
bool hflip;
bool vflip;
const char *drivername;
int CS_pin;
int speed;
int back;
int depth;
const char *type;
bool rotate;
} display_config_t;
const display_config_t * config_display_get();
esp_err_t config_i2c_set(const i2c_config_t * config, int port);
esp_err_t config_spi_set(const spi_bus_config_t * config, int host, int dc);
const i2c_config_t * config_i2c_get(int * i2c_port);
const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host);
void parse_set_GPIO(void (*cb)(int gpio, char *value));
typedef struct {
bool rmii;
char model[16];
int rst;
int mdc, mdio;
int host;
int cs, mosi, miso, intr, clk;
int speed;
} eth_config_t;
typedef struct {
i2s_pin_config_t pin;
char model[32];
int mute_gpio;
int mute_level;
int i2c_addr;
int sda;
int scl;
} i2s_platform_config_t;
typedef struct {
int gpio;
int level;
bool fixed;
} gpio_with_level_t;
typedef struct {
gpio_with_level_t vcc;
gpio_with_level_t gnd;
gpio_with_level_t amp;
gpio_with_level_t ir;
gpio_with_level_t jack;
gpio_with_level_t green;
gpio_with_level_t red;
gpio_with_level_t spkfault;
} set_GPIO_struct_t;
typedef struct {
int A;
int B;
int SW;
bool knobonly;
bool volume_lock;
bool longpress;
int timer;
} rotary_struct_t;
typedef struct {
bool fixed;
@@ -38,7 +83,25 @@ typedef struct {
char * group;
int gpio;
} gpio_entry_t;
const display_config_t * config_display_get();
const eth_config_t * config_eth_get( );
esp_err_t config_display_set(const display_config_t * config);
esp_err_t config_i2c_set(const i2c_config_t * config, int port);
esp_err_t config_i2s_set(const i2s_platform_config_t * config, const char * nvs_name);
esp_err_t config_spi_set(const spi_bus_config_t * config, int host, int dc);
const i2c_config_t * config_i2c_get(int * i2c_port);
const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host);
void parse_set_GPIO(void (*cb)(int gpio, char *value));
const i2s_platform_config_t * config_dac_get();
const i2s_platform_config_t * config_spdif_get( );
esp_err_t config_spdif_set(const i2s_platform_config_t * config);
bool is_spdif_config_locked();
esp_err_t free_gpio_entry( gpio_entry_t ** gpio);
gpio_entry_t * get_gpio_by_name(char * name,char * group, bool refresh);
gpio_entry_t * get_gpio_by_no(int gpionum, bool refresh);
cJSON * get_gpio_list();
cJSON * get_gpio_list(bool refresh);
bool is_dac_config_locked();
bool are_statistics_enabled();
const rotary_struct_t * config_rotary_get();
esp_err_t config_rotary_set(rotary_struct_t * rotary);

View File

@@ -55,10 +55,12 @@ static const actrls_config_map_t actrls_config_map[] =
};
// BEWARE: the actions below need to stay aligned with the corresponding enum to properly support json parsing
// along with the actrls_t controls in LMS_controls, bt_sink and raop_sink
#define EP(x) [x] = #x /* ENUM PRINT */
static const char * actrls_action_s[ ] = { EP(ACTRLS_VOLUP),EP(ACTRLS_VOLDOWN),EP(ACTRLS_TOGGLE),EP(ACTRLS_PLAY),
static const char * actrls_action_s[ ] = { EP(ACTRLS_POWER),EP(ACTRLS_VOLUP),EP(ACTRLS_VOLDOWN),EP(ACTRLS_TOGGLE),EP(ACTRLS_PLAY),
EP(ACTRLS_PAUSE),EP(ACTRLS_STOP),EP(ACTRLS_REW),EP(ACTRLS_FWD),EP(ACTRLS_PREV),EP(ACTRLS_NEXT),
EP(BCTRLS_UP),EP(BCTRLS_DOWN),EP(BCTRLS_LEFT),EP(BCTRLS_RIGHT),
EP(BCTRLS_PS1),EP(BCTRLS_PS2),EP(BCTRLS_PS3),EP(BCTRLS_PS4),EP(BCTRLS_PS5),EP(BCTRLS_PS6),
EP(KNOB_LEFT),EP(KNOB_RIGHT),EP(KNOB_PUSH),
""} ;

View File

@@ -11,9 +11,10 @@
#include "buttons.h"
// BEWARE: this is the index of the array of action below (change actrls_action_s as well!)
typedef enum { ACTRLS_NONE = -1, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY,
typedef enum { ACTRLS_NONE = -1, ACTRLS_POWER,ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY,
ACTRLS_PAUSE, ACTRLS_STOP, ACTRLS_REW, ACTRLS_FWD, ACTRLS_PREV, ACTRLS_NEXT,
BCTRLS_UP, BCTRLS_DOWN, BCTRLS_LEFT, BCTRLS_RIGHT,
BCTRLS_PS1,BCTRLS_PS2,BCTRLS_PS3,BCTRLS_PS4,BCTRLS_PS5,BCTRLS_PS6,
KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH,
ACTRLS_REMAP, ACTRLS_MAX
} actrls_action_e;

View File

@@ -33,11 +33,12 @@ static struct {
int channel;
float sum, avg, scale;
int count;
int cells;
int cells, attenuation;
TimerHandle_t timer;
} battery = {
.channel = CONFIG_BAT_CHANNEL,
.cells = 2,
.attenuation = ADC_ATTEN_DB_0,
};
/****************************************************************************************
@@ -51,8 +52,9 @@ float battery_value_svc(void) {
*
*/
uint8_t battery_level_svc(void) {
// TODO: this is totally incorrect
return battery.avg ? (battery.avg - (3.0 * battery.cells)) / ((4.2 - 3.0) * battery.cells) * 100 : 0;
// TODO: this is vastly incorrect
int level = battery.avg ? (battery.avg - (3.0 * battery.cells)) / ((4.2 - 3.0) * battery.cells) * 100 : 0;
return level < 100 ? level : 100;
}
/****************************************************************************************
@@ -81,6 +83,7 @@ void battery_svc_init(void) {
#ifndef CONFIG_BAT_LOCKED
if ((p = strcasestr(nvs_item, "channel")) != NULL) battery.channel = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "scale")) != NULL) battery.scale = atof(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "atten")) != NULL) battery.attenuation = atoi(strchr(p, '=') + 1);
#endif
if ((p = strcasestr(nvs_item, "cells")) != NULL) battery.cells = atof(strchr(p, '=') + 1);
free(nvs_item);
@@ -88,7 +91,7 @@ void battery_svc_init(void) {
if (battery.channel != -1) {
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(battery.channel, ADC_ATTEN_DB_0);
adc1_config_channel_atten(battery.channel, battery.attenuation);
battery.avg = adc1_get_raw(battery.channel) * battery.scale / 4095.0;
battery.timer = xTimerCreate("battery", BATTERY_TIMER / portTICK_RATE_MS, pdTRUE, NULL, battery_callback);

View File

@@ -25,8 +25,6 @@
#include "rotary_encoder.h"
#include "globdefs.h"
bool gpio36_39_used;
static const char * TAG = "buttons";
static int n_buttons = 0;
@@ -49,7 +47,15 @@ static EXT_RAM_ATTR struct button_s {
TimerHandle_t timer;
} buttons[MAX_BUTTONS];
// can't use EXT_RAM_ATTR for initialized structure
static struct {
int gpio, level;
struct button_s *button;
} polled_gpio[] = { {36, -1, NULL}, {39, -1, NULL}, {-1, -1, NULL} };
static TimerHandle_t polled_timer;
static EXT_RAM_ATTR struct {
QueueHandle_t queue;
void *client;
rotary_encoder_info_t info;
@@ -57,7 +63,7 @@ static struct {
rotary_handler handler;
} rotary;
static struct {
static EXT_RAM_ATTR struct {
RingbufHandle_t rb;
infrared_handler handler;
} infrared;
@@ -76,7 +82,7 @@ static void common_task_init(void) {
if (!common_queue_set) {
common_queue_set = xQueueCreateSet(BUTTON_QUEUE_LEN + 1);
xTaskCreateStatic( (TaskFunction_t) buttons_task, "buttons_thread", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
xTaskCreateStatic( (TaskFunction_t) buttons_task, "buttons_thread", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 2, xStack, &xTaskBuffer);
}
}
@@ -117,6 +123,22 @@ static void buttons_timer( TimerHandle_t xTimer ) {
}
}
/****************************************************************************************
* Buttons polling timer
*/
static void buttons_polling( TimerHandle_t xTimer ) {
for (int i = 0; polled_gpio[i].gpio != -1; i++) {
if (!polled_gpio[i].button) continue;
int level = gpio_get_level(polled_gpio[i].gpio);
if (level != polled_gpio[i].level) {
polled_gpio[i].level = level;
buttons_timer(polled_gpio[i].button->timer);
}
}
}
/****************************************************************************************
* Tasks that calls the appropriate functions when buttons are pressed
*/
@@ -252,14 +274,29 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
}
}
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
if (gpio == 36 || gpio == 39) gpio36_39_used = true;
// and initialize level ...
buttons[n_buttons].level = gpio_get_level(gpio);
gpio_isr_handler_add(gpio, gpio_isr_handler, (void*) &buttons[n_buttons]);
gpio_intr_enable(gpio);
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
for (int i = 0; polled_gpio[i].gpio != -1; i++) if (polled_gpio[i].gpio == gpio) {
if (!polled_timer) {
polled_timer = xTimerCreate("buttonsPolling", 100 / portTICK_RATE_MS, pdTRUE, polled_gpio, buttons_polling);
xTimerStart(polled_timer, portMAX_DELAY);
}
polled_gpio[i].button = buttons + n_buttons;
polled_gpio[i].level = gpio_get_level(gpio);
ESP_LOGW(TAG, "creating polled gpio %u, level %u", gpio, polled_gpio[i].level);
gpio = -1;
break;
}
// only create timers and ISR is this is not a polled gpio
if (gpio != -1) {
gpio_isr_handler_add(gpio, gpio_isr_handler, (void*) &buttons[n_buttons]);
gpio_intr_enable(gpio);
}
n_buttons++;
}
@@ -337,7 +374,8 @@ static void rotary_button_handler(void *id, button_event_e event, button_press_e
* Create rotary encoder
*/
bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handler handler) {
if (A == -1 || B == -1) {
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
if (A == -1 || B == -1 || A == 36 || A == 39 || B == 36 || B == 39) {
ESP_LOGI(TAG, "Cannot create rotary %d %d", A, B);
return false;
}
@@ -348,9 +386,6 @@ bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handle
rotary.client = id;
rotary.handler = handler;
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
if (A == 36 || A == 39 || B == 36 || B == 39 || SW == 36 || SW == 39) gpio36_39_used = true;
// Initialise the rotary encoder device with the GPIOs for A and B signals
rotary_encoder_init(&rotary.info, A, B);

View File

@@ -17,7 +17,6 @@ extern int i2c_system_port;
extern int i2c_system_speed;
extern int spi_system_host;
extern int spi_system_dc_gpio;
extern bool gpio36_39_used;
typedef struct {
int timer, base_channel, max;
} pwm_system_t;

View File

@@ -409,7 +409,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b
}
double mclk;
int sdm0, sdm1, sdm2, odir, m_scale = 8;
int sdm0, sdm1, sdm2, odir, m_scale = (rate > 96000 && bits > 16) ? 4 : 8;
int fi2s_clk = rate*channel*bits*m_scale;
if (p_i2s_obj[i2s_num]->mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN)) {
//DAC uses bclk as sample clock, not WS. WS can be something arbitrary.
@@ -463,7 +463,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b
double fi2s_rate = i2s_apll_get_fi2s(bits, sdm0, sdm1, sdm2, odir);
p_i2s_obj[i2s_num]->real_rate = fi2s_rate/bits/channel/m_scale;
ESP_LOGI(I2S_TAG, "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK_M: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d",
rate, fi2s_rate/bits/channel/m_scale, bits, 1, m_scale, fi2s_rate, fi2s_rate/8, 1, 0);
rate, fi2s_rate/bits/channel/m_scale, bits, 1, m_scale, fi2s_rate, fi2s_rate/m_scale, 1, 0);
} else {
I2S[i2s_num]->clkm_conf.clka_en = 0;
I2S[i2s_num]->clkm_conf.clkm_div_a = 63;
@@ -928,8 +928,8 @@ static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_co
if (i2s_config->mode & I2S_MODE_TX) {
// PATCH
I2S[i2s_num]->conf.tx_msb_right = 1;
I2S[i2s_num]->conf.tx_right_first = 0;
I2S[i2s_num]->conf.tx_msb_right = i2s_config->bits_per_sample == 32 ? 0 : 1;
I2S[i2s_num]->conf.tx_right_first = ~I2S[i2s_num]->conf.tx_msb_right;
I2S[i2s_num]->conf.tx_slave_mod = 0; // Master
I2S[i2s_num]->fifo_conf.tx_fifo_mod_force_en = 1;
@@ -940,8 +940,7 @@ static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_co
}
if (i2s_config->mode & I2S_MODE_RX) {
// PATCH
I2S[i2s_num]->conf.rx_msb_right = 1;
I2S[i2s_num]->conf.rx_msb_right = 1;
I2S[i2s_num]->conf.rx_right_first = 0;
I2S[i2s_num]->conf.rx_slave_mod = 0; // Master
I2S[i2s_num]->fifo_conf.rx_fifo_mod_force_en = 1;

View File

@@ -28,7 +28,7 @@
static const char *TAG = "led";
static struct led_s {
static EXT_RAM_ATTR struct led_s {
gpio_num_t gpio;
bool on;
int onstate;
@@ -40,7 +40,7 @@ static struct led_s {
TimerHandle_t timer;
} leds[MAX_LED];
static struct {
static EXT_RAM_ATTR struct {
int gpio;
int active;
int pwm;

View File

@@ -119,6 +119,8 @@ const char * messaging_get_class_desc(messaging_classes msg_class){
CASE_TO_STR(MESSAGING_CLASS_OTA);
CASE_TO_STR(MESSAGING_CLASS_SYSTEM);
CASE_TO_STR(MESSAGING_CLASS_STATS);
CASE_TO_STR(MESSAGING_CLASS_CFGCMD);
CASE_TO_STR(MESSAGING_CLASS_BT);
default:
return "Unknown";
break;
@@ -141,12 +143,12 @@ cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle){
else {
json_message = cJSON_CreateObject();
cJSON_AddStringToObject(json_message, "message", message->message);
vRingbufferReturnItem(buf_handle, (void *)message);
cJSON_AddStringToObject(json_message, "type", messaging_get_type_desc(message->type));
cJSON_AddStringToObject(json_message, "class", messaging_get_class_desc(message->msg_class));
cJSON_AddNumberToObject(json_message,"sent_time",message->sent_time);
cJSON_AddNumberToObject(json_message,"current_time",esp_timer_get_time() / 1000);
cJSON_AddItemToArray(json_messages,json_message);
vRingbufferReturnItem(buf_handle, (void *)message);
}
}
return json_messages;
@@ -260,5 +262,23 @@ void log_send_messaging(messaging_types msgtype,const char *fmt, ...) {
else{
ESP_LOGE(tag, "Memory allocation failed while sending message");
}
}
void cmd_send_messaging(const char * cmdname,messaging_types msgtype, const char *fmt, ...){
va_list va;
va_start(va, fmt);
size_t cmd_len = strlen(cmdname)+1;
size_t ln = vsnprintf(NULL, 0, fmt, va)+1;
char * message_txt = malloc(ln+cmd_len);
if(message_txt){
strcpy(message_txt,cmdname);
strcat(message_txt,"\n");
vsprintf((message_txt+cmd_len), fmt, va);
va_end(va);
ESP_LOG_LEVEL_LOCAL(messaging_type_to_err_type(msgtype),tag, "%s",message_txt);
messaging_post_message(msgtype, MESSAGING_CLASS_CFGCMD, message_txt );
free(message_txt);
}
else{
ESP_LOGE(tag, "Memory allocation failed while sending message");
}
}

View File

@@ -1,4 +1,5 @@
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/ringbuf.h"
#include "cJSON.h"
#pragma once
@@ -10,7 +11,9 @@ typedef enum {
typedef enum {
MESSAGING_CLASS_OTA,
MESSAGING_CLASS_SYSTEM,
MESSAGING_CLASS_STATS
MESSAGING_CLASS_STATS,
MESSAGING_CLASS_CFGCMD,
MESSAGING_CLASS_BT
} messaging_classes;
typedef struct messaging_list_t *messaging_handle_t;
@@ -30,6 +33,7 @@ void messaging_post_message(messaging_types type,messaging_classes msg_class, co
cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle);
single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle);
void log_send_messaging(messaging_types msgtype,const char *fmt, ...);
void cmd_send_messaging(const char * cmdname,messaging_types msgtype, const char *fmt, ...);
esp_err_t messaging_type_to_err_type(messaging_types type);
void messaging_service_init();

View File

@@ -32,11 +32,8 @@ static const char *TAG = "monitor";
static TimerHandle_t monitor_timer;
static struct {
int gpio;
int active;
} jack = { CONFIG_JACK_GPIO, 0 },
spkfault = { CONFIG_SPKFAULT_GPIO, 0 };
static monitor_gpio_t jack = { CONFIG_JACK_GPIO, 0 };
static monitor_gpio_t spkfault = { CONFIG_SPKFAULT_GPIO, 0 };
void (*jack_handler_svc)(bool inserted);
bool jack_inserted_svc(void);
@@ -44,11 +41,13 @@ bool jack_inserted_svc(void);
void (*spkfault_handler_svc)(bool inserted);
bool spkfault_svc(void);
/****************************************************************************************
*
*/
static void task_stats( cJSON* top ) {
#ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY
#pragma message("Compiled with trace facility")
static struct {
TaskStatus_t *tasks;
uint32_t total, n;
@@ -63,6 +62,7 @@ static void task_stats( cJSON* top ) {
*scratch = '\0';
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
#pragma message("Compiled with runtime stats")
uint32_t elapsed = current.total - previous.total;
for(int i = 0, n = 0; i < current.n; i++ ) {
@@ -90,6 +90,8 @@ static void task_stats( cJSON* top ) {
}
}
#else
#pragma message("Compiled WITHOUT runtime stats")
for (int i = 0, n = 0; i < current.n; i ++) {
n += sprintf(scratch + n, "%16s s:%5u\t", current.tasks[i].pcTaskName, current.tasks[i].usStackHighWaterMark);
cJSON * t=cJSON_CreateObject();
@@ -109,6 +111,8 @@ static void task_stats( cJSON* top ) {
cJSON_AddItemToObject(top,"tasks",tlist);
if (previous.tasks) free(previous.tasks);
previous = current;
#else
#pragma message("Compiled WITHOUT trace facility")
#endif
}
@@ -142,7 +146,6 @@ static void monitor_callback(TimerHandle_t xTimer) {
*/
static void jack_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) {
ESP_LOGD(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed");
messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_SYSTEM,"jack is %s",BUTTON_PRESSED ? "inserted" : "removed");
if (jack_handler_svc) (*jack_handler_svc)(event == BUTTON_PRESSED);
}
@@ -181,6 +184,9 @@ static void set_jack_gpio(int gpio, char *value) {
jack.gpio = gpio;
if ((p = strchr(value, ':')) != NULL) jack.active = atoi(p + 1);
}
else {
jack.gpio = -1;
}
}
#endif
@@ -194,6 +200,9 @@ static void set_spkfault_gpio(int gpio, char *value) {
spkfault.gpio = gpio;
if ((p = strchr(value, ':')) != NULL) spkfault.active = atoi(p + 1);
}
else {
spkfault.gpio = -1;
}
}
#endif
@@ -237,7 +246,7 @@ void monitor_svc_init(void) {
monitor_timer = xTimerCreate("monitor", MONITOR_TIMER / portTICK_RATE_MS, pdTRUE, NULL, monitor_callback);
xTimerStart(monitor_timer, portMAX_DELAY);
}
free(p);
FREE_AND_NULL(p);
ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
@@ -245,3 +254,17 @@ void monitor_svc_init(void) {
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
}
/****************************************************************************************
*
*/
monitor_gpio_t * get_spkfault_gpio(){
return &spkfault ;
}
/****************************************************************************************
*
*/
monitor_gpio_t * get_jack_insertion_gpio(){
return &jack;
}

View File

@@ -9,6 +9,10 @@
*/
#pragma once
typedef struct {
int gpio;
int active;
} monitor_gpio_t;
extern void (*jack_handler_svc)(bool inserted);
extern bool jack_inserted_svc(void);
@@ -17,5 +21,8 @@ extern void (*spkfault_handler_svc)(bool inserted);
extern bool spkfault_svc(void);
extern float battery_value_svc(void);
extern uint8_t battery_level_svc(void);
extern uint16_t battery_level_svc(void);
extern monitor_gpio_t * get_spkfault_gpio();
extern monitor_gpio_t * get_jack_insertion_gpio();

View File

@@ -38,6 +38,8 @@
#include "gds_text.h"
#include "gds_draw.h"
#include "platform_esp32.h"
#include "lwip/sockets.h"
extern const char * get_certificate();
#define IF_DISPLAY(x) if(display) { x; }
@@ -92,6 +94,7 @@ ota_status_t * ota_status;
struct timeval tv;
static esp_http_client_config_t http_client_config;
void _printMemStats(){
ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
@@ -276,8 +279,8 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
// int data_len - data length of data
// void *user_data -- user_data context, from esp_http_client_config_t user_data
// char *header_key For HTTP_EVENT_ON_HEADER event_id, it<69>s store current http header key
// char *header_value For HTTP_EVENT_ON_HEADER event_id, it<69>s store current http header value
// char *header_key For HTTP_EVENT_ON_HEADER event_id, it<69>s store current http header key
// char *header_value For HTTP_EVENT_ON_HEADER event_id, it<69>s store current http header value
// --------------
switch (evt->event_id) {
case HTTP_EVENT_ERROR:
@@ -630,7 +633,7 @@ void ota_task(void *pvParameter)
gettimeofday(&tv, NULL);
uint32_t elapsed_ms= (tv.tv_sec-ota_status->OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status->OTA_start.tv_usec)/1000;
ESP_LOGI(TAG,"OTA progress : %d/%.0f (%d pct), %d KB/s", ota_status->actual_image_len, ota_status->total_image_len, ota_status->newpct, elapsed_ms>0?ota_status->actual_image_len*1000/elapsed_ms/1024:0);
sendMessaging(MESSAGING_INFO,"Writing binary file %%%3d.",ota_status->newpct);
sendMessaging(MESSAGING_INFO,"Writing binary file %3d %%.",ota_status->newpct);
ota_status->lastpct=ota_status->newpct;
}
taskYIELD();
@@ -659,7 +662,7 @@ void ota_task(void *pvParameter)
ESP_LOGI(TAG,"OTA Process completed successfully!");
sendMessaging(MESSAGING_INFO,"Success!");
IF_DISPLAY(GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Success!"));
vTaskDelay(1500/ portTICK_PERIOD_MS); // wait here to give the UI a chance to refresh
vTaskDelay(3500/ portTICK_PERIOD_MS); // wait here to give the UI a chance to refresh
IF_DISPLAY(GDS_Clear(display,GDS_COLOR_BLACK));
esp_restart();
} else {
@@ -721,5 +724,66 @@ esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t
return ESP_OK;
}
extern void set_lms_server_details(in_addr_t ip, u16_t hport, u16_t cport);
in_addr_t discover_ota_server(int max) {
struct sockaddr_in d;
struct sockaddr_in s;
char buf[32], port_d[] = "JSON", clip_d[] = "CLIP";
struct pollfd pollinfo;
uint8_t len;
uint16_t hport=9000;
uint16_t cport=9090;
int disc_sock = socket(AF_INET, SOCK_DGRAM, 0);
socklen_t enable = 1;
setsockopt(disc_sock, SOL_SOCKET, SO_BROADCAST, (const void *)&enable, sizeof(enable));
len = sprintf(buf,"e%s%c%s", port_d, '\0', clip_d) + 1;
memset(&d, 0, sizeof(d));
d.sin_family = AF_INET;
d.sin_port = htons(3483);
d.sin_addr.s_addr = htonl(INADDR_BROADCAST);
pollinfo.fd = disc_sock;
pollinfo.events = POLLIN;
do {
ESP_LOGI(TAG,"sending LMS discovery");
memset(&s, 0, sizeof(s));
if (sendto(disc_sock, buf, len, 0, (struct sockaddr *)&d, sizeof(d)) < 0) {
ESP_LOGE(TAG,"error sending discovery");
}
else {
if (poll(&pollinfo, 1, 5000) == 1) {
char readbuf[64], *p;
socklen_t slen = sizeof(s);
memset(readbuf, 0, sizeof(readbuf));
recvfrom(disc_sock, readbuf, sizeof(readbuf) - 1, 0, (struct sockaddr *)&s, &slen);
ESP_LOGI(TAG,"got response from: %s:%d - %s", inet_ntoa(s.sin_addr), ntohs(s.sin_port),readbuf);
if ((p = strstr(readbuf, port_d)) != NULL) {
p += strlen(port_d);
hport = atoi(p + 1);
}
if ((p = strstr(readbuf, clip_d)) != NULL) {
p += strlen(clip_d);
cport = atoi(p + 1);
}
server_notify(s.sin_addr.s_addr, hport, cport);
}
}
} while (s.sin_addr.s_addr == 0 && (!max || --max));
closesocket(disc_sock);
return s.sin_addr.s_addr;
}

View File

@@ -31,4 +31,4 @@ const char * ota_get_status();
uint8_t ota_get_pct_complete();
esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length);
in_addr_t discover_ota_server(int max);

View File

@@ -1,6 +1,4 @@
idf_component_register( SRC_DIRS . external ac101 tas57xx
idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978
INCLUDE_DIRS . ac101
PRIV_REQUIRES
codecs
@@ -17,8 +15,6 @@ idf_component_register( SRC_DIRS . external ac101 tas57xx
EMBED_FILES vu.data
)
set_source_files_properties(mad.c
PROPERTIES COMPILE_FLAGS
-Wno-maybe-uninitialized
@@ -33,9 +29,16 @@ set_source_files_properties(flac.c
-Wno-maybe-uninitialized
)
add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4)
add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DCUSTOM_VERSION=${BUILD_NUMBER})
if (${DEPTH} EQUAL "32")
add_definitions(-DBYTES_PER_FRAME=8)
else()
add_definitions(-DRESAMPLE16 -DBYTES_PER_FRAME=4)
endif()
if (NOT DEFINED AAC_DISABLED_SBR)
add_definitions(-DAAC_ENABLE_SBR)
endif()
add_compile_options (-O3 )

View File

@@ -48,116 +48,87 @@ static const char TAG[] = "AC101";
return b;\
}
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static void deinit(void);
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config);
static void speaker(bool active);
static void headset(bool active);
static bool volume(unsigned left, unsigned right);
static void power(adac_power_e mode);
const struct adac_s dac_ac101 = { "AC101", init, deinit, power, speaker, headset, volume };
const struct adac_s dac_ac101 = { "AC101", init, adac_deinit, power, speaker, headset, volume };
static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val);
static uint16_t i2c_read_reg(uint8_t reg);
static void ac101_start(ac_module_t mode);
static void ac101_stop(void);
static void ac101_set_earph_volume(uint8_t volume);
static void ac101_set_spk_volume(uint8_t volume);
static int i2c_port;
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
esp_err_t res = ESP_OK;
char *p;
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = -1,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = -1,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 250000,
};
if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
i2c_port = i2c_port_num;
i2c_param_config(i2c_port, &i2c_config);
i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
res = i2c_read_reg(CHIP_AUDIO_RS);
if (!res) {
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
adac_init(config, i2c_port);
if (adac_read_word(AC101_ADDR, CHIP_AUDIO_RS) == 0xffff) {
ESP_LOGW(TAG, "No AC101 detected");
i2c_driver_delete(i2c_port);
return 0;
return false;
}
res = i2c_write_reg(CHIP_AUDIO_RS, 0x123);
// huh?
ESP_LOGI(TAG, "AC101 detected");
adac_write_word(AC101_ADDR, CHIP_AUDIO_RS, 0x123);
vTaskDelay(100 / portTICK_PERIOD_MS);
// enable the PLL from BCLK source
i2c_write_reg(PLL_CTRL1, BIN(0000,0001,0100,1111)); // F=1,M=1,PLL,INT=31 (medium)
i2c_write_reg(PLL_CTRL2, BIN(1000,0110,0000,0000)); // PLL, F=96,N_i=1024-96,F=0,N_f=0*0.2;
// i2c_write_reg(PLL_CTRL2, BIN(1000,0011,1100,0000));
adac_write_word(AC101_ADDR, PLL_CTRL1, BIN(0000,0001,0100,1111)); // F=1,M=1,PLL,INT=31 (medium)
adac_write_word(AC101_ADDR, PLL_CTRL2, BIN(1000,0110,0000,0000)); // PLL, F=96,N_i=1024-96,F=0,N_f=0*0.2;
// adac_write_word(AC101_ADDR, PLL_CTRL2, BIN(1000,0011,1100,0000));
// clocking system
i2c_write_reg(SYSCLK_CTRL, BIN(1010,1010,0000,1000)); // PLLCLK, BCLK1, IS1CLK, PLL, SYSCLK
i2c_write_reg(MOD_CLK_ENA, BIN(1000,0000,0000,1100)); // IS21, ADC, DAC
i2c_write_reg(MOD_RST_CTRL, BIN(1000,0000,0000,1100)); // IS21, ADC, DAC
i2c_write_reg(I2S_SR_CTRL, BIN(0111,0000,0000,0000)); // 44.1kHz
adac_write_word(AC101_ADDR, SYSCLK_CTRL, BIN(1010,1010,0000,1000)); // PLLCLK, BCLK1, IS1CLK, PLL, SYSCLK
adac_write_word(AC101_ADDR, MOD_CLK_ENA, BIN(1000,0000,0000,1100)); // IS21, ADC, DAC
adac_write_word(AC101_ADDR, MOD_RST_CTRL, BIN(1000,0000,0000,1100)); // IS21, ADC, DAC
adac_write_word(AC101_ADDR, I2S_SR_CTRL, BIN(0111,0000,0000,0000)); // 44.1kHz
// analogue config
i2c_write_reg(I2S1LCK_CTRL, BIN(1000,1000,0101,0000)); // Slave, BCLK=I2S/8,LRCK=32,16bits,I2Smode, Stereo
i2c_write_reg(I2S1_SDOUT_CTRL, BIN(1100,0000,0000,0000)); // I2S1ADC (R&L)
i2c_write_reg(I2S1_SDIN_CTRL, BIN(1100,0000,0000,0000)); // IS21DAC (R&L)
i2c_write_reg(I2S1_MXR_SRC, BIN(0010,0010,0000,0000)); // ADCL, ADCR
i2c_write_reg(ADC_SRCBST_CTRL, BIN(0100,0100,0100,0000)); // disable all boost (default)
#if ENABLE_ADC
i2c_write_reg(ADC_SRC, BIN(0000,0100,0000,1000)); // source=linein(R/L)
i2c_write_reg(ADC_DIG_CTRL, BIN(1000,0000,0000,0000)); // enable digital ADC
i2c_write_reg(ADC_ANA_CTRL, BIN(1011, 1011,0000,0000)); // enable analogue R/L, 0dB
#if BYTES_PER_FRAME == 8
adac_write_word(AC101_ADDR, I2S1LCK_CTRL, BIN(1000,1000,0111,0000)); // Slave, BCLK=I2S/8,LRCK=32,24bits,I2Smode, Stereo
i2s_config->bits_per_sample = 24;
#else
i2c_write_reg(ADC_SRC, BIN(0000,0000,0000,0000)); // source=none
i2c_write_reg(ADC_DIG_CTRL, BIN(0000,0000,0000,0000)); // disable digital ADC
i2c_write_reg(ADC_ANA_CTRL, BIN(0011, 0011,0000,0000)); // disable analogue R/L, 0dB
adac_write_word(AC101_ADDR, I2S1LCK_CTRL, BIN(1000,1000,0101,0000)); // Slave, BCLK=I2S/8,LRCK=32,16bits,I2Smode, Stereo
#endif
adac_write_word(AC101_ADDR, I2S1_SDOUT_CTRL, BIN(1100,0000,0000,0000)); // I2S1ADC (R&L)
adac_write_word(AC101_ADDR, I2S1_SDIN_CTRL, BIN(1100,0000,0000,0000)); // IS21DAC (R&L)
adac_write_word(AC101_ADDR, I2S1_MXR_SRC, BIN(0010,0010,0000,0000)); // ADCL, ADCR
adac_write_word(AC101_ADDR, ADC_SRCBST_CTRL, BIN(0100,0100,0100,0000)); // disable all boost (default)
#if ENABLE_ADC
adac_write_word(AC101_ADDR, ADC_SRC, BIN(0000,0100,0000,1000)); // source=linein(R/L)
adac_write_word(AC101_ADDR, ADC_DIG_CTRL, BIN(1000,0000,0000,0000)); // enable digital ADC
adac_write_word(AC101_ADDR, ADC_ANA_CTRL, BIN(1011, 1011,0000,0000)); // enable analogue R/L, 0dB
#else
adac_write_word(AC101_ADDR, ADC_SRC, BIN(0000,0000,0000,0000)); // source=none
adac_write_word(AC101_ADDR, ADC_DIG_CTRL, BIN(0000,0000,0000,0000)); // disable digital ADC
adac_write_word(AC101_ADDR, ADC_ANA_CTRL, BIN(0011, 0011,0000,0000)); // disable analogue R/L, 0dB
#endif
//Path Configuration
i2c_write_reg(DAC_MXR_SRC, BIN(1000,1000,0000,0000)); // DAC from I2S
i2c_write_reg(DAC_DIG_CTRL, BIN(1000,0000,0000,0000)); // enable DAC
i2c_write_reg(OMIXER_DACA_CTRL, BIN(1111,0000,0000,0000)); // enable DAC/Analogue (see note on offset removal and PA)
i2c_write_reg(OMIXER_DACA_CTRL, BIN(1111,1111,0000,0000)); // this toggle is needed for headphone PA offset
adac_write_word(AC101_ADDR, DAC_MXR_SRC, BIN(1000,1000,0000,0000)); // DAC from I2S
adac_write_word(AC101_ADDR, DAC_DIG_CTRL, BIN(1000,0000,0000,0000)); // enable DAC
adac_write_word(AC101_ADDR, OMIXER_DACA_CTRL, BIN(1111,0000,0000,0000)); // enable DAC/Analogue (see note on offset removal and PA)
adac_write_word(AC101_ADDR, OMIXER_DACA_CTRL, BIN(1111,1111,0000,0000)); // this toggle is needed for headphone PA offset
#if ENABLE_ADC
i2c_write_reg(OMIXER_SR, BIN(0000,0001,0000,0010)); // source=DAC(R/L) (are DACR and DACL really inverted in bitmap?)
adac_write_word(AC101_ADDR, OMIXER_SR, BIN(0000,0001,0000,0010)); // source=DAC(R/L) (are DACR and DACL really inverted in bitmap?)
#else
i2c_write_reg(OMIXER_SR, BIN(0000,0101,0000,1010)); // source=DAC(R/L) and LINEIN(R/L)
adac_write_word(AC101_ADDR, OMIXER_SR, BIN(0000,0101,0000,1010)); // source=DAC(R/L) and LINEIN(R/L)
#endif
// enable earphone & speaker
i2c_write_reg(SPKOUT_CTRL, 0x0220);
i2c_write_reg(HPOUT_CTRL, 0xf801);
adac_write_word(AC101_ADDR, SPKOUT_CTRL, 0x0220);
adac_write_word(AC101_ADDR, HPOUT_CTRL, 0xf801);
// set gain for speaker and earphone
ac101_set_spk_volume(100);
ac101_set_earph_volume(100);
ESP_LOGI(TAG, "AC101 uses I2C sda:%d, scl:%d", i2c_config.sda_io_num, i2c_config.scl_io_num);
return (res == ESP_OK);
}
/****************************************************************************************
* init
*/
static void deinit(void) {
i2c_driver_delete(i2c_port);
return true;
}
/****************************************************************************************
@@ -190,9 +161,9 @@ static void power(adac_power_e mode) {
* speaker
*/
static void speaker(bool active) {
uint16_t value = i2c_read_reg(SPKOUT_CTRL);
if (active) i2c_write_reg(SPKOUT_CTRL, value | SPKOUT_EN);
else i2c_write_reg(SPKOUT_CTRL, value & ~SPKOUT_EN);
uint16_t value = adac_read_word(AC101_ADDR, SPKOUT_CTRL);
if (active) adac_write_word(AC101_ADDR, SPKOUT_CTRL, value | SPKOUT_EN);
else adac_write_word(AC101_ADDR, SPKOUT_CTRL, value & ~SPKOUT_EN);
}
/****************************************************************************************
@@ -200,49 +171,9 @@ static void speaker(bool active) {
*/
static void headset(bool active) {
// there might be aneed to toggle OMIXER_DACA_CTRL 11:8, not sure
uint16_t value = i2c_read_reg(HPOUT_CTRL);
if (active) i2c_write_reg(HPOUT_CTRL, value | EAROUT_EN);
else i2c_write_reg(HPOUT_CTRL, value & ~EAROUT_EN);
}
/****************************************************************************************
*
*/
static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
esp_err_t ret =0;
uint8_t send_buff[4];
send_buff[0] = (AC101_ADDR << 1);
send_buff[1] = reg;
send_buff[2] = (val>>8) & 0xff;
send_buff[3] = val & 0xff;
ret |= i2c_master_start(cmd);
ret |= i2c_master_write(cmd, send_buff, 4, ACK_CHECK_EN);
ret |= i2c_master_stop(cmd);
ret |= i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/****************************************************************************************
*
*/
static uint16_t i2c_read_reg(uint8_t reg) {
uint8_t data[2] = { 0 };
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( AC101_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( AC101_ADDR << 1 ) | READ_BIT, ACK_CHECK_EN); //check or not
i2c_master_read(cmd, data, 2, ACK_VAL);
i2c_master_stop(cmd);
i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return (data[0] << 8) + data[1];;
uint16_t value = adac_read_word(AC101_ADDR, HPOUT_CTRL);
if (active) adac_write_word(AC101_ADDR, HPOUT_CTRL, value | EAROUT_EN);
else adac_write_word(AC101_ADDR, HPOUT_CTRL, value & ~EAROUT_EN);
}
/****************************************************************************************
@@ -264,7 +195,7 @@ void set_sample_rate(int rate) {
ESP_LOGW(TAG, "Unknown sample rate %hu", rate);
rate = SAMPLE_RATE_44100;
}
i2c_write_reg(I2S_SR_CTRL, rate);
adac_write_word(AC101_ADDR, I2S_SR_CTRL, rate);
}
/****************************************************************************************
@@ -273,8 +204,8 @@ void set_sample_rate(int rate) {
static void ac101_set_spk_volume(uint8_t volume) {
uint16_t value = max(volume, 100);
value = ((int) value * 0x1f) / 100;
value |= i2c_read_reg(SPKOUT_CTRL) & ~0x1f;
i2c_write_reg(SPKOUT_CTRL, value);
value |= adac_read_word(AC101_ADDR, SPKOUT_CTRL) & ~0x1f;
adac_write_word(AC101_ADDR, SPKOUT_CTRL, value);
}
/****************************************************************************************
@@ -283,8 +214,8 @@ static void ac101_set_spk_volume(uint8_t volume) {
static void ac101_set_earph_volume(uint8_t volume) {
uint16_t value = max(volume, 100);
value = (((int) value * 0x3f) / 100) << 4;
value |= i2c_read_reg(HPOUT_CTRL) & ~(0x3f << 4);
i2c_write_reg(HPOUT_CTRL, value);
value |= adac_read_word(AC101_ADDR, HPOUT_CTRL) & ~(0x3f << 4);
adac_write_word(AC101_ADDR, HPOUT_CTRL, value);
}
#if 0
@@ -292,14 +223,14 @@ static void ac101_set_earph_volume(uint8_t volume) {
* Get normalized (0..100) speaker volume
*/
static int ac101_get_spk_volume(void) {
return ((i2c_read_reg(SPKOUT_CTRL) & 0x1f) * 100) / 0x1f;
return ((adac_read_word(AC101_ADDR, SPKOUT_CTRL) & 0x1f) * 100) / 0x1f;
}
/****************************************************************************************
* Get normalized (0..100) earphone volume
*/
static int ac101_get_earph_volume(void) {
return (((i2c_read_reg(HPOUT_CTRL) >> 4) & 0x3f) * 100) / 0x3f;
return (((adac_read_word(AC101_ADDR, HPOUT_CTRL) >> 4) & 0x3f) * 100) / 0x3f;
}
/****************************************************************************************
@@ -308,7 +239,7 @@ static int ac101_get_earph_volume(void) {
static void ac101_set_output_mixer_gain(ac_output_mixer_gain_t gain,ac_output_mixer_source_t source)
{
uint16_t regval,temp,clrbit;
regval = i2c_read_reg(OMIXER_BST1_CTRL);
regval = adac_read_word(AC101_ADDR, OMIXER_BST1_CTRL);
switch(source){
case SRC_MIC1:
temp = (gain&0x7) << 6;
@@ -327,14 +258,15 @@ static void ac101_set_output_mixer_gain(ac_output_mixer_gain_t gain,ac_output_mi
}
regval &= clrbit;
regval |= temp;
i2c_write_reg(OMIXER_BST1_CTRL,regval);
adac_write_word(AC101_ADDR, OMIXER_BST1_CTRL,regval);
}
/****************************************************************************************
*
*/
static void ac101_deinit(void) {
i2c_write_reg(CHIP_AUDIO_RS, 0x123); //soft reset
static void deinit(void) {
adac_write_word(AC101_ADDR, CHIP_AUDIO_RS, 0x123); //soft reset
adac_deinit();
}
/****************************************************************************************
@@ -342,11 +274,11 @@ static void ac101_deinit(void) {
*/
static void ac101_i2s_config_clock(ac_i2s_clock_t *cfg) {
uint16_t regval=0;
regval = i2c_read_reg(I2S1LCK_CTRL);
regval = adac_read_word(AC101_ADDR, I2S1LCK_CTRL);
regval &= 0xe03f;
regval |= (cfg->bclk_div << 9);
regval |= (cfg->lclk_div << 6);
i2c_write_reg(I2S1LCK_CTRL, regval);
adac_write_word(AC101_ADDR, I2S1LCK_CTRL, regval);
}
#endif
@@ -356,21 +288,21 @@ static void ac101_i2s_config_clock(ac_i2s_clock_t *cfg) {
*/
static void ac101_start(ac_module_t mode) {
if (mode == AC_MODULE_LINE) {
i2c_write_reg(0x51, 0x0408);
i2c_write_reg(0x40, 0x8000);
i2c_write_reg(0x50, 0x3bc0);
adac_write_word(AC101_ADDR, 0x51, 0x0408);
adac_write_word(AC101_ADDR, 0x40, 0x8000);
adac_write_word(AC101_ADDR, 0x50, 0x3bc0);
}
if (mode == AC_MODULE_ADC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE) {
// I2S1_SDOUT_CTRL
// i2c_write_reg(PLL_CTRL2, 0x8120);
i2c_write_reg(0x04, 0x800c);
i2c_write_reg(0x05, 0x800c);
// res |= i2c_write_reg(0x06, 0x3000);
// adac_write_word(AC101_ADDR, PLL_CTRL2, 0x8120);
adac_write_word(AC101_ADDR, 0x04, 0x800c);
adac_write_word(AC101_ADDR, 0x05, 0x800c);
// res |= adac_write_word(AC101_ADDR, 0x06, 0x3000);
}
if (mode == AC_MODULE_DAC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE) {
uint16_t value = i2c_read_reg(PLL_CTRL2);
uint16_t value = adac_read_word(AC101_ADDR, PLL_CTRL2);
value |= 0x8000;
i2c_write_reg(PLL_CTRL2, value);
adac_write_word(AC101_ADDR, PLL_CTRL2, value);
}
}
@@ -378,8 +310,8 @@ static void ac101_start(ac_module_t mode) {
*
*/
static void ac101_stop(void) {
uint16_t value = i2c_read_reg(PLL_CTRL2);
uint16_t value = adac_read_word(AC101_ADDR, PLL_CTRL2);
value &= ~0x8000;
i2c_write_reg(PLL_CTRL2, value);
adac_write_word(AC101_ADDR, PLL_CTRL2, value);
}

View File

@@ -11,6 +11,7 @@
#include "freertos/FreeRTOS.h"
#include "driver/i2s.h"
#include "driver/i2c.h"
typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
@@ -27,4 +28,12 @@ struct adac_s {
extern const struct adac_s dac_tas57xx;
extern const struct adac_s dac_tas5713;
extern const struct adac_s dac_ac101;
extern const struct adac_s dac_wm8978;
extern const struct adac_s dac_external;
int adac_init(char *config, int i2c_port);
void adac_deinit(void);
esp_err_t adac_write_byte(int i2c_addr, uint8_t reg, uint8_t val);
esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val);
uint8_t adac_read_byte(int i2c_addr, uint8_t reg);
uint16_t adac_read_word(int i2c_addr, uint8_t reg);

View File

@@ -0,0 +1,172 @@
/*
* Squeezelite for esp32
*
* (c) Sebastien 2019
* Philippe G. 2019, philippe_44@outlook.com
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*
*/
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/i2s.h>
#include "driver/i2c.h"
#include "esp_log.h"
#include "adac.h"
static const char TAG[] = "DAC core";
static int i2c_port = -1;
/****************************************************************************************
* init
*/
int adac_init(char *config, int i2c_port_num) {
char *p;
int i2c_addr = 0;
// some crappy codecs require MCLK to work
if ((p = strcasestr(config, "mck")) != NULL) {
ESP_LOGI(TAG, "Configuring MCLK on GPIO0");
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_WRITE(PIN_CTRL, 0xFFFFFFF0);
}
i2c_port = i2c_port_num;
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = -1,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = -1,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 250000,
};
if ((p = strcasestr(config, "i2c")) != NULL) i2c_addr = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
if (i2c_config.sda_io_num == -1 || i2c_config.scl_io_num == -1) {
ESP_LOGW(TAG, "DAC does not use i2c");
return i2c_addr;
}
ESP_LOGI(TAG, "DAC uses I2C port:%d, sda:%d, scl:%d", i2c_port, i2c_config.sda_io_num, i2c_config.scl_io_num);
// we have an I2C configured
i2c_param_config(i2c_port, &i2c_config);
i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
return i2c_addr;
}
/****************************************************************************************
* close
*/
void adac_deinit(void) {
if (i2c_port != -1) i2c_driver_delete(i2c_port);
}
/****************************************************************************************
*
*/
esp_err_t adac_write_byte(int i2c_addr,uint8_t reg, uint8_t val) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, val, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C write failed");
}
return ret;
}
/****************************************************************************************
*
*/
uint8_t adac_read_byte(int i2c_addr, uint8_t reg) {
uint8_t data = 255;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK);
i2c_master_read_byte(cmd, &data, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C read failed");
}
return data;
}
/****************************************************************************************
*
*/
uint16_t adac_read_word(int i2c_addr, uint8_t reg) {
uint8_t data[2] = { 255, 255 };
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK);
i2c_master_read(cmd, data, 2, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C read failed");
}
return (data[0] << 8) | data[1];
}
/****************************************************************************************
*
*/
esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val)
{
uint8_t data[] = { i2c_addr << 1, reg,
val >> 8, val & 0xff };
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write(cmd, data, 4, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C write failed");
}
return ret;
}

View File

@@ -21,7 +21,7 @@
#include "squeezelite.h"
#include <alac_wrapper.h>
#include "alac_wrapper.h"
#if BYTES_PER_FRAME == 4
#define ALIGN8(n) (n << 8)
@@ -119,8 +119,15 @@ static int read_mp4_header(void) {
// extract audio config from within alac
if (!strcmp(type, "alac") && bytes > len) {
u8_t *ptr = streambuf->readp + 36;
l->decoder = alac_create_decoder(len - 36, ptr, &l->sample_size, &l->sample_rate, &l->channels);
unsigned int block_size;
l->play = l->trak;
l->decoder = alac_create_decoder(len - 36, ptr, &l->sample_size, &l->sample_rate, &l->channels, &block_size);
l->writebuf = malloc(block_size + 256);
LOG_INFO("allocated write buffer of %u bytes", block_size);
if (!l->writebuf) {
LOG_ERROR("allocation failed");
return -1;
}
}
// extract the total number of samples from stts
@@ -374,10 +381,9 @@ static decode_state alac_decode(void) {
// need to create a buffer with contiguous data
if (bytes < block_size) {
u8_t *buffer = malloc(block_size);
memcpy(buffer, streambuf->readp, bytes);
memcpy(buffer + bytes, streambuf->buf, block_size - bytes);
iptr = buffer;
iptr = malloc(block_size);
memcpy(iptr, streambuf->readp, bytes);
memcpy(iptr + bytes, streambuf->buf, block_size - bytes);
} else iptr = streambuf->readp;
if (!alac_to_pcm(l->decoder, iptr, l->writebuf, 2, &frames)) {
@@ -472,6 +478,7 @@ static decode_state alac_decode(void) {
}
} else if (l->sample_size == 16) {
u16_t *_iptr = (u16_t*) iptr;
iptr += count * 4;
while (count--) {
*optr++ = ALIGN16(*_iptr++);
*optr++ = ALIGN16(*_iptr++);
@@ -484,6 +491,7 @@ static decode_state alac_decode(void) {
}
} else if (l->sample_size == 32) {
u32_t *_iptr = (u32_t*) iptr;
iptr += count * 8;
while (count--) {
*optr++ = ALIGN32(*_iptr++);
*optr++ = ALIGN32(*_iptr++);
@@ -509,27 +517,17 @@ static decode_state alac_decode(void) {
return DECODE_RUNNING;
}
static void alac_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
if (l->decoder) alac_delete_decoder(l->decoder);
else l->writebuf = malloc(BLOCK_SIZE * 2);
if (l->chunkinfo) free(l->chunkinfo);
if (l->block_size) free(l->block_size);
if (l->stsc) free(l->stsc);
l->decoder = l->chunkinfo = l->stsc = l->block_size = NULL;
l->skip = 0;
l->samples = l->sttssamples = 0;
l->empty = false;
l->pos = l->consume = l->sample = l->nextchunk = 0;
}
static void alac_close(void) {
if (l->decoder) alac_delete_decoder(l->decoder);
if (l->writebuf) free(l->writebuf);
if (l->chunkinfo) free(l->chunkinfo);
if (l->block_size) free(l->block_size);
if (l->stsc) free(l->stsc);
l->decoder = l->chunkinfo = l->stsc = l->block_size = NULL;
free(l->writebuf);
memset(l, 0, sizeof(struct alac));
}
static void alac_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
alac_close();
}
struct codec *register_alac(void) {
@@ -543,12 +541,8 @@ struct codec *register_alac(void) {
alac_decode, // decode
};
l = malloc(sizeof(struct alac));
if (!l) {
return NULL;
}
l->decoder = l->chunkinfo = l->stsc = l->block_size = NULL;
l = calloc(1, sizeof(struct alac));
if (!l) return NULL;
LOG_INFO("using alac to decode alc");
return &ret;

View File

@@ -20,7 +20,7 @@ CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ON
# -I$(COMPONENT_PATH)/../codecs/inc/faad2
COMPONENT_SRCDIRS := . tas57xx ac101 external
COMPONENT_SRCDIRS := . tas57xx ac101 external wm8978
COMPONENT_ADD_INCLUDEDIRS := . ./tas57xx ./ac101
COMPONENT_EMBED_FILES := vu.data

View File

@@ -97,22 +97,6 @@ static void sendIR(u16_t addr, u16_t cmd) {
UNLOCK_P;
}
static void lms_volume_up(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_VOLUP_FRONT, pressed);
} else {
cli_send_cmd("button volup");
}
}
static void lms_volume_down(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_VOLDOWN_FRONT, pressed);
} else {
cli_send_cmd("button voldown");
}
}
static void lms_toggle(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_PAUSE, pressed);
@@ -129,117 +113,79 @@ static void lms_pause(bool pressed) {
}
}
static void lms_play(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_PLAY, pressed);
} else {
cli_send_cmd("button play.single");
}
}
static void lms_stop(bool pressed) {
cli_send_cmd("button stop");
}
static void lms_rew(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_REW, pressed);
} else {
cli_send_cmd("button rew.repeat");
}
#define LMS_CALLBACK(N,B,E) \
static void lms_##N (bool pressed) { \
if (raw_mode) { \
sendBUTN( BUTN_##B , pressed ); \
} else { \
cli_send_cmd("button" " " #E); \
} \
}
static void lms_fwd(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_FWD, pressed);
} else {
cli_send_cmd("button fwd.repeat");
}
}
LMS_CALLBACK(power, POWER_FRONT, power)
LMS_CALLBACK(play, PLAY, play.single)
static void lms_prev(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_REW, pressed);
} else {
cli_send_cmd("button rew");
}
}
LMS_CALLBACK(volup, VOLUP_FRONT, volup)
LMS_CALLBACK(voldown, VOLDOWN_FRONT, voldown)
static void lms_next(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_FWD, pressed);
} else {
cli_send_cmd("button fwd");
}
}
LMS_CALLBACK(rew, REW, rew.repeat)
LMS_CALLBACK(fwd, FWD, fwd.repeat)
LMS_CALLBACK(prev, REW, rew)
LMS_CALLBACK(next, FWD, fwd)
static void lms_up(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_ARROW_UP, pressed);
} else {
cli_send_cmd("button arrow_up");
}
}
LMS_CALLBACK(up, ARROW_UP, arrow_up)
LMS_CALLBACK(down, ARROW_DOWN, arrow_down)
LMS_CALLBACK(left, ARROW_LEFT, arrow_left)
LMS_CALLBACK(right, ARROW_RIGHT, arrow_right)
static void lms_down(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_ARROW_DOWN, pressed);
} else {
cli_send_cmd("button arrow_down");
}
}
LMS_CALLBACK(pre1, PRESET_1, preset_1.single)
LMS_CALLBACK(pre2, PRESET_2, preset_2.single)
LMS_CALLBACK(pre3, PRESET_3, preset_3.single)
LMS_CALLBACK(pre4, PRESET_4, preset_4.single)
LMS_CALLBACK(pre5, PRESET_5, preset_5.single)
LMS_CALLBACK(pre6, PRESET_6, preset_6.single)
static void lms_left(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_ARROW_LEFT, pressed);
} else {
cli_send_cmd("button arrow_left");
}
}
static void lms_right(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_ARROW_RIGHT, pressed);
} else {
cli_send_cmd("button arrow_right");
}
}
static void lms_knob_left(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_KNOB_LEFT, pressed);
} else {
cli_send_cmd("button knob_left");
}
}
static void lms_knob_right(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_KNOB_RIGHT, pressed);
} else {
cli_send_cmd("button knob_right");
}
}
static void lms_knob_push(bool pressed) {
if (raw_mode) {
sendBUTN(BUTN_KNOB_PUSH, pressed);
} else {
cli_send_cmd("button knob_push");
}
}
LMS_CALLBACK(knob_left, KNOB_LEFT, knob_left)
LMS_CALLBACK(knob_right, KNOB_RIGHT, knob_right)
LMS_CALLBACK(knob_push, KNOB_PUSH, knob_push)
const actrls_t LMS_controls = {
lms_volume_up, lms_volume_down, // volume up, volume down
lms_power,
lms_volup, lms_voldown, // volume up, volume down
lms_toggle, lms_play, // toggle, play
lms_pause, lms_stop, // pause, stop
lms_rew, lms_fwd, // rew, fwd
lms_prev, lms_next, // prev, next
lms_up, lms_down,
lms_left, lms_right,
lms_pre1, lms_pre2, lms_pre3, lms_pre4, lms_pre5, lms_pre6,
lms_knob_left, lms_knob_right, lms_knob_push,
};
/****************************************************************************************
*
*/
static void connect_cli_socket(void) {
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = server_ip,
.sin_port = htons(server_cport),
};
socklen_t addrlen = sizeof(addr);
cli_sock = socket(AF_INET, SOCK_STREAM, 0);
if (connect(cli_sock, (struct sockaddr *) &addr, addrlen) < 0) {
LOG_ERROR("unable to connect to server %s:%hu with cli", inet_ntoa(server_ip), server_cport);
closesocket(cli_sock);
cli_sock = -1;
}
}
/****************************************************************************************
*
*/
@@ -250,7 +196,11 @@ static void cli_send_cmd(char *cmd) {
len = sprintf(packet, "%02x:%02x:%02x:%02x:%02x:%02x %s\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], cmd);
LOG_DEBUG("sending command %s at %s:%hu", packet, inet_ntoa(server_ip), server_cport);
if (cli_sock < 0) connect_cli_socket();
if (send(cli_sock, packet, len, MSG_DONTWAIT) < 0) {
closesocket(cli_sock);
cli_sock = -1;
LOG_WARN("cannot send CLI %s", packet);
}
@@ -262,25 +212,13 @@ static void cli_send_cmd(char *cmd) {
* Notification when server changes
*/
static void notify(in_addr_t ip, u16_t hport, u16_t cport) {
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
server_ip = ip;
server_hport = hport;
server_cport = cport;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = server_ip;
addr.sin_port = htons(server_cport);
// close existing CLI connection and open new one
if (cli_sock >= 0) closesocket(cli_sock);
cli_sock = socket(AF_INET, SOCK_STREAM, 0);
if (connect(cli_sock, (struct sockaddr *) &addr, addrlen) < 0) {
LOG_ERROR("unable to connect to server %s:%hu with cli", inet_ntoa(server_ip), server_cport);
cli_sock = -1;
}
connect_cli_socket();
LOG_INFO("notified server %s hport %hu cport %hu", inet_ntoa(ip), hport, cport);

View File

@@ -31,7 +31,7 @@ extern log_level loglevel;
static bool enable_bt_sink;
static bool enable_airplay;
#define RAOP_OUTPUT_SIZE (RAOP_SAMPLE_RATE * 2 * 2 * 2 * 1.2)
#define RAOP_OUTPUT_SIZE (((RAOP_SAMPLE_RATE * BYTES_PER_FRAME * 2 * 120) / 100) & ~BYTES_PER_FRAME)
#define SYNC_WIN_SLOW 32
#define SYNC_WIN_CHECK 8
#define SYNC_WIN_FAST 2
@@ -63,19 +63,19 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
while (len) {
LOCK_O;
bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf));
bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / (BYTES_PER_FRAME / 4);
bytes = min(len, bytes);
#if BYTES_PER_FRAME == 4
memcpy(outputbuf->writep, data, bytes);
#else
{
s16_t *iptr = (s16_t*) data;
ISAMPLE_T *optr = (ISAMPLE_T*) outputbuf->writep;
size_t n = bytes / BYTES_PER_FRAME * 2;
ISAMPLE_T *optr = (ISAMPLE_T *) outputbuf->writep;
size_t n = bytes / 2;
while (n--) *optr++ = *iptr++ << 16;
}
#endif
_buf_inc_writep(outputbuf, bytes);
_buf_inc_writep(outputbuf, bytes * BYTES_PER_FRAME / 4);
space = _buf_space(outputbuf);
len -= bytes;
@@ -188,7 +188,6 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
// this is async, so player might have been deleted
switch (event) {
case RAOP_TIMING: {
if (!raop_sync.enabled || output.state != OUTPUT_RUNNING || output.frames_played_dmp < output.device_frames) break;
u32_t ms, now = gettime_ms();
@@ -327,12 +326,12 @@ void register_external(void) {
void deregister_external(void) {
if (!strcasestr(output.device, "BT ") && enable_bt_sink) {
bt_sink_deinit();
LOG_INFO("Stopping BT sink");
bt_sink_deinit();
}
if (enable_airplay){
raop_sink_deinit();
LOG_INFO("Stopping AirPlay sink");
raop_sink_deinit();
}
}

View File

@@ -113,14 +113,23 @@ struct ANIC_header {
u8_t mode;
};
struct dmxt_packet {
char opcode[4];
u16_t x;
u16_t length;
};
#pragma pack(pop)
static struct {
TaskHandle_t task;
SemaphoreHandle_t mutex;
int width, height;
bool dirty;
int wake;
bool owned;
struct {
SemaphoreHandle_t mutex;
int width, height;
bool dirty;
};
} displayer = { .dirty = true, .owned = true };
static uint32_t *grayMap;
@@ -143,13 +152,13 @@ static uint32_t *grayMap;
static struct scroller_s {
// copy of grfs content
u8_t screen;
u32_t pause, speed;
int wake;
u32_t pause;
u16_t mode;
s16_t by;
// scroller management & sharing between grfg and scrolling task
bool active, first, overflow;
int scrolled;
int speed, wake;
struct {
u8_t *frame;
u32_t width;
@@ -167,7 +176,7 @@ static struct {
u8_t *data;
u32_t size;
u16_t x, y;
bool enable;
bool enable, full;
} artwork;
#define MAX_BARS 32
@@ -175,15 +184,13 @@ static struct {
static EXT_RAM_ATTR struct {
int bar_gap, bar_width, bar_border;
bool rotate;
struct {
struct bar_s {
int current, max;
int limit;
} bars[MAX_BARS];
float spectrum_scale;
int n, col, row, height, width, border, style, max;
enum { VISU_BLANK, VISU_VUMETER, VISU_SPECTRUM, VISU_WAVEFORM } mode;
int speed, wake;
float fft[FFT_LEN*2], samples[FFT_LEN*2], hanning[FFT_LEN];
enum { VISU_BLANK, VISU_VUMETER = 0x01, VISU_SPECTRUM = 0x02, VISU_WAVEFORM } mode;
struct {
u8_t *frame;
int width;
@@ -191,6 +198,18 @@ static EXT_RAM_ATTR struct {
} back;
} visu;
static EXT_RAM_ATTR struct {
float fft[FFT_LEN*2], samples[FFT_LEN*2], hanning[FFT_LEN];
int levels[2];
} meters;
static EXT_RAM_ATTR struct {
int mode;
int max;
u16_t config;
struct bar_s bars[MAX_BARS] ;
} led_visu;
extern const uint8_t vu_bitmap[] asm("_binary_vu_data_start");
#define ANIM_NONE 0x00
@@ -211,7 +230,7 @@ static bool (*display_bus_chain)(void *from, enum display_bus_cmd_e cmd);
#define max(a,b) (((a) > (b)) ? (a) : (b))
static void server(in_addr_t ip, u16_t hport, u16_t cport);
static void sendSETD(u16_t width, u16_t height);
static void sendSETD(u16_t width, u16_t height, u16_t led_config);
static void sendANIC(u8_t code);
static bool handler(u8_t *data, int len);
static bool display_bus_handler(void *from, enum display_bus_cmd_e cmd);
@@ -222,8 +241,11 @@ static void grfs_handler(u8_t *data, int len);
static void grfg_handler(u8_t *data, int len);
static void grfa_handler(u8_t *data, int len);
static void visu_handler(u8_t *data, int len);
static void dmxt_handler(u8_t *data, int len);
static void displayer_task(void* arg);
void *led_display;
/* scrolling undocumented information
grfs
B: screen number
@@ -281,45 +303,56 @@ static void displayer_task(void* arg);
/****************************************************************************************
*
*/
bool sb_display_init(void) {
bool sb_displayer_init(void) {
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
static EXT_RAM_ATTR StackType_t xStack[SCROLL_STACK_SIZE] __attribute__ ((aligned (4)));
// no display, just make sure we won't have requests
if (!display || GDS_GetWidth(display) <= 0 || GDS_GetHeight(display) <= 0) {
LOG_INFO("no display for LMS");
if ((GDS_GetWidth(display) <= 0 || GDS_GetHeight(display) <= 0) && !led_display) {
LOG_INFO("no display or led visualizer for LMS");
return false;
}
// inform LMS of our screen dimensions
sendSETD(GDS_GetWidth(display), GDS_GetHeight(display));
if (display) {
// need to force height to 32 maximum
displayer.width = GDS_GetWidth(display);
displayer.height = min(GDS_GetHeight(display), SB_HEIGHT);
// need to force height to 32 maximum
displayer.width = GDS_GetWidth(display);
displayer.height = min(GDS_GetHeight(display), SB_HEIGHT);
// allocate gray-color mapping if needed;
if (GDS_GetMode(display) > GDS_GRAYSCALE) {
grayMap = malloc(256*sizeof(*grayMap));
for (int i = 0; i < 256; i++) grayMap[i] = GDS_GrayMap(display, i);
}
// allocate gray-color mapping if needed;
if (GDS_GetMode(display) > GDS_GRAYSCALE) {
grayMap = malloc(256*sizeof(*grayMap));
for (int i = 0; i < 256; i++) grayMap[i] = GDS_GrayMap(display, i);
// create visu configuration
visu.bar_gap = 1;
visu.back.frame = calloc(1, (displayer.width * displayer.height) / 8);
// size scroller (width + current screen)
scroller.scroll.max = (displayer.width * displayer.height / 8) * (15 + 1);
scroller.scroll.frame = malloc(scroller.scroll.max);
scroller.back.frame = malloc(displayer.width * displayer.height / 8);
scroller.frame = malloc(displayer.width * displayer.height / 8);
// chain handlers
display_bus_chain = display_bus;
display_bus = display_bus_handler;
}
// create visu configuration
visu.bar_gap = 1;
visu.speed = 100;
visu.back.frame = calloc(1, (displayer.width * displayer.height) / 8);
dsps_fft2r_init_fc32(visu.fft, FFT_LEN);
dsps_wind_hann_f32(visu.hanning, FFT_LEN);
if (led_display) {
// PLACEHOLDER to init config
led_visu.mode = VISU_VUMETER;
}
// create scroll management task
// inform LMS of our screen/led dimensions
sendSETD(GDS_GetWidth(display), GDS_GetHeight(display), led_visu.config);
dsps_fft2r_init_fc32(meters.fft, FFT_LEN);
dsps_wind_hann_f32(meters.hanning, FFT_LEN);
// create displayer management task
displayer.mutex = xSemaphoreCreateMutex();
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
// size scroller (width + current screen)
scroller.scroll.max = (displayer.width * displayer.height / 8) * (15 + 1);
scroller.scroll.frame = malloc(scroller.scroll.max);
scroller.back.frame = malloc(displayer.width * displayer.height / 8);
scroller.frame = malloc(displayer.width * displayer.height / 8);
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "squeeze_displayer", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
// chain handlers
slimp_handler_chain = slimp_handler;
@@ -328,10 +361,7 @@ bool sb_display_init(void) {
notify_chain = server_notify;
server_notify = server;
display_bus_chain = display_bus;
display_bus = display_bus_handler;
return true;
return display != NULL;
}
/****************************************************************************************
@@ -380,14 +410,14 @@ static void sendANIC(u8_t code) {
/****************************************************************************************
* Send SETD for width
*/
static void sendSETD(u16_t width, u16_t height) {
static void sendSETD(u16_t width, u16_t height, u16_t led_config) {
struct SETD_header pkt_header;
memset(&pkt_header, 0, sizeof(pkt_header));
memcpy(&pkt_header.opcode, "SETD", 4);
pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2
pkt_header.length = htonl(sizeof(pkt_header) + 4 - 8);
pkt_header.length = htonl(sizeof(pkt_header) + 6 - 8);
LOG_INFO("sending dimension %ux%u", width, height);
@@ -398,6 +428,7 @@ static void sendSETD(u16_t width, u16_t height) {
send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
send_packet((uint8_t *) &width, 2);
send_packet((uint8_t *) &height, 2);
send_packet((uint8_t *) &led_config, 2);
UNLOCK_P;
}
@@ -410,13 +441,13 @@ static void server(in_addr_t ip, u16_t hport, u16_t cport) {
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
sprintf(msg, "%s:%hu", inet_ntoa(ip), hport);
if (displayer.owned) GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg);
if (display && displayer.owned) GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg);
displayer.dirty = true;
xSemaphoreGive(displayer.mutex);
// inform new LMS server of our capabilities
sendSETD(displayer.width, GDS_GetHeight(display));
sendSETD(GDS_GetWidth(display), GDS_GetHeight(display), led_visu.config);
if (notify_chain) (*notify_chain)(ip, hport, cport);
}
@@ -441,6 +472,8 @@ static bool handler(u8_t *data, int len){
grfa_handler(data, len);
} else if (!strncmp((char*) data, "visu", 4)) {
visu_handler(data, len);
} else if (!strncmp((char*) data, "dmxt", 4)) {
dmxt_handler(data, len);
} else {
res = false;
}
@@ -503,7 +536,8 @@ static void show_display_buffer(char *ddram) {
char *line2;
memset(line1, 0, LINELEN+1);
strncpy(line1, ddram, LINELEN);
strncpy(line1, ddram, LINELEN+1);
line1[LINELEN] = '\0';
line2 = &(ddram[LINELEN]);
line2[LINELEN] = '\0';
@@ -623,14 +657,20 @@ void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x,
* Process graphic display data
*/
static void grfe_handler( u8_t *data, int len) {
struct grfe_packet *pkt = (struct grfe_packet*) data;
// we don't support transition, simply claim we're done
if (pkt->transition != 'c') {
LOG_INFO("Transition %c requested with offset %hu, param %d", pkt->transition, pkt->offset, pkt->param);
sendANIC(ANIM_TRANSITION);
}
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
scroller.active = false;
// full screen artwork or for small screen, full screen visu has priority
if (((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) ||
(artwork.enable && artwork.x == 0 && artwork.y == 0)) {
if (((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) || artwork.full) {
xSemaphoreGive(displayer.mutex);
return;
}
@@ -753,8 +793,7 @@ static void grfg_handler(u8_t *data, int len) {
LOG_DEBUG("gfrg s:%hu w:%hu (len:%u)", htons(pkt->screen), htons(pkt->width), len);
// full screen artwork or for small screen, visu has priority when full screen
if (((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) ||
(artwork.enable && artwork.x == 0 && artwork.y == 0)) {
if (((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) || artwork.full) {
return;
}
@@ -808,6 +847,9 @@ static void grfa_handler(u8_t *data, int len) {
artwork.y = htons(pkt->y);
} else if (artwork.size) GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK);
artwork.full = artwork.enable && artwork.x == 0 && artwork.y == 0;
LOG_INFO("gfra en:%u x:%hu, y:%hu", artwork.enable, artwork.x, artwork.y);
// done in any case
return;
}
@@ -823,6 +865,7 @@ static void grfa_handler(u8_t *data, int len) {
// now use new parameters
artwork.x = htons(pkt->x);
artwork.y = htons(pkt->y);
artwork.full = artwork.enable && artwork.x == 0 && artwork.y == 0;
if (artwork.data) free(artwork.data);
artwork.data = malloc(length);
}
@@ -841,93 +884,57 @@ static void grfa_handler(u8_t *data, int len) {
}
/****************************************************************************************
* Update visualization bars
* Fit spectrum into N bands and convert to dB
*/
static void visu_update(void) {
// no update when artwork is full screen (but no need to protect against not owning the display as we are playing
if ((artwork.enable && artwork.x == 0 && artwork.y == 0) || pthread_mutex_trylock(&visu_export.mutex)) {
return;
}
void spectrum_scale(int n, struct bar_s *bars, int max, float *samples) {
float rate = visu_export.rate;
// now arrange the result with the number of bar and sampling rate (don't want DC)
for (int i = 0, j = 1; i < n && j < (FFT_LEN / 2); i++) {
float power, count;
int mode = visu.mode & ~VISU_ESP32;
// not enough samples
if (visu_export.level < (mode == VISU_VUMETER ? RMS_LEN : FFT_LEN) * 2 && visu_export.running) {
pthread_mutex_unlock(&visu_export.mutex);
return;
}
// reset bars for all cases first
for (int i = visu.n; --i >= 0;) visu.bars[i].current = 0;
if (visu_export.running) {
if (mode == VISU_VUMETER) {
s16_t *iptr = visu_export.buffer;
// calculate sum(L²+R²), try to not overflow at the expense of some precision
for (int i = RMS_LEN; --i >= 0;) {
visu.bars[0].current += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
iptr++;
visu.bars[1].current += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
iptr++;
}
// convert to dB (1 bit remaining for getting X²/N, 60dB dynamic starting from 0dBFS = 3 bits back-off)
for (int i = visu.n; --i >= 0;) {
visu.bars[i].current = visu.max * (0.01667f*10*log10f(0.0000001f + (visu.bars[i].current >> (visu_export.gain == FIXED_ONE ? 7 : 1))) - 0.2543f);
if (visu.bars[i].current > visu.max) visu.bars[i].current = visu.max;
else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
}
} else {
// on xtensa/esp32 the floating point FFT takes 1/2 cycles of the fixed point
for (int i = 0 ; i < FFT_LEN ; i++) {
// don't normalize here, but we are due INT16_MAX and FFT_LEN / 2 / 2
visu.samples[i * 2 + 0] = (float) (visu_export.buffer[2*i] + visu_export.buffer[2*i + 1]) * visu.hanning[i];
visu.samples[i * 2 + 1] = 0;
}
// actual FFT that might be less cycle than all the crap below
dsps_fft2r_fc32_ae32(visu.samples, FFT_LEN);
dsps_bit_rev_fc32_ansi(visu.samples, FFT_LEN);
float rate = visu_export.rate;
// now arrange the result with the number of bar and sampling rate (don't want DC)
for (int i = 0, j = 1; i < visu.n && j < (FFT_LEN / 2); i++) {
float power, count;
// find the next point in FFT (this is real signal, so only half matters)
for (count = 0, power = 0; j * visu_export.rate < visu.bars[i].limit * FFT_LEN && j < FFT_LEN / 2; j++, count += 1) {
power += visu.samples[2*j] * visu.samples[2*j] + visu.samples[2*j+1] * visu.samples[2*j+1];
}
// due to sample rate, we have reached the end of the available spectrum
if (j >= (FFT_LEN / 2)) {
// normalize accumulated data
if (count) power /= count * 2.;
} else if (count) {
// how much of what remains do we need to add
float ratio = j - (visu.bars[i].limit * FFT_LEN) / rate;
power += (visu.samples[2*j] * visu.samples[2*j] + visu.samples[2*j+1] * visu.samples[2*j+1]) * ratio;
// normalize accumulated data
power /= (count + ratio) * 2;
} else {
// no data for that band (sampling rate too high), just assume same as previous one
power = (visu.samples[2*j] * visu.samples[2*j] + visu.samples[2*j+1] * visu.samples[2*j+1]) / 2.;
}
// convert to dB and bars, same back-off
if (power) visu.bars[i].current = visu.max * (0.01667f*10*(log10f(power) - log10f(FFT_LEN/(visu_export.gain == FIXED_ONE ? 128 : 2)*2)) - 0.2543f);
if (visu.bars[i].current > visu.max) visu.bars[i].current = visu.max;
else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
}
// find the next point in FFT (this is real signal, so only half matters)
for (count = 0, power = 0; j * visu_export.rate < bars[i].limit * FFT_LEN && j < FFT_LEN / 2; j++, count += 1) {
power += samples[2*j] * samples[2*j] + samples[2*j+1] * samples[2*j+1];
}
// due to sample rate, we have reached the end of the available spectrum
if (j >= (FFT_LEN / 2)) {
// normalize accumulated data
if (count) power /= count * 2.;
} else if (count) {
// how much of what remains do we need to add
float ratio = j - (bars[i].limit * FFT_LEN) / rate;
power += (samples[2*j] * samples[2*j] + samples[2*j+1] * samples[2*j+1]) * ratio;
// normalize accumulated data
power /= (count + ratio) * 2;
} else {
// no data for that band (sampling rate too high), just assume same as previous one
power = (samples[2*j] * samples[2*j] + samples[2*j+1] * samples[2*j+1]) / 2.;
}
// convert to dB and bars, same back-off
bars[i].current = max * (0.01667f*10*(log10f(0.0000001f + power) - log10f(FFT_LEN*(visu_export.gain == FIXED_ONE ? 256 : 2))) - 0.2543f);
if (bars[i].current > max) bars[i].current = max;
else if (bars[i].current < 0) bars[i].current = 0;
}
}
// we took what we want, we can release the buffer
visu_export.level = 0;
pthread_mutex_unlock(&visu_export.mutex);
/****************************************************************************************
* Fit levels to max and convert to dB
*/
void vu_scale(struct bar_s *bars, int max, int *levels) {
// convert to dB (1 bit remaining for getting X²/N, 60dB dynamic starting from 0dBFS = 3 bits back-off)
for (int i = 2; --i >= 0;) {
bars[i].current = max * (0.01667f*10*log10f(0.0000001f + (levels[i] >> (visu_export.gain == FIXED_ONE ? 8 : 1))) - 0.2543f);
if (bars[i].current > max) bars[i].current = max;
else if (bars[i].current < 0) bars[i].current = 0;
}
}
/****************************************************************************************
* visu draw
*/
void visu_draw(void) {
// don't refresh screen if all max are 0 (we were are somewhat idle)
int clear = 0;
for (int i = visu.n; --i >= 0;) clear = max(clear, visu.bars[i].max);
@@ -938,9 +945,8 @@ static void visu_update(void) {
GDS_DrawBitmapCBR(display, visu.back.frame, visu.back.width, displayer.height, GDS_COLOR_WHITE);
}
if (mode != VISU_VUMETER || !visu.style) {
if ((visu.mode & ~VISU_ESP32) != VISU_VUMETER || !visu.style) {
// there is much more optimization to be done here, like not redrawing bars unless needed
for (int i = visu.n; --i >= 0;) {
// update maximum
if (visu.bars[i].current > visu.bars[i].max) visu.bars[i].max = visu.bars[i].current;
@@ -970,7 +976,7 @@ static void visu_update(void) {
}
}
}
} else if (displayer.width / 2 > 3 * VU_WIDTH / 4) {
} else if (displayer.width / 2 >= 3 * VU_WIDTH / 4) {
if (visu.rotate) {
draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, visu.height / 2, visu.rotate);
draw_VU(display, vu_bitmap, visu.bars[1].current, 0, visu.row + visu.height / 2, visu.height / 2, visu.rotate);
@@ -984,6 +990,77 @@ static void visu_update(void) {
}
}
/****************************************************************************************
* Update displayer
*/
static void displayer_update(void) {
// no update when artwork is full screen and no led_strip (but no need to protect against not owning the display as we are playing
if ((artwork.full && !led_visu.mode) || pthread_mutex_trylock(&visu_export.mutex)) {
return;
}
int mode = (visu.mode & ~VISU_ESP32) | led_visu.mode;
// not enough frames
if (visu_export.level < (mode & VISU_SPECTRUM ? FFT_LEN : RMS_LEN) && visu_export.running) {
pthread_mutex_unlock(&visu_export.mutex);
return;
}
// reset all levels no matter what
meters.levels[0] = meters.levels[1] = 0;
memset(meters.samples, 0, sizeof(meters.samples));
if (visu_export.running) {
// calculate data for VU-meter
if (mode & VISU_VUMETER) {
s16_t *iptr = (s16_t*) visu_export.buffer + (BYTES_PER_FRAME / 4) - 1;
int *left = &meters.levels[0], *right = &meters.levels[1];
// calculate sum(L²+R²), try to not overflow at the expense of some precision
for (int i = RMS_LEN; --i >= 0;) {
*left += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
iptr += BYTES_PER_FRAME / 4;
*right += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
iptr += BYTES_PER_FRAME / 4;
}
}
// calculate data for spectrum
if (mode & VISU_SPECTRUM) {
s16_t *iptr = (s16_t*) visu_export.buffer + (BYTES_PER_FRAME / 4) - 1;
// on xtensa/esp32 the floating point FFT takes 1/2 cycles of the fixed point
for (int i = 0 ; i < FFT_LEN ; i++) {
// don't normalize here, but we are due INT16_MAX and FFT_LEN / 2 / 2
meters.samples[i * 2 + 0] = (float) (*iptr + *(iptr+BYTES_PER_FRAME/4)) * meters.hanning[i];
meters.samples[i * 2 + 1] = 0;
iptr += 2 * BYTES_PER_FRAME / 4;
}
// actual FFT that might be less cycle than all the crap below
dsps_fft2r_fc32_ae32(meters.samples, FFT_LEN);
dsps_bit_rev_fc32_ansi(meters.samples, FFT_LEN);
}
}
// we took what we want, we can release the buffer
visu_export.level = 0;
pthread_mutex_unlock(&visu_export.mutex);
// actualize the display
if (visu.mode && !artwork.full) {
if (visu.mode & VISU_SPECTRUM) spectrum_scale(visu.n, visu.bars, visu.max, meters.samples);
else for (int i = 2; --i >= 0;) vu_scale(visu.bars, visu.max, meters.levels);
visu_draw();
}
// actualize led_vu
if (led_visu.mode) {
// PLACEHOLDER to handle led_display. you need potentially scaling of spectrum (X and Y)
// and scaling of levels (Y) and then call the
}
}
/****************************************************************************************
* Calculate spectrum spread
@@ -1065,13 +1142,27 @@ static void visu_handler( u8_t *data, int len) {
bars = htonl(pkt->bars);
visu.spectrum_scale = htonl(pkt->spectrum_scale) / 100.;
} else {
// full screen visu, try to use bottom screen if available
// full screen visu, try to optimize orientation/shape
visu.width = htonl(pkt->full.width);
visu.height = GDS_GetHeight(display) > displayer.height ? GDS_GetHeight(display) - displayer.height : GDS_GetHeight(display);
visu.row = GDS_GetHeight(display) - visu.height;
visu.height = GDS_GetHeight(display);
// try to estimate if we should rotate visu
if (visu.height > displayer.height && visu.height > 2*visu.width) visu.rotate = true;
// do we have enough height to play with layout
if (GDS_GetHeight(display) > displayer.height) {
// by default, use up to the bottom of the display
visu.height -= displayer.height;
visu.row = displayer.height;
if (artwork.enable && artwork.y) {
// server sets width to artwork X offset to tell us to rotate
if (visu.width != artwork.x) {
visu.height = artwork.y - displayer.height;
if (visu.height <= 0) {
visu.height = displayer.height;
LOG_WARN("No room left for visualizer, disable it or increase artwork offset %d", artwork.y);
}
} else visu.rotate = true;
}
} else visu.row = 0;
// is this spectrum or analogue/digital
if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) {
@@ -1094,9 +1185,10 @@ static void visu_handler( u8_t *data, int len) {
visu.style = htonl(pkt->classical_vu.style);
if (visu.style) visu.row = visu.height - VU_HEIGHT;
}
if (bars > MAX_BARS) bars = MAX_BARS;
}
if (bars > MAX_BARS) bars = MAX_BARS;
// for rotate, swap width & height
if (visu.rotate) visu_fit(bars, visu.height, visu.width);
else visu_fit(bars, visu.width, visu.height);
@@ -1110,7 +1202,7 @@ static void visu_handler( u8_t *data, int len) {
if (visu.row < displayer.height) scroller.active = false;
vTaskResume(displayer.task);
}
visu.wake = 0;
displayer.wake = 0;
// reset bars maximum
for (int i = visu.n; --i >= 0;) visu.bars[i].max = 0;
@@ -1125,6 +1217,25 @@ static void visu_handler( u8_t *data, int len) {
xSemaphoreGive(displayer.mutex);
}
/****************************************************************************************
* Dmx style packet handler
* ToDo: make packet match dmx protocol format
*/
static void dmxt_handler( u8_t *data, int len) {
struct dmxt_packet *pkt = (struct dmxt_packet*) data;
uint16_t offset = htons(pkt->x);
uint16_t length = htons(pkt->length);
LOG_INFO("dmx packet len:%u offset:%u", length, offset);
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
// PLACEHOLDER
//led_vu_data(data + sizeof(struct dmxt_packet), offset, length);
xSemaphoreGive(displayer.mutex);
}
/****************************************************************************************
* Scroll task
* - with the addition of the visualizer, it's a bit a 2-headed beast not easy to
@@ -1137,15 +1248,15 @@ static void displayer_task(void *args) {
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
// suspend ourselves if nothing to do, grfg or visu will wake us up
if (!scroller.active && !visu.mode) {
if (!scroller.active && !visu.mode && !led_visu.mode) {
xSemaphoreGive(displayer.mutex);
vTaskSuspend(NULL);
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
scroller.wake = visu.wake = 0;
scroller.wake = displayer.wake = 0;
}
// go for long sleep when either item is disabled
if (!visu.mode) visu.wake = LONG_WAKE;
if (!visu.mode && !led_visu.mode) displayer.wake = LONG_WAKE;
if (!scroller.active) scroller.wake = LONG_WAKE;
// scroll required amount of columns (within the window)
@@ -1181,20 +1292,20 @@ static void displayer_task(void *args) {
}
// update visu if active
if (visu.mode && visu.wake <= 0) {
visu_update();
visu.wake = 100;
if ((visu.mode || led_visu.mode) && displayer.wake <= 0) {
displayer_update();
displayer.wake = 100;
}
// need to make sure we own display
if (displayer.owned) GDS_Update(display);
if (display && displayer.owned) GDS_Update(display);
// release semaphore and sleep what's needed
xSemaphoreGive(displayer.mutex);
sleep = min(visu.wake, scroller.wake);
sleep = min(displayer.wake, scroller.wake);
vTaskDelay(sleep / portTICK_PERIOD_MS);
scroller.wake -= sleep;
visu.wake -= sleep;
displayer.wake -= sleep;
}
}

View File

@@ -15,6 +15,7 @@
#include "esp_timer.h"
#include "esp_wifi.h"
#include "monitor.h"
#include "platform_config.h"
mutex_type slimp_mutex;
@@ -44,14 +45,14 @@ uint32_t _gettime_ms_(void) {
}
extern void sb_controls_init(void);
extern bool sb_display_init(void);
extern bool sb_displayer_init(void);
u8_t custom_player_id = 12;
void embedded_init(void) {
mutex_create(slimp_mutex);
sb_controls_init();
if (sb_display_init()) custom_player_id = 100;
custom_player_id = sb_displayer_init() ? 100 : 101;
}
u16_t get_RSSI(void) {
@@ -66,6 +67,30 @@ u16_t get_plugged(void) {
return jack_inserted_svc() ? PLUG_HEADPHONE : 0;
}
u8_t get_battery(void) {
return (battery_level_svc() * 16) / 100;
u16_t get_battery(void) {
return (u16_t) (battery_value_svc() * 128) & 0x0fff;
}
void set_name(char *name) {
char *cmd = config_alloc_get(NVS_TYPE_STR, "autoexec1");
char *p, *q;
if (!cmd) return;
if ((p = strstr(cmd, " -n")) != NULL) {
q = p + 3;
// in case some smart dude has a " -" in player's name
while ((q = strstr(q, " -")) != NULL) {
if (!strchr(q, '"') || !strchr(q+1, '"')) break;
q++;
}
if (q) memmove(p, q, strlen(q) + 1);
else *p = '\0';
}
asprintf(&q, "%s -n \"%s\"", cmd, name);
config_set_value(NVS_TYPE_STR, "autoexec1", q);
free(q);
free(cmd);
}

View File

@@ -30,19 +30,24 @@ typedef unsigned long long u64_t;
#define _CONST
#endif
#define STREAM_THREAD_STACK_SIZE 6 * 1024
#define DECODE_THREAD_STACK_SIZE 16 * 1024
#define OUTPUT_THREAD_STACK_SIZE 6 * 1024
#define IR_THREAD_STACK_SIZE 6 * 1024
#define STREAM_THREAD_STACK_SIZE 4 * 1024
#define DECODE_THREAD_STACK_SIZE 14 * 1024
#define OUTPUT_THREAD_STACK_SIZE 4 * 1024
#define IR_THREAD_STACK_SIZE 4 * 1024
// number of 5s times search for a server will happen beforee slimproto exits (0 = no limit)
// number of times the 5s search for a server will happen before slimproto exits (0 = no limit)
#define MAX_SERVER_RETRIES 5
// or can be as simple as #define PLAYER_ID 100
#define PLAYER_ID custom_player_id
extern u8_t custom_player_id;
#define BASE_CAP "Model=squeezeesp32,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION
#if BYTES_PER_FRAME == 8
#define BASE_CAP "Model=squeezeesp32,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Balance=1,Depth=32,Firmware=" VERSION
#else
#define BASE_CAP "Model=squeezeesp32,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Balance=1,Depth=16,Firmware=" VERSION
#endif
// to force some special buffer attribute
#define EXT_BSS __attribute__((section(".ext_ram.bss")))
@@ -72,16 +77,19 @@ extern mutex_type slimp_mutex;
#define PLUG_HEADPHONE 0x04
u16_t get_RSSI(void); // must provide or define as 0xffff
u16_t get_plugged(void); // must provide or define as 0x0
u8_t get_battery(void); // must provide 0..15 or define as 0x0
u16_t get_battery(void); // must provide 12 bits data or define as 0x0 (exact meaning is device-dependant)
// set name
void set_name(char *name); // can be defined as an empty macro
// to be defined to nothing if you don't want to support these
extern struct visu_export_s {
pthread_mutex_t mutex;
u32_t level, size, rate, gain;
s16_t *buffer;
void *buffer;
bool running;
} visu_export;
void output_visu_export(s16_t *frames, frames_t out_frames, u32_t rate, bool silence, u32_t gain);
void output_visu_export(void *frames, frames_t out_frames, u32_t rate, bool silence, u32_t gain);
void output_visu_init(log_level level);
void output_visu_close(void);

View File

@@ -8,11 +8,12 @@
*
*/
#include "platform_config.h"
#include "squeezelite.h"
#include "equalizer.h"
#include "esp_equalizer.h"
#define EQ_BANDS 10
#define EQ_BANDS 10
static log_level loglevel = lINFO;
@@ -22,6 +23,25 @@ static struct {
bool update;
} equalizer = { .update = true };
/****************************************************************************************
* initialize equalizer
*/
void equalizer_init(void) {
s8_t gain[EQ_BANDS] = { };
char *config = config_alloc_get(NVS_TYPE_STR, "equalizer");
char *p = strtok(config, ", !");
for (int i = 0; p && i < EQ_BANDS; i++) {
gain[i] = atoi(p);
p = strtok(NULL, ", :");
}
free(config);
equalizer_update(gain);
LOG_INFO("initializing equalizer");
}
/****************************************************************************************
* open equalizer
*/
@@ -68,7 +88,18 @@ void equalizer_close(void) {
* update equalizer gain
*/
void equalizer_update(s8_t *gain) {
for (int i = 0; i < EQ_BANDS; i++) equalizer.gain[i] = gain[i];
char config[EQ_BANDS * 4 + 1] = { };
char *p = config;
for (int i = 0; i < EQ_BANDS; i++) {
equalizer.gain[i] = gain[i];
if (gain[i] < 0) *p++ = '-';
*p++ = (gain[i] / 10) + 0x30;
*p++ = (gain[i] % 10) + 0x30;
if (i < EQ_BANDS - 1) *p++ = ',';
}
config_set_value(NVS_TYPE_STR, "equalizer", config);
equalizer.update = true;
}
@@ -76,7 +107,7 @@ void equalizer_update(s8_t *gain) {
* process equalizer
*/
void equalizer_process(u8_t *buf, u32_t bytes, u32_t sample_rate) {
// don't want to process with output locked, so tak ethe small risk to miss one parametric update
// don't want to process with output locked, so take the small risk to miss one parametric update
if (equalizer.update) {
equalizer_close();
equalizer_open(sample_rate);

View File

@@ -10,6 +10,7 @@
#pragma once
void equalizer_init(void);
void equalizer_open(u32_t sample_rate);
void equalizer_close(void);
void equalizer_update(s8_t *gain);

View File

@@ -20,7 +20,6 @@
static const char TAG[] = "DAC external";
static void deinit(void) { }
static void speaker(bool active) { }
static void headset(bool active) { }
static bool volume(unsigned left, unsigned right) { return false; }
@@ -28,48 +27,57 @@ static void power(adac_power_e mode);
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static bool i2c_json_execute(char *set);
static esp_err_t i2c_write_reg(uint8_t reg, uint8_t val);
static uint8_t i2c_read_reg(uint8_t reg);
const struct adac_s dac_external = { "i2s", init, deinit, power, speaker, headset, volume };
static int i2c_port, i2c_addr;
const struct adac_s dac_external = { "i2s", init, adac_deinit, power, speaker, headset, volume };
static cJSON *i2c_json;
static int i2c_addr;
static struct {
char *model;
bool mclk;
char *controlset;
} codecs[] = {
{ "es8388", true,
"{\"init\":[ \
{\"reg\":8,\"val\":0}, {\"reg\":2,\"val\":243}, {\"reg\":43,\"val\":128}, {\"reg\":0,\"val\":5}, \
{\"reg\":1,\"val\":64}, {\"reg\":4,\"val\":60}, {\"reg\":23,\"val\":24}, {\"reg\":24,\"val\":2}, \
{\"reg\":26,\"val\":0}, {\"reg\":27,\"val\":0}, {\"reg\":25,\"val\":50}, {\"reg\":38,\"val\":0}, \
{\"reg\":39,\"val\":184}, {\"reg\":42,\"val\":184}, {\"reg\":46,\"val\":30}, {\"reg\":47,\"val\":30}, \
{\"reg\":48,\"val\":30}, {\"reg\":49,\"val\":30}, {\"reg\":2,\"val\":170}]}" },
{ NULL, false, NULL }
};
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
char *p;
i2c_port = i2c_port_num;
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = -1,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = -1,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 250000,
};
i2c_addr = adac_init(config, i2c_port_num);
if (!i2c_addr) return true;
if ((p = strcasestr(config, "i2c")) != NULL) i2c_addr = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
p = config_alloc_get_str("dac_controlset", CONFIG_DAC_CONTROLSET, NULL);
i2c_json = cJSON_Parse(p);
if (!i2c_addr || !i2c_json || i2c_config.sda_io_num == -1 || i2c_config.scl_io_num == -1) {
if (p) free(p);
ESP_LOGW(TAG, "No i2c controlset found");
return true;
if ((!p || !*p) && (p = strcasestr(config, "model")) != NULL) {
char model[32] = "";
int i;
sscanf(p, "%*[^=]=%31[^,]", model);
for (i = 0; *model && ((p = codecs[i].controlset) != NULL) && strcasecmp(codecs[i].model, model); i++);
if (p && codecs[i].mclk) {
ESP_LOGI(TAG, "Configuring MCLK on GPIO0");
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_WRITE(PIN_CTRL, 0xFFFFFFF0);
}
}
ESP_LOGI(TAG, "DAC uses I2C @%d with sda:%d, scl:%d", i2c_addr, i2c_config.sda_io_num, i2c_config.scl_io_num);
i2c_json = cJSON_Parse(p);
// we have an I2C configured
i2c_param_config(i2c_port, &i2c_config);
i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
if (!i2c_json) {
ESP_LOGW(TAG, "no i2c controlset found");
return true;
}
if (!i2c_json_execute("init")) {
ESP_LOGE(TAG, "could not intialize DAC");
@@ -105,70 +113,17 @@ bool i2c_json_execute(char *set) {
if (!reg || !val) continue;
if (!mode) {
i2c_write_reg(reg->valueint, val->valueint);
adac_write_byte(i2c_addr, reg->valueint, val->valueint);
} else if (!strcasecmp(mode->valuestring, "or")) {
uint8_t data = i2c_read_reg(reg->valueint);
uint8_t data = adac_read_byte(i2c_addr,reg->valueint);
data |= (uint8_t) val->valueint;
i2c_write_reg(reg->valueint, data);
adac_write_byte(i2c_addr, reg->valueint, data);
} else if (!strcasecmp(mode->valuestring, "and")) {
uint8_t data = i2c_read_reg(reg->valueint);
uint8_t data = adac_read_byte(i2c_addr, reg->valueint);
data &= (uint8_t) val->valueint;
i2c_write_reg(reg->valueint, data);
adac_write_byte(i2c_addr, reg->valueint, data);
}
}
return true;
}
/****************************************************************************************
*
*/
static esp_err_t i2c_write_reg(uint8_t reg, uint8_t val) {
esp_err_t ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, val, I2C_MASTER_NACK);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C write failed");
}
return ret;
}
/****************************************************************************************
*
*/
static uint8_t i2c_read_reg(uint8_t reg) {
esp_err_t ret;
uint8_t data = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK);
i2c_master_read_byte(cmd, &data, I2C_MASTER_NACK);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C read failed");
}
return data;
}

View File

@@ -30,7 +30,7 @@
#if BYTES_PER_FRAME == 4
#define ALIGN(n) (n)
#else
#define ALIGN(n) (n << 8)
#define ALIGN(n) (n << 16)
#endif
#define WRAPBUF_LEN 2048
@@ -151,16 +151,33 @@ static int read_mp4_header(unsigned long *samplerate_p, unsigned char *channels_
LOG_WARN("error parsing esds");
return -1;
}
mp4_desc_length(&ptr);
info.profile = *ptr >> 3;
int desc_len = mp4_desc_length(&ptr);
int AOT = *ptr >> 3;
info.profile = AAC_PROFILE_LC;
info.sampRateCore = (*ptr++ & 0x07) << 1;
info.sampRateCore |= (*ptr >> 7) & 0x01;
info.sampRateCore = rates[info.sampRateCore];
info.nChans = (*ptr & 0x7f) >> 3;
*channels_p = info.nChans;
// Note that 24 bits frequencies are not handled
#if AAC_ENABLE_SBR
if (AOT == 5 || AOT == 29) {
*samplerate_p = rates[((ptr[0] & 0x03) << 1) | (ptr[1] >> 7)];
LOG_WARN("AAC stream with SBR => high CPU required (use LMS proxied mode)");
} else if (desc_len > 2 && ((ptr[1] << 3) | (ptr[2] >> 5)) == 0x2b7 && (ptr[2] & 0x1f) == 0x05 && (ptr[3] & 0x80)) {
*samplerate_p = rates[(ptr[3] & 0x78) >> 3];
LOG_WARN("AAC stream with extended SBR => high CPU required (use LMS proxied mode)");
} else if (AOT == 2) {
*samplerate_p = info.sampRateCore;
} else {
*samplerate_p = 44100;
LOG_ERROR("AAC audio object type %d not handled", AOT);
}
#else
*samplerate_p = info.sampRateCore;
#endif
HAAC(a, SetRawBlockParams, a->hAac, 0, &info);
LOG_DEBUG("playable aac track: %u (p:%x, r:%d, c:%d)", trak, info.profile, info.sampRateCore, info.nChans);
LOG_DEBUG("playable aac track: %u (p:%x, r:%d, c:%d, desc_len:%d)", trak, AOT, info.sampRateCore, info.nChans, desc_len);
play = trak;
}
@@ -332,7 +349,7 @@ static decode_state helixaac_decode(void) {
size_t bytes_total, bytes_wrap;
int res, bytes;
static AACFrameInfo info;
ISAMPLE_T *iptr;
s16_t *iptr;
u8_t *sptr;
bool endstream;
frames_t frames;
@@ -372,7 +389,7 @@ static decode_state helixaac_decode(void) {
u8_t *p = streambuf->readp + n;
int bytes = bytes_wrap - n;
if (!HAAC(a, Decode, a->hAac, &p, &bytes, (short*) a->write_buf)) {
if (!HAAC(a, Decode, a->hAac, &p, &bytes, (s16_t*) a->write_buf)) {
HAAC(a, GetLastFrameInfo, a->hAac, &info);
channels = info.nChans;
samplerate = info.sampRateOut;
@@ -425,17 +442,16 @@ static decode_state helixaac_decode(void) {
// not finished header parsing come back next time
UNLOCK_S;
LOG_INFO("header not found yet");
LOG_DEBUG("header not found yet");
return DECODE_RUNNING;
}
}
// we always have at least WRAPBUF_LEN unless it's the end of a stream
if (bytes_wrap < WRAPBUF_LEN) {
if (bytes_wrap < WRAPBUF_LEN && bytes_wrap != bytes_total) {
// build a linear buffer if we are crossing the end of streambuf
memcpy(a->wrap_buf, streambuf->readp, bytes_wrap);
memcpy(a->wrap_buf + bytes_wrap, streambuf->buf, min(WRAPBUF_LEN, bytes_total) - bytes_wrap);
sptr = a->wrap_buf;
bytes = bytes_wrap = min(WRAPBUF_LEN, bytes_total);
} else {
@@ -444,13 +460,13 @@ static decode_state helixaac_decode(void) {
}
// decode function changes iptr, so can't use streambuf->readp (same for bytes)
res = HAAC(a, Decode, a->hAac, &sptr, &bytes, (short*) a->write_buf);
res = HAAC(a, Decode, a->hAac, &sptr, &bytes, (s16_t*) a->write_buf);
if (res < 0) {
LOG_WARN("AAC decode error %d", res);
}
HAAC(a, GetLastFrameInfo, a->hAac, &info);
iptr = (ISAMPLE_T *) a->write_buf;
iptr = (s16_t*) a->write_buf;
bytes = bytes_wrap - bytes;
endstream = false;
@@ -544,8 +560,8 @@ static decode_state helixaac_decode(void) {
iptr += count * 2;
#else
while (count--) {
*optr++ = *iptr++ << 8;
*optr++ = *iptr++ << 8;
*optr++ = ALIGN(*iptr++);
*optr++ = ALIGN(*iptr++);
}
#endif
} else if (info.nChans == 1) {
@@ -598,7 +614,7 @@ static void helixaac_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
// always free decoder as flush only works when no parameter has changed
HAAC(a, FreeDecoder, a->hAac);
} else {
a->write_buf = malloc(FRAME_BUF * BYTES_PER_FRAME);
a->write_buf = malloc(FRAME_BUF * 4);
a->wrap_buf = malloc(WRAPBUF_LEN);
}

View File

@@ -343,16 +343,9 @@ int main(int argc, char **argv) {
while (optind < argc && strlen(argv[optind]) >= 2 && argv[optind][0] == '-') {
char *opt = argv[optind] + 1;
if (strstr("oabcCdefmMnNpPrs"
if (strstr("oabcCdefmMnNpPrsZ"
#if ALSA
"UVO"
#endif
/*
* only allow '-Z <rate>' override of maxSampleRate
* reported by client if built with the capability to resample!
*/
#if RESAMPLE || RESAMPLE16
"Z"
#endif
, opt) && optind < argc - 1) {
optarg = argv[optind + 1];
@@ -519,6 +512,9 @@ int main(int argc, char **argv) {
case 'N':
namefile = optarg;
break;
case 'Z':
maxSampleRate = atoi(optarg);
break;
case 'W':
pcm_check_header = true;
break;
@@ -552,9 +548,6 @@ int main(int argc, char **argv) {
resample = "";
}
break;
case 'Z':
maxSampleRate = atoi(optarg);
break;
#endif
#if DSD
case 'D':

View File

@@ -188,7 +188,12 @@ static decode_state opus_decompress(void) {
// work backward to unpack samples (if needed)
iptr = (s16_t *) write_buf + count;
optr = (ISAMPLE_T *) write_buf + frames * 2;
IF_DIRECT(
optr = (ISAMPLE_T *) outputbuf->writep + frames * 2;
)
IF_PROCESS(
optr = (ISAMPLE_T *) write_buf + frames * 2;
)
if (channels == 2) {
#if BYTES_PER_FRAME == 4
@@ -224,7 +229,6 @@ static decode_state opus_decompress(void) {
if (stream.state <= DISCONNECT) {
LOG_INFO("partial decode");
UNLOCK_O_direct;
UNLOCK_S;
return DECODE_COMPLETE;
} else {
LOG_INFO("no frame decoded");

View File

@@ -47,6 +47,7 @@ frames_t _output_frames(frames_t avail) {
frames_t frames, size;
bool silence;
u8_t flags = output.channels;
s32_t cross_gain_in = 0, cross_gain_out = 0; ISAMPLE_T *cross_ptr = NULL;
@@ -59,7 +60,7 @@ frames_t _output_frames(frames_t avail) {
silence = false;
// start when threshold met
if (output.state == OUTPUT_BUFFER && frames > output.threshold * output.next_sample_rate / 10 && frames > output.start_frames) {
if (output.state == OUTPUT_BUFFER && (frames * BYTES_PER_FRAME) > output.threshold * output.next_sample_rate / 10 && frames > output.start_frames) {
output.state = OUTPUT_RUNNING;
LOG_INFO("start buffer frames: %u", frames);
wake_controller();
@@ -254,7 +255,13 @@ frames_t _output_frames(frames_t avail) {
out_frames = !silence ? min(size, cont_frames) : size;
wrote = output.write_cb(out_frames, silence, gainL, gainR, cross_gain_in, cross_gain_out, &cross_ptr);
IF_DSD(
if (output.outfmt != PCM) {
flags = 0;
}
)
wrote = output.write_cb(out_frames, silence, gainL, gainR, flags, cross_gain_in, cross_gain_out, &cross_ptr);
if (wrote <= 0) {
frames -= size;

View File

@@ -40,7 +40,7 @@ static uint8_t *btout;
static frames_t oframes;
static bool stats;
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags,
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
#define DECLARE_ALL_MIN_MAX \
@@ -79,7 +79,7 @@ void output_close_bt(void) {
equalizer_close();
}
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags,
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr) {
assert(btout != NULL);
@@ -90,9 +90,7 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
}
if (gainL != FIXED_ONE || gainR!= FIXED_ONE) {
_apply_gain(outputbuf, out_frames, gainL, gainR);
}
_apply_gain(outputbuf, out_frames, gainL, gainR, flags);
#if BYTES_PER_FRAME == 4
memcpy(btout + oframes * BYTES_PER_FRAME, outputbuf->readp, out_frames * BYTES_PER_FRAME);
@@ -114,15 +112,17 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
memcpy(btout + oframes * BYTES_PER_FRAME, buf, out_frames * BYTES_PER_FRAME);
}
output_visu_export((s16_t*) (btout + oframes * BYTES_PER_FRAME), out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
oframes += out_frames;
return (int)out_frames;
}
int32_t output_bt_data(uint8_t *data, int32_t len) {
int32_t avail_data = 0, wanted_len = 0, start_timer = 0;
int32_t iframes = len / BYTES_PER_FRAME, start_timer = 0;
if (len < 0 || data == NULL || !running) {
if (iframes <= 0 || data == NULL || !running) {
return 0;
}
@@ -131,32 +131,25 @@ int32_t output_bt_data(uint8_t *data, int32_t len) {
// This is how the BTC layer calculates the number of bytes to
// for us to send. (BTC_SBC_DEC_PCM_DATA_LEN * sizeof(OI_INT16) - availPcmBytes
wanted_len=len;
SET_MIN_MAX(len,req);
TIME_MEASUREMENT_START(start_timer);
LOCK;
output.device_frames = 0; // todo: check if this is the right way do to this.
SET_MIN_MAX_SIZED(_buf_used(outputbuf),bt,outputbuf->size);
output.device_frames = 0;
output.updated = gettime_ms();
output.frames_played_dmp = output.frames_played;
SET_MIN_MAX_SIZED(_buf_used(outputbuf),bt,outputbuf->size);
do {
avail_data = _output_frames( wanted_len/BYTES_PER_FRAME )*BYTES_PER_FRAME; // Keep the transfer buffer full
wanted_len-=avail_data;
} while (wanted_len > 0 && avail_data != 0);
if (wanted_len > 0) {
SET_MIN_MAX(wanted_len, under);
}
output.frames_in_process = len-wanted_len;
equalizer_process(data, (len - wanted_len) * BYTES_PER_FRAME, output.current_sample_rate);
_output_frames(iframes);
output.frames_in_process = oframes;
UNLOCK;
equalizer_process(data, oframes * BYTES_PER_FRAME, output.current_sample_rate);
SET_MIN_MAX(TIME_MEASUREMENT_GET(start_timer),lock_out_time);
SET_MIN_MAX((len-wanted_len), rec);
SET_MIN_MAX((len-oframes*BYTES_PER_FRAME), rec);
TIME_MEASUREMENT_START(start_timer);
return len-wanted_len;
return oframes * BYTES_PER_FRAME;
}
void output_bt_tick(void) {

View File

@@ -73,12 +73,16 @@ void output_init_embedded(log_level level, char *device, unsigned output_buf_siz
slimp_handler_chain = slimp_handler;
slimp_handler = handler;
// init equalizer before backends
equalizer_init();
memset(&output, 0, sizeof(output));
output_init_common(level, device, output_buf_size, rates, idle);
output.start_frames = FRAME_BLOCK;
output.rate_delay = rate_delay;
if (strcasestr(device, "BT ")) {
if (strcasestr(device, "BT ") || !strcasecmp(device, "BT")) {
LOG_INFO("init Bluetooth");
close_cb = &output_close_bt;
output_init_bt(level, device, output_buf_size, params, rates, rate_delay, idle);
@@ -114,12 +118,18 @@ void set_volume(unsigned left, unsigned right) {
bool test_open(const char *device, unsigned rates[], bool userdef_rates) {
memset(rates, 0, MAX_SUPPORTED_SAMPLERATES * sizeof(unsigned));
if (!strcasecmp(device, "I2S")) {
unsigned _rates[] = { 192000, 176400, 96000, 88200, 48000,
unsigned _rates[] = {
#if BYTES_PER_FRAME == 4
192000, 176400,
#endif
96000, 88200, 48000,
44100, 32000, 24000, 22050, 16000,
12000, 11025, 8000, 0 };
memcpy(rates, _rates, sizeof(_rates));
} else if (!strcasecmp(device, "SPDIF")) {
unsigned _rates[] = { 48000, 44100, 0 };
unsigned _rates[] = { 96000, 88200, 48000,
44100, 32000, 24000, 22050, 16000,
12000, 11025, 8000, 0 };
memcpy(rates, _rates, sizeof(_rates));
} else {
rates[0] = 44100;

View File

@@ -51,6 +51,7 @@ sure that using rate_delay would fix that
#define UNLOCK mutex_unlock(outputbuf->mutex)
#define FRAME_BLOCK MAX_SILENCE_FRAMES
#define SPDIF_BLOCK 256
// must have an integer ratio with FRAME_BLOCK (see spdif comment)
#define DMA_BUF_LEN 512
@@ -78,21 +79,24 @@ extern struct buffer *streambuf;
extern struct buffer *outputbuf;
extern u8_t *silencebuf;
const struct adac_s *dac_set[] = { &dac_tas57xx, &dac_tas5713, &dac_ac101, NULL };
const struct adac_s *dac_set[] = { &dac_tas57xx, &dac_tas5713, &dac_ac101, &dac_wm8978, NULL };
const struct adac_s *adac = &dac_external;
static log_level loglevel;
static bool (*slimp_handler_chain)(u8_t *data, int len);
static bool jack_mutes_amp;
static bool running, isI2SStarted;
static bool running, isI2SStarted, ended;
static i2s_config_t i2s_config;
static u8_t *obuf;
static frames_t oframes;
static bool spdif;
static struct {
bool enabled;
u8_t *buf;
size_t count;
} spdif;
static size_t dma_buf_frames;
static pthread_t thread;
static TaskHandle_t stats_task;
static TaskHandle_t stats_task, output_i2s_task;
static bool stats;
static struct {
int gpio, active;
@@ -101,9 +105,9 @@ static struct {
DECLARE_ALL_MIN_MAX;
static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags,
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
static void *output_thread_i2s(void *arg);
static void output_thread_i2s(void *arg);
static void output_thread_i2s_stats(void *arg);
static void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count);
static void (*jack_handler_chain)(bool inserted);
@@ -165,7 +169,7 @@ static void jack_handler(bool inserted) {
static void set_amp_gpio(int gpio, char *value) {
char *p;
if (!strcasecmp(value, "amp")) {
if (strcasestr(value, "amp")) {
amp_control.gpio = gpio;
if ((p = strchr(value, ':')) != NULL) amp_control.active = atoi(p + 1);
@@ -234,10 +238,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
set_i2s_pin(spdif_config, &i2s_spdif_pin);
set_i2s_pin(dac_config, &i2s_dac_pin);
/* BEWARE: i2s. must be patched to set tx_msb_right/rx_msb_right to 1
* or SPDIF will not work. These settings are not accessible from
* userland and I don't know why
*/
/* BEWARE: i2s.c must be patched otherwise L/R are swapped in 32 bits mode */
// common I2S initialization
i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
@@ -249,7 +250,10 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //Interrupt level 1
if (strcasestr(device, "spdif")) {
spdif = true;
spdif.enabled = true;
if ((spdif.buf = heap_caps_malloc(SPDIF_BLOCK * 16, MALLOC_CAP_INTERNAL)) == NULL) {
LOG_ERROR("Cannot allocate SPDIF buffer");
}
if (i2s_spdif_pin.bck_io_num == -1 || i2s_spdif_pin.ws_io_num == -1 || i2s_spdif_pin.data_out_num == -1) {
LOG_WARN("Cannot initialize I2S for SPDIF bck:%d ws:%d do:%d", i2s_spdif_pin.bck_io_num,
@@ -327,7 +331,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
}
LOG_INFO("Initializing I2S mode %s with rate: %d, bits per sample: %d, buffer frames: %d, number of buffers: %d ",
spdif ? "S/PDIF" : "normal",
spdif.enabled ? "S/PDIF" : "normal",
i2s_config.sample_rate, i2s_config.bits_per_sample, i2s_config.dma_buf_len, i2s_config.dma_buf_count);
i2s_stop(CONFIG_I2S_NUM);
@@ -346,14 +350,13 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
parse_set_GPIO(set_amp_gpio);
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
cfg.thread_name= "output_i2s";
cfg.inherit_cfg = false;
cfg.prio = CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1;
cfg.stack_size = PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE;
esp_pthread_set_cfg(&cfg);
pthread_create(&thread, NULL, output_thread_i2s, NULL);
// create task as a FreeRTOS task but uses stack in internal RAM
{
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
static DRAM_ATTR StackType_t xStack[OUTPUT_THREAD_STACK_SIZE] __attribute__ ((aligned (4)));
output_i2s_task = xTaskCreateStaticPinnedToCore( (TaskFunction_t) output_thread_i2s, "output_i2s", OUTPUT_THREAD_STACK_SIZE,
NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, xStack, &xTaskBuffer, 0 );
}
// do we want stats
p = config_alloc_get_default(NVS_TYPE_STR, "stats", "n", 0);
@@ -364,7 +367,8 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
if (stats) {
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
static EXT_RAM_ATTR StackType_t xStack[STAT_STACK_SIZE] __attribute__ ((aligned (4)));
stats_task = xTaskCreateStatic( (TaskFunction_t) output_thread_i2s_stats, "output_i2s_sts", STAT_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
stats_task = xTaskCreateStatic( (TaskFunction_t) output_thread_i2s_stats, "output_i2s_sts", STAT_STACK_SIZE,
NULL, ESP_TASK_PRIO_MIN, xStack, &xTaskBuffer);
}
}
@@ -376,7 +380,8 @@ void output_close_i2s(void) {
LOCK;
running = false;
UNLOCK;
pthread_join(thread, NULL);
while (!ended) vTaskDelay(20 / portTICK_PERIOD_MS);
if (stats) vTaskDelete(stats_task);
i2s_driver_uninstall(CONFIG_I2S_NUM);
@@ -398,46 +403,23 @@ bool output_volume_i2s(unsigned left, unsigned right) {
/****************************************************************************************
* Write frames to the output buffer
*/
static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags,
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr) {
#if BYTES_PER_FRAME == 8
s32_t *optr;
#endif
if (!silence) {
if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) {
_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
}
#if BYTES_PER_FRAME == 4
if (gainL != FIXED_ONE || gainR!= FIXED_ONE) {
_apply_gain(outputbuf, out_frames, gainL, gainR);
}
_apply_gain(outputbuf, out_frames, gainL, gainR, flags);
memcpy(obuf + oframes * BYTES_PER_FRAME, outputbuf->readp, out_frames * BYTES_PER_FRAME);
#else
optr = (s32_t*) outputbuf->readp;
#endif
} else {
#if BYTES_PER_FRAME == 4
memcpy(obuf + oframes * BYTES_PER_FRAME, silencebuf, out_frames * BYTES_PER_FRAME);
#else
optr = (s32_t*) silencebuf;
#endif
}
#if BYTES_PER_FRAME == 8
IF_DSD(
if (output.outfmt == DOP) {
update_dop((u32_t *) optr, out_frames, output.invert);
} else if (output.outfmt != PCM && output.invert)
dsd_invert((u32_t *) optr, out_frames);
)
_scale_and_pack_frames(obuf + oframes * BYTES_PER_FRAME, optr, out_frames, gainL, gainR, output.format);
#endif
output_visu_export((s16_t*) (obuf + oframes * BYTES_PER_FRAME), out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
// don't update visu if we don't have enough data in buffer
if (silence || output.external || _buf_used(outputbuf) > outputbuf->size >> 2 ) {
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
}
oframes += out_frames;
@@ -447,20 +429,14 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
/****************************************************************************************
* Main output thread
*/
static void *output_thread_i2s(void *arg) {
size_t count = 0, bytes;
static void output_thread_i2s(void *arg) {
size_t bytes;
frames_t iframes = FRAME_BLOCK;
uint32_t timer_start = 0;
int discard = 0;
uint32_t fullness = gettime_ms();
bool synced;
output_state state = OUTPUT_OFF - 1;
char *sbuf = NULL;
// spdif needs 16 bytes per frame : 32 bits/sample, 2 channels, BMC encoded
if (spdif && (sbuf = malloc(FRAME_BLOCK * 16)) == NULL) {
LOG_ERROR("Cannot allocate SPDIF buffer");
}
while (running) {
@@ -491,7 +467,7 @@ static void *output_thread_i2s(void *arg) {
isI2SStarted = false;
i2s_stop(CONFIG_I2S_NUM);
adac->power(ADAC_STANDBY);
count = 0;
spdif.count = 0;
}
usleep(100000);
continue;
@@ -551,22 +527,32 @@ static void *output_thread_i2s(void *arg) {
*/
}
i2s_config.sample_rate = output.current_sample_rate;
i2s_set_sample_rates(CONFIG_I2S_NUM, spdif ? i2s_config.sample_rate * 2 : i2s_config.sample_rate);
i2s_set_sample_rates(CONFIG_I2S_NUM, spdif.enabled ? i2s_config.sample_rate * 2 : i2s_config.sample_rate);
i2s_zero_dma_buffer(CONFIG_I2S_NUM);
#if BYTES_PER_FRAME == 4
equalizer_close();
equalizer_open(output.current_sample_rate);
//return;
#endif
}
#if BYTES_PER_FRAME == 4
// run equalizer
equalizer_process(obuf, oframes * BYTES_PER_FRAME, output.current_sample_rate);
#endif
// we assume that here we have been able to entirely fill the DMA buffers
if (spdif) {
spdif_convert((ISAMPLE_T*) obuf, oframes, (u32_t*) sbuf, &count);
i2s_write(CONFIG_I2S_NUM, sbuf, oframes * 16, &bytes, portMAX_DELAY);
bytes /= 4;
if (spdif.enabled) {
size_t obytes, count = 0;
bytes = 0;
// need IRAM for speed but can't allocate a FRAME_BLOCK * 16, so process by smaller chunks
while (count < oframes) {
size_t chunk = min(SPDIF_BLOCK, oframes - count);
spdif_convert((ISAMPLE_T*) obuf + count * 2, chunk, (u32_t*) spdif.buf, &spdif.count);
i2s_write(CONFIG_I2S_NUM, spdif.buf, chunk * 16, &obytes, portMAX_DELAY);
bytes += obytes / (16 / BYTES_PER_FRAME);
count += chunk;
}
#if BYTES_PER_FRAME == 4
} else if (i2s_config.bits_per_sample == 32) {
i2s_write_expand(CONFIG_I2S_NUM, obuf, oframes * BYTES_PER_FRAME, 16, 32, &bytes, portMAX_DELAY);
@@ -585,9 +571,10 @@ static void *output_thread_i2s(void *arg) {
}
if (spdif) free(sbuf);
if (spdif.enabled) free(spdif.buf);
ended = true;
return 0;
vTaskDelete(NULL);
}
/****************************************************************************************
@@ -633,54 +620,7 @@ static void output_thread_i2s_stats(void *arg) {
#define VUCP ((0xCC) << 24)
#define VUCP_MUTE ((0xD4) << 24) // To mute PCM, set VUCP = invalid.
extern const u16_t spdif_bmclookup[256];
/*
SPDIF is supposed to be (before BMC encoding, from LSB to MSB)
PPPP AAAA SSSS SSSS SSSS SSSS SSSS VUCP
after BMC encoding, each bits becomes 2 hence this becomes a 64 bits word. The
the trick is to start not with a PPPP sequence but with an VUCP sequence to that
the 16 bits samples are aligned with a BMC word boundary. Note that the LSB of the
audio is transmitted first (not the MSB) and that ESP32 libray sends R then L,
contrary to what seems to be usually done, so (dst) order had to be changed
*/
void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
u16_t hi, lo, aux;
// frames are 2 channels of 16 bits
frames *= 2;
while (frames--) {
#if BYTES_PER_FRAME == 4
hi = spdif_bmclookup[(u8_t)(*src >> 8)];
lo = spdif_bmclookup[(u8_t) *src];
#else
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
lo = spdif_bmclookup[(u8_t) *src >> 16];
#endif
lo ^= ~((s16_t)hi) >> 16;
// 16 bits sample:
*(dst+0) = ((u32_t)lo << 16) | hi;
// 4 bits auxillary-audio-databits, the first used as parity
aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);
// VUCP-Bits: Valid, Subcode, Channelstatus, Parity = 0
// As parity is always 0, we can use fixed preambles
if (++(*count) > 383) {
*(dst+1) = VUCP | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
*count = 0;
} else {
*(dst+1) = VUCP | ((((*count) & 0x01) ? PREAMBLE_W : PREAMBLE_M) << 16) | aux;
}
src++;
dst += 2;
}
}
const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least significant bit first)
static const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least significant bit first)
0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
@@ -715,7 +655,74 @@ const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least signif
0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
};
/*
SPDIF is supposed to be (before BMC encoding, from LSB to MSB)
PPPP AAAA SSSS SSSS SSSS SSSS SSSS VUCP
after BMC encoding, each bits becomes 2 hence this becomes a 64 bits word. The
the trick is to start not with a PPPP sequence but with an VUCP sequence to that
the 16 bits samples are aligned with a BMC word boundary. Note that the LSB of the
audio is transmitted first (not the MSB) and that ESP32 libray sends R then L,
contrary to what seems to be usually done, so (dst) order had to be changed
*/
void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
register u16_t hi, lo, aux;
size_t cnt = *count;
while (frames--) {
// start with left channel
#if BYTES_PER_FRAME == 4
hi = spdif_bmclookup[(u8_t)(*src >> 8)];
lo = spdif_bmclookup[(u8_t) *src++];
// invert if last preceeding bit is 1
lo ^= ~((s16_t)hi) >> 16;
// first 16 bits
*dst++ = ((u32_t)lo << 16) | hi;
aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);
#else
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
lo = spdif_bmclookup[(u8_t)(*src >> 16)];
// invert if last preceeding bit is 1
lo ^= ~((s16_t)hi) >> 16;
// first 16 bits
*dst++ = ((u32_t)lo << 16) | hi;
// we use 20 bits samples as we need to force parity
aux = spdif_bmclookup[(u8_t)(*src++ >> 12)];
aux = (u8_t) (aux ^ (~((s16_t)lo) >> 16));
aux |= (0xb3 ^ (((u16_t)((s8_t)aux)) >> 9)) << 8;
#endif
// VUCP-Bits: Valid, Subcode, Channelstatus, Parity = 0
// As parity is always 0, we can use fixed preambles
if (++cnt > 191) {
*dst++ = VUCP | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
cnt = 0;
} else {
*dst++ = VUCP | (PREAMBLE_M << 16) | aux;
}
// then do right channel, no need to check PREAMBLE_B
#if BYTES_PER_FRAME == 4
hi = spdif_bmclookup[(u8_t)(*src >> 8)];
lo = spdif_bmclookup[(u8_t) *src++];
lo ^= ~((s16_t)hi) >> 16;
*dst++ = ((u32_t)lo << 16) | hi;
aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);
#else
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
lo = spdif_bmclookup[(u8_t)(*src >> 16)];
lo ^= ~((s16_t)hi) >> 16;
*dst++ = ((u32_t)lo << 16) | hi;
aux = spdif_bmclookup[(u8_t)(*src++ >> 12)];
aux = (u8_t) (aux ^ (~((s16_t)lo) >> 16));
aux |= (0xb3 ^ (((u16_t)((s8_t)aux)) >> 9)) << 8;
#endif
*dst++ = VUCP | (PREAMBLE_W << 16) | aux;
}
*count = cnt;
}

View File

@@ -23,14 +23,8 @@
#include "squeezelite.h"
#if BYTES_PER_FRAM == 4
#define MAX_VAL16 0x7fffffffLL
#define MAX_SCALESAMPLE 0x7fffffffffffLL
#define MIN_SCALESAMPLE -MAX_SCALESAMPLE
#else
#define MAX_SCALESAMPLE 0x7fffffffffffLL
#define MIN_SCALESAMPLE -MAX_SCALESAMPLE
#endif
// inlining these on windows prevents them being linkable...
#if !WIN
@@ -49,7 +43,32 @@ s32_t to_gain(float f) {
return (s32_t)(f * 65536.0F);
}
void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, output_format format) {
void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, u8_t flags, output_format format) {
// in-place copy input samples if mono/combined is used (never happens with DSD active)
if ((flags & MONO_LEFT) && (flags & MONO_RIGHT)) {
s32_t *ptr = inputptr;
frames_t count = cnt;
while (count--) {
// use 64 bits integer for purists but should really not care
*ptr = *(ptr + 1) = ((s64_t) *ptr + (s64_t) *(ptr + 1)) / 2;
ptr += 2;
}
} else if (flags & MONO_RIGHT) {
s32_t *ptr = inputptr + 1;
frames_t count = cnt;
while (count--) {
*(ptr - 1) = *ptr;
ptr += 2;
}
} else if (flags & MONO_LEFT) {
s32_t *ptr = inputptr;
frames_t count = cnt;
while (count--) {
*(ptr + 1) = *ptr;
ptr += 2;
}
}
switch(format) {
#if DSD
case U32_LE:
@@ -356,17 +375,42 @@ void _apply_cross(struct buffer *outputbuf, frames_t out_frames, s32_t cross_gai
}
}
#if !WIN
inline
#endif
void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR) {
ISAMPLE_T *ptrL = (ISAMPLE_T *)(void *)outputbuf->readp;
ISAMPLE_T *ptrR = (ISAMPLE_T *)(void *)outputbuf->readp + 1;
while (count--) {
*ptrL = gain(gainL, *ptrL);
*ptrR = gain(gainR, *ptrR);
ptrL += 2;
ptrR += 2;
void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR, u8_t flags) {
if (gainL == FIXED_ONE && gainR == FIXED_ONE && !(flags & (MONO_LEFT | MONO_RIGHT))) {
return;
} else if ((flags & MONO_LEFT) && (flags & MONO_RIGHT)) {
ISAMPLE_T *ptrL = (ISAMPLE_T *)(void *)outputbuf->readp;
ISAMPLE_T *ptrR = (ISAMPLE_T *)(void *)outputbuf->readp + 1;
while (count--) {
*ptrL = *ptrR = (gain(gainL, *ptrL) + gain(gainR, *ptrR)) / 2;
ptrL += 2; ptrR += 2;
}
} else if (flags & MONO_RIGHT) {
ISAMPLE_T *ptr = (ISAMPLE_T *)(void *)outputbuf->readp + 1;
while (count--) {
*(ptr - 1) = *ptr = gain(gainR, *ptr);
ptr += 2;
}
} else if (flags & MONO_LEFT) {
ISAMPLE_T *ptr = (ISAMPLE_T *)(void *)outputbuf->readp;
while (count--) {
*(ptr + 1) = *ptr = gain(gainL, *ptr);
ptr += 2;
}
} else {
ISAMPLE_T *ptrL = (ISAMPLE_T *)(void *)outputbuf->readp;
ISAMPLE_T *ptrR = (ISAMPLE_T *)(void *)outputbuf->readp + 1;
while (count--) {
*ptrL = gain(gainL, *ptrL);
*ptrR = gain(gainR, *ptrR);
ptrL += 2; ptrR += 2;
}
}
}

View File

@@ -22,14 +22,14 @@
#include "squeezelite.h"
#define VISUEXPORT_SIZE 2048
#define VISUEXPORT_SIZE 512
EXT_BSS struct visu_export_s visu_export;
static struct visu_export_s *visu = &visu_export;
static log_level loglevel = lINFO;
void output_visu_export(s16_t *frames, frames_t out_frames, u32_t rate, bool silence, u32_t gain) {
void output_visu_export(void *frames, frames_t out_frames, u32_t rate, bool silence, u32_t gain) {
// no data to process
if (silence) {
@@ -37,17 +37,17 @@ void output_visu_export(s16_t *frames, frames_t out_frames, u32_t rate, bool sil
return;
}
// do not block, try to stuff data put wait for consumer to have used them
// do not block, try to stuff data but wait for consumer to have used them
if (!pthread_mutex_trylock(&visu->mutex)) {
// don't mix sample rates
if (visu->rate != rate) visu->level = 0;
// stuff buffer up and wait for consumer to read it (should reset level)
if (visu->level < visu->size) {
u32_t space = min(visu->size - visu->level, out_frames * 2) * 2;
u32_t space = min(visu->size - visu->level, out_frames) * BYTES_PER_FRAME;
memcpy(visu->buffer + visu->level, frames, space);
visu->level += space / 2;
visu->level += space / BYTES_PER_FRAME;
visu->running = true;
visu->rate = rate ? rate : 44100;
visu->gain = gain;
@@ -71,7 +71,7 @@ void output_visu_init(log_level level) {
visu->size = VISUEXPORT_SIZE;
visu->running = false;
visu->rate = 44100;
visu->buffer = malloc(VISUEXPORT_SIZE * sizeof(s16_t) * 2);
LOG_INFO("Initialize VISUEXPORT %u 16 bits samples", VISUEXPORT_SIZE);
visu->buffer = malloc(VISUEXPORT_SIZE * BYTES_PER_FRAME);
LOG_INFO("Initialize VISUEXPORT %u %u bits samples", VISUEXPORT_SIZE, BYTES_PER_FRAME * 4);
}

View File

@@ -204,7 +204,7 @@ static decode_state pcm_decode(void) {
out = process.max_in_frames;
);
if ((stream.state <= DISCONNECT && bytes == 0) || (limit && audio_left == 0)) {
if ((stream.state <= DISCONNECT && bytes < bytes_per_frame) || (limit && audio_left == 0)) {
UNLOCK_O_direct;
UNLOCK_S;
return DECODE_COMPLETE;

View File

@@ -122,7 +122,7 @@ void send_packet(u8_t *packet, size_t len) {
static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, u8_t mac[6]) {
#ifndef BASE_CAP
#define BASE_CAP "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION
#define BASE_CAP "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Balance=1,Firmware=" VERSION
#endif
#define SSL_CAP "CanHTTPS=1"
const char *base_cap;
@@ -397,8 +397,9 @@ static void process_strm(u8_t *pkt, int len) {
output.next_replay_gain = unpackN(&strm->replay_gain);
output.fade_mode = strm->transition_type - '0';
output.fade_secs = strm->transition_period;
output.invert = (strm->flags & 0x03) == 0x03;
LOG_DEBUG("set fade mode: %u", output.fade_mode);
output.invert = (strm->flags & 0x03) == 0x03;
output.channels = (strm->flags & 0x0c) >> 2;
LOG_DEBUG("set fade: %u, channels: %u, invert: %u", output.fade_mode, output.channels, output.invert);
UNLOCK_O;
}
break;
@@ -479,6 +480,9 @@ static void process_setd(u8_t *pkt, int len) {
LOG_INFO("set name: %s", setd->data);
// confirm change to server
sendSETDName(setd->data);
#if EMBEDDED
set_name(player_name);
#endif
// write name to name_file if -N option set
if (name_file) {
FILE *fp = fopen(name_file, "w");
@@ -822,14 +826,14 @@ in_addr_t discover_server(char *default_server, int max) {
memset(&s, 0, sizeof(s));
if (sendto(disc_sock, buf, len, 0, (struct sockaddr *)&d, sizeof(d)) < 0) {
LOG_INFO("error sending disovery");
LOG_INFO("error sending discovery");
}
if (poll(&pollinfo, 1, 5000) == 1) {
char readbuf[32], *p;
char readbuf[64], *p;
socklen_t slen = sizeof(s);
memset(readbuf, 0, 32);
recvfrom(disc_sock, readbuf, 32 - 1, 0, (struct sockaddr *)&s, &slen);
memset(readbuf, 0, sizeof(readbuf));
recvfrom(disc_sock, readbuf, sizeof(readbuf) - 1, 0, (struct sockaddr *)&s, &slen);
LOG_INFO("got response from: %s:%d", inet_ntoa(s.sin_addr), ntohs(s.sin_port));
if ((p = strstr(readbuf, port_d)) != NULL) {
@@ -913,7 +917,11 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con
LOCK_O;
snprintf(fixed_cap, FIXED_CAP_LEN, ",ModelName=%s,MaxSampleRate=%u", modelname ? modelname : MODEL_NAME_STRING,
#if RESAMPLE || RESAMPLE16
((maxSampleRate > 0) ? maxSampleRate : output.supported_rates[0]));
#else
((maxSampleRate > 0 && maxSampleRate < output.supported_rates[0]) ? maxSampleRate : output.supported_rates[0]));
#endif
for (i = 0; i < MAX_CODECS; i++) {
if (codecs[i] && codecs[i]->id && strlen(fixed_cap) < FIXED_CAP_LEN - 10) {
@@ -962,7 +970,7 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con
if (++failed_connect > 5 && !server) {
slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, MAX_SERVER_RETRIES);
if (!slimproto_ip) return;
} else if (MAX_SERVER_RETRIES && failed_connect > 5 * MAX_SERVER_RETRIES) return;
} else if (reconnect && MAX_SERVER_RETRIES && failed_connect > 5 * MAX_SERVER_RETRIES) return;
#else
// rediscover server if it was not set at startup or exit
if (!server && ++failed_connect > 5) {

View File

@@ -24,9 +24,9 @@
// make may define: PORTAUDIO, SELFPIPE, RESAMPLE, RESAMPLE_MP, VISEXPORT, GPIO, IR, DSD, LINKALL to influence build
#define MAJOR_VERSION "1.9"
#define MINOR_VERSION "2"
#define MICRO_VERSION "1145"
#define MAJOR_VERSION "1"
#define MINOR_VERSION "0"
#define MICRO_VERSION ""
#if defined(CUSTOM_VERSION)
#define VERSION "v" MAJOR_VERSION "." MINOR_VERSION "-" MICRO_VERSION STR(CUSTOM_VERSION)
@@ -471,7 +471,7 @@ void _wake_create(event_event*);
#define MAX_SILENCE_FRAMES 2048
#define FIXED_ONE 0x10000
#define FIXED_ONE 0x10000
#ifndef BYTES_PER_FRAME
#define BYTES_PER_FRAME 8
@@ -654,12 +654,15 @@ typedef enum { FADE_INACTIVE = 0, FADE_DUE, FADE_ACTIVE } fade_state;
typedef enum { FADE_UP = 1, FADE_DOWN, FADE_CROSS } fade_dir;
typedef enum { FADE_NONE = 0, FADE_CROSSFADE, FADE_IN, FADE_OUT, FADE_INOUT } fade_mode;
#define MONO_RIGHT 0x02
#define MONO_LEFT 0x01
#define MAX_SUPPORTED_SAMPLERATES 18
#define TEST_RATES = { 768000, 705600, 384000, 352800, 192000, 176400, 96000, 88200, 48000, 44100, 32000, 24000, 22500, 16000, 12000, 11025, 8000, 0 }
struct outputstate {
output_state state;
output_format format;
u8_t channels;
const char *device;
int external;
u32_t init_size;
@@ -673,7 +676,7 @@ struct outputstate {
unsigned latency;
int pa_hostapi_option;
#endif
int (* write_cb)(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
int (* write_cb)(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
unsigned start_frames;
unsigned frames_played;
unsigned frames_played_dmp;// frames played at the point delay is measured
@@ -756,9 +759,9 @@ void output_close_stdout(void);
#endif
// output_pack.c
void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, output_format format);
void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, u8_t flags, output_format format);
void _apply_cross(struct buffer *outputbuf, frames_t out_frames, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR);
void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR, u8_t flags);
s32_t gain(s32_t gain, s32_t sample);
s32_t to_gain(float f);

View File

@@ -334,7 +334,7 @@ static void *stream_thread() {
n = _recv(ssl, fd, streambuf->writep, space, 0);
if (n == 0) {
LOG_INFO("end of stream");
LOG_INFO("end of stream (%u bytes)", stream.bytes);
_disconnect(DISCONNECT, DISCONNECT_OK);
}
if (n < 0 && _last_error() != ERROR_WOULDBLOCK) {
@@ -521,19 +521,16 @@ void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, un
#if USE_SSL
if (ntohs(port) == 443) {
char *server = strcasestr(header, "Host:");
char server[256], *p;
ssl = SSL_new(SSLctx);
SSL_set_fd(ssl, sock);
// add SNI
sscanf(header, "Host:%255s", server);
if (server) {
char *p, *servername = malloc(1024);
sscanf(server, "Host:%255[^:]s", servername);
for (p = servername; *p == ' '; p++);
SSL_set_tlsext_host_name(ssl, p);
free(servername);
if ((p = strchr(server, ':')) != NULL) *p = '\0';
SSL_set_tlsext_host_name(ssl, server);
}
while (1) {

View File

@@ -20,7 +20,7 @@
#include "adac.h"
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
#define TAS5713 0x36 /* i2c address of TAS5713 */
#define TAS5713 (0x36 >> 1) /* i2c address of TAS5713 */
// TAS5713 I2C-bus register addresses
@@ -40,13 +40,12 @@
static const char TAG[] = "TAS5713";
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static void deinit(void);
static void speaker(bool active) { };
static void headset(bool active) { } ;
static bool volume(unsigned left, unsigned right);
static void power(adac_power_e mode) { };
const struct adac_s dac_tas5713 = {"TAS5713", init, deinit, power, speaker, headset, volume};
const struct adac_s dac_tas5713 = {"TAS5713", init, adac_deinit, power, speaker, headset, volume};
struct tas5713_cmd_s {
uint8_t reg;
@@ -63,53 +62,30 @@ typedef enum {
TAS57_VOLUME
} dac_cmd_e;
static int i2c_port;
static void tas5713_set(uint8_t reg, uint8_t val);
static uint8_t tas5713_get(uint8_t reg);
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
char *p;
i2c_port = i2c_port_num;
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = -1,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = -1,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 250000,
};
if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
i2c_param_config(i2c_port, &i2c_config);
esp_err_t res = i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
/* find if there is a tas5713 attached. Reg 0 should read non-zero if so */
if (!tas5713_get(0x00)) {
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
/* find if there is a tas5713 attached. Reg 0 should read non-zero but not 255 if so */
adac_init(config, i2c_port);
if (adac_read_byte(TAS5713, 0x00) == 255) {
ESP_LOGW(TAG, "No TAS5713 detected");
i2c_driver_delete(i2c_port);
adac_deinit();
return 0;
}
ESP_LOGI(TAG, "TAS5713 uses I2C sda:%d, scl:%d", i2c_config.sda_io_num, i2c_config.scl_io_num);
ESP_LOGI(TAG, "TAS5713 found");
/* do the init sequence */
tas5713_set(TAS5713_OSC_TRIM, 0x00); /* a delay is required after this */
esp_err_t res = adac_write_byte(TAS5713, TAS5713_OSC_TRIM, 0x00); /* a delay is required after this */
vTaskDelay(50 / portTICK_PERIOD_MS);
tas5713_set(TAS5713_SERIAL_DATA_INTERFACE, 0x03); /* I2S LJ 16 bit */
tas5713_set(TAS5713_SYSTEM_CTRL2, 0x00); /* exit all channel shutdown */
tas5713_set(TAS5713_SOFT_MUTE, 0x00); /* unmute */
tas5713_set(TAS5713_VOL_MASTER, 0x20);
tas5713_set(TAS5713_VOL_CH1, 0x30);
tas5713_set(TAS5713_VOL_CH2, 0x30);
tas5713_set(TAS5713_VOL_HEADPHONE, 0xFF);
res |= adac_write_byte(TAS5713, TAS5713_SERIAL_DATA_INTERFACE, 0x03); /* I2S LJ 16 bit */
res |= adac_write_byte(TAS5713, TAS5713_SYSTEM_CTRL2, 0x00); /* exit all channel shutdown */
res |= adac_write_byte(TAS5713, TAS5713_SOFT_MUTE, 0x00); /* unmute */
res |= adac_write_byte(TAS5713, TAS5713_VOL_MASTER, 0x20);
res |= adac_write_byte(TAS5713, TAS5713_VOL_CH1, 0x30);
res |= adac_write_byte(TAS5713, TAS5713_VOL_CH2, 0x30);
res |= adac_write_byte(TAS5713, TAS5713_VOL_HEADPHONE, 0xFF);
/* The tas5713 typically has the mclk connected to the sclk. In this
configuration, mclk must be a multiple of the sclk. The lowest workable
@@ -126,70 +102,9 @@ static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
return true;
}
/****************************************************************************************
* init
*/
static void deinit(void) {
i2c_driver_delete(i2c_port);
}
/****************************************************************************************
* change volume
*/
static bool volume(unsigned left, unsigned right) {
return false;
}
/****************************************************************************************
* DAC specific commands
*/
void tas5713_set(uint8_t reg, uint8_t val) {
esp_err_t ret = ESP_OK;
ESP_LOGI(TAG,"TAS5713 send %x %x", reg, val);
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd,
TAS5713 | I2C_MASTER_WRITE,
I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, reg, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, val, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd);
ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Could not send command to TAS5713 %d", ret);
}
}
/*************************************************************************
* Read from i2c for the tas5713. This doubles as tas5713 detect. This function
* returns zero on error, so read register 0x00 for tas detect, which will be
* non-zero in this application.
*/
static uint8_t tas5713_get(uint8_t reg) {
int ret;
uint8_t data = 0;
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, TAS5713 | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, reg, I2C_MASTER_NACK);
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, TAS5713 | I2C_MASTER_READ, I2C_MASTER_NACK);
i2c_master_read_byte(i2c_cmd, &data, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd);
ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
if (ret == ESP_OK) {
ESP_LOGI(TAG,"TAS5713 reg 0x%x is 0x%x", reg, data);
}
return data;
}

View File

@@ -18,19 +18,18 @@
#include "esp_log.h"
#include "adac.h"
#define TAS575x 0x98
#define TAS578x 0x90
#define TAS575x (0x98 >> 1)
#define TAS578x (0x90 >> 1)
static const char TAG[] = "TAS575x/8x";
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static void deinit(void);
static void speaker(bool active);
static void headset(bool active);
static bool volume(unsigned left, unsigned right);
static void power(adac_power_e mode);
const struct adac_s dac_tas57xx = { "TAS57xx", init, deinit, power, speaker, headset, volume };
const struct adac_s dac_tas57xx = { "TAS57xx", init, adac_deinit, power, speaker, headset, volume };
struct tas57xx_cmd_s {
uint8_t reg;
@@ -44,6 +43,11 @@ static const struct tas57xx_cmd_s tas57xx_init_sequence[] = {
{ 0x25, 0x08 }, // ignore SCK halt
{ 0x08, 0x10 }, // Mute control enable (from TAS5780)
{ 0x54, 0x02 }, // Mute output control (from TAS5780)
#if BYTES_PER_FRAME == 8
{ 0x28, 0x03 }, // I2S length 32 bits
#else
{ 0x28, 0x00 }, // I2S length 16 bits
#endif
{ 0x02, 0x00 }, // restart
{ 0xff, 0xff } // end of table
};
@@ -60,7 +64,6 @@ static const struct tas57xx_cmd_s tas57xx_cmd[] = {
};
static uint8_t tas57_addr;
static int i2c_port;
static void dac_cmd(dac_cmd_e cmd, ...);
static int tas57_detect(void);
@@ -68,32 +71,14 @@ static int tas57_detect(void);
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
char *p;
i2c_port = i2c_port_num;
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = -1,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = -1,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 250000,
};
if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
i2c_param_config(i2c_port, &i2c_config);
i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
// find which TAS we are using (if any)
tas57_addr = tas57_detect();
tas57_addr = adac_init(config, i2c_port);
if (!tas57_addr) tas57_addr = tas57_detect();
if (!tas57_addr) {
ESP_LOGW(TAG, "No TAS57xx detected");
i2c_driver_delete(i2c_port);
adac_deinit();
return false;
}
@@ -101,7 +86,7 @@ static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
for (int i = 0; tas57xx_init_sequence[i].reg != 0xff; i++) {
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, tas57_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, (tas57_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, tas57xx_init_sequence[i].reg, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, tas57xx_init_sequence[i].value, I2C_MASTER_NACK);
ESP_LOGD(TAG, "i2c write %x at %u", tas57xx_init_sequence[i].reg, tas57xx_init_sequence[i].value);
@@ -111,8 +96,6 @@ static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
ESP_LOGI(TAG, "TAS57xx uses I2C sda:%d, scl:%d", i2c_config.sda_io_num, i2c_config.scl_io_num);
if (res != ESP_OK) {
ESP_LOGE(TAG, "could not intialize TAS57xx %d", res);
return false;
@@ -121,13 +104,6 @@ static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
return true;
}
/****************************************************************************************
* init
*/
static void deinit(void) {
i2c_driver_delete(i2c_port);
}
/****************************************************************************************
* change volume
*/
@@ -176,25 +152,17 @@ void dac_cmd(dac_cmd_e cmd, ...) {
esp_err_t ret = ESP_OK;
va_start(args, cmd);
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
switch(cmd) {
case TAS57_VOLUME:
ESP_LOGE(TAG, "DAC volume not handled yet");
break;
default:
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, tas57_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, tas57xx_cmd[cmd].reg, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, tas57xx_cmd[cmd].value, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd);
ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
ret = adac_write_byte(tas57_addr, tas57xx_cmd[cmd].reg, tas57xx_cmd[cmd].value);
}
i2c_cmd_link_delete(i2c_cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "could not intialize TAS57xx %d", ret);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "could not use TAS57xx %d", ret);
}
va_end(args);
@@ -204,25 +172,10 @@ void dac_cmd(dac_cmd_e cmd, ...) {
* TAS57 detection
*/
static int tas57_detect(void) {
uint8_t data, addr[] = {TAS578x, TAS575x};
int ret;
uint8_t addr[] = {TAS578x, TAS575x};
for (int i = 0; i < sizeof(addr); i++) {
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, addr[i] | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, 00, I2C_MASTER_NACK);
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, addr[i] | I2C_MASTER_READ, I2C_MASTER_NACK);
i2c_master_read_byte(i2c_cmd, &data, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd);
ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
if (ret == ESP_OK) {
if (adac_read_byte(addr[i], 0) != 255) {
ESP_LOGI(TAG, "Detected TAS @0x%x", addr[i]);
return addr[i];
}

View File

@@ -236,7 +236,12 @@ static decode_state vorbis_decode(void) {
// work backward to unpack samples (if needed)
iptr = (s16_t *) write_buf + count;
optr = (ISAMPLE_T *) write_buf + frames * 2;
IF_DIRECT(
optr = (ISAMPLE_T *) outputbuf->writep + frames * 2;
)
IF_PROCESS(
optr = (ISAMPLE_T *) write_buf + frames * 2;
)
if (channels == 2) {
#if BYTES_PER_FRAME == 4

View File

@@ -0,0 +1,98 @@
/*
* Squeezelite for esp32
*
* (c) Wizmo 2021
* Sebastien 2019
* Philippe G. 2019, philippe_44@outlook.com
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*
*/
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/i2s.h>
#include "driver/i2c.h"
#include "esp_log.h"
#include "adac.h"
static const char TAG[] = "WM8978";
static void speaker(bool active) { }
static void headset(bool active) { }
static bool volume(unsigned left, unsigned right) { return false; }
static void power(adac_power_e mode);
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static esp_err_t i2c_write_shadow(uint8_t reg, uint16_t val);
static uint16_t i2c_read_shadow(uint8_t reg);
static int WM8978;
const struct adac_s dac_wm8978 = { "WM8978", init, adac_deinit, power, speaker, headset, volume };
// initiation table for non-readbale 9-bit i2c registers
static uint16_t WM8978_REGVAL_TBL[58] = {
0X0000, 0X0000, 0X0000, 0X0000, 0X0050, 0X0000, 0X0140, 0X0000,
0X0000, 0X0000, 0X0000, 0X00FF, 0X00FF, 0X0000, 0X0100, 0X00FF,
0X00FF, 0X0000, 0X012C, 0X002C, 0X002C, 0X002C, 0X002C, 0X0000,
0X0032, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000,
0X0038, 0X000B, 0X0032, 0X0000, 0X0008, 0X000C, 0X0093, 0X00E9,
0X0000, 0X0000, 0X0000, 0X0000, 0X0003, 0X0010, 0X0010, 0X0100,
0X0100, 0X0002, 0X0001, 0X0001, 0X0039, 0X0039, 0X0039, 0X0039,
0X0001, 0X0001
};
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
WM8978 = adac_init(config, i2c_port);
if (!WM8978) WM8978 = 0x1a;
ESP_LOGI(TAG, "WM8978 detected @%d", WM8978);
// init sequence
i2c_write_shadow(0, 0);
i2c_write_shadow(4, 16);
i2c_write_shadow(6, 0);
i2c_write_shadow(10, 8);
i2c_write_shadow(43, 16);
i2c_write_shadow(49, 102);
// Configure system clk to GPIO0 for DAC MCLK input
ESP_LOGI(TAG, "Configuring MCLK on GPIO0");
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_WRITE(PIN_CTRL, 0xFFFFFFF0);
return true;
}
/****************************************************************************************
* power
*/
static void power(adac_power_e mode) {
uint16_t *data, off[] = {0, 0, 0}, on[] = {11, 384, 111};
data = (mode == ADAC_STANDBY || mode == ADAC_OFF) ? off : on;
i2c_write_shadow(1, data[0]);
i2c_write_shadow(2, data[1]);
i2c_write_shadow(3, data[2]);
}
/****************************************************************************************
* Write with custom reg/value structure
*/
static esp_err_t i2c_write_shadow(uint8_t reg, uint16_t val) {
WM8978_REGVAL_TBL[reg] = val;
reg = (reg << 1) | ((val >> 8) & 0x01);
val &= 0xff;
return adac_write_byte(WM8978, reg, val);
}
/****************************************************************************************
* Return local register value
*/
static uint16_t i2c_read_shadow(uint8_t reg) {
return WM8978_REGVAL_TBL[reg];
}

Some files were not shown because too many files have changed in this diff Show More