mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-14 15:37:10 +03:00
Compare commits
209 Commits
ESP32-A1S.
...
misc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c36348e99d | ||
|
|
faa9976d3d | ||
|
|
1dbffe6753 | ||
|
|
8e5dbd2144 | ||
|
|
974ff5fa68 | ||
|
|
4dce4b307b | ||
|
|
4aa54537e5 | ||
|
|
d27a430664 | ||
|
|
0fa41e5ef7 | ||
|
|
2146014f04 | ||
|
|
7d409668c8 | ||
|
|
0ebce7472b | ||
|
|
5f5466fcb0 | ||
|
|
71911914cb | ||
|
|
4f6dcc2cc7 | ||
|
|
f4615462c7 | ||
|
|
6033c7fc14 | ||
|
|
a98b1d00b0 | ||
|
|
1e45348e4a | ||
|
|
96a05d8a6b | ||
|
|
7a9f3e0781 | ||
|
|
3575245324 | ||
|
|
c36d663a7f | ||
|
|
13294ddd0c | ||
|
|
449ef32a4e | ||
|
|
50390dbc61 | ||
|
|
fc5f3f5ac9 | ||
|
|
6f4ed0679e | ||
|
|
511df0b4b8 | ||
|
|
6682ee947e | ||
|
|
813e7ee712 | ||
|
|
6c9537e259 | ||
|
|
ac9ad2eee2 | ||
|
|
f38840bbe0 | ||
|
|
fd56f649ab | ||
|
|
c4bd320afe | ||
|
|
5e8978d1af | ||
|
|
6e082a5654 | ||
|
|
5af3250aea | ||
|
|
527187b2f8 | ||
|
|
102d2f6af5 | ||
|
|
4edd429b0a | ||
|
|
35099d1131 | ||
|
|
ce9c3952e8 | ||
|
|
4ee6352185 | ||
|
|
3a720a1e7d | ||
|
|
987fa5d18c | ||
|
|
23c936ec93 | ||
|
|
d4f10a761e | ||
|
|
d68d163538 | ||
|
|
94580c6771 | ||
|
|
2717277c6e | ||
|
|
b6d537a207 | ||
|
|
cf047c8098 | ||
|
|
21f3768ada | ||
|
|
9cb18fa980 | ||
|
|
d4f6289500 | ||
|
|
21c3ce1fba | ||
|
|
48e8525ba9 | ||
|
|
0db9631700 | ||
|
|
192cb975e2 | ||
|
|
d1f6085199 | ||
|
|
afaa5323d7 | ||
|
|
f92447e9b9 | ||
|
|
22f8d1d88b | ||
|
|
8136b7fd9a | ||
|
|
e588deb3af | ||
|
|
38ec8ac6f8 | ||
|
|
791167f794 | ||
|
|
c9b859ef8c | ||
|
|
db74419bd7 | ||
|
|
265403d364 | ||
|
|
d3dd8b9078 | ||
|
|
de41fb9583 | ||
|
|
2699216d25 | ||
|
|
c7b1c7cd83 | ||
|
|
f51af21b90 | ||
|
|
5ec63f400c | ||
|
|
1753e11698 | ||
|
|
58c6ca059b | ||
|
|
5ef63dc3e4 | ||
|
|
468c847499 | ||
|
|
8c0e766cd7 | ||
|
|
0f792d71ee | ||
|
|
1fc7675c14 | ||
|
|
7b439ae6ee | ||
|
|
c5d7fd521d | ||
|
|
a856b26181 | ||
|
|
a949ec2d24 | ||
|
|
5ec5236991 | ||
|
|
d4cd400cd9 | ||
|
|
64bb5f018b | ||
|
|
593927aac3 | ||
|
|
51761d0890 | ||
|
|
5c56abfe75 | ||
|
|
9ac7c5bbeb | ||
|
|
be28555a40 | ||
|
|
1d32479bc4 | ||
|
|
c83ddc4adc | ||
|
|
5a7cf9b8fe | ||
|
|
190326726c | ||
|
|
387276f2f3 | ||
|
|
87b8c46263 | ||
|
|
d7aef11856 | ||
|
|
be842d235d | ||
|
|
d49ad66177 | ||
|
|
ca28669b0a | ||
|
|
45f480d948 | ||
|
|
852e312879 | ||
|
|
c79cf5b58f | ||
|
|
1a4a8ba559 | ||
|
|
7ad39a02f5 | ||
|
|
f96d06912f | ||
|
|
36571d3dad | ||
|
|
b075bbaea3 | ||
|
|
ec7982dbfc | ||
|
|
afd76bdfbb | ||
|
|
89ecd19d32 | ||
|
|
8904ec1afd | ||
|
|
5120029643 | ||
|
|
4a529d6fbd | ||
|
|
afe697e4b1 | ||
|
|
879b1f9107 | ||
|
|
5a7d4fd535 | ||
|
|
d61c650f39 | ||
|
|
f096ee269e | ||
|
|
0610e1a2bc | ||
|
|
68db286777 | ||
|
|
5075878f05 | ||
|
|
d965187d2c | ||
|
|
e25b098678 | ||
|
|
a3b23bffc2 | ||
|
|
dbc7a6b14e | ||
|
|
a105f7fd99 | ||
|
|
1b39a4f7c9 | ||
|
|
cac6306a04 | ||
|
|
749d71a36f | ||
|
|
4d40355d5c | ||
|
|
c311faa90f | ||
|
|
0821551a2f | ||
|
|
3a2bfe470f | ||
|
|
f6b55c5ac9 | ||
|
|
f9e97036cf | ||
|
|
4444fed343 | ||
|
|
bc0d104290 | ||
|
|
bdec9d25c6 | ||
|
|
b25367fc0c | ||
|
|
603296d921 | ||
|
|
a5b7d24dca | ||
|
|
2733e3ec23 | ||
|
|
9c79888b72 | ||
|
|
2b59c38b4b | ||
|
|
a13afd76c9 | ||
|
|
b98a481858 | ||
|
|
9e27a0e21d | ||
|
|
d08f7142ae | ||
|
|
efa3f1f07d | ||
|
|
263679dcac | ||
|
|
50d7d57f48 | ||
|
|
99e4b107d6 | ||
|
|
d293de4b64 | ||
|
|
554cf89ac2 | ||
|
|
df36b65916 | ||
|
|
d9a6b37d19 | ||
|
|
0629b017b1 | ||
|
|
43aa62ac56 | ||
|
|
22c2044f17 | ||
|
|
7457632990 | ||
|
|
361cc08e3c | ||
|
|
e742905fbd | ||
|
|
4b719deddf | ||
|
|
b57e79ca5f | ||
|
|
6ef4c78b3b | ||
|
|
f0002293a0 | ||
|
|
29997c40b2 | ||
|
|
ac5d54e6c1 | ||
|
|
b3eae8dad1 | ||
|
|
6804e81249 | ||
|
|
8639566909 | ||
|
|
87bf6255f4 | ||
|
|
6084af8fbf | ||
|
|
8ec124c47c | ||
|
|
b8bb881820 | ||
|
|
ccb4842e13 | ||
|
|
3a7addad2e | ||
|
|
644f4eb1e6 | ||
|
|
00bab8f76b | ||
|
|
72c084d7c0 | ||
|
|
12e7d2d8fb | ||
|
|
f5bb058541 | ||
|
|
5871252869 | ||
|
|
d5bf498d3d | ||
|
|
eb647aeea3 | ||
|
|
451f187856 | ||
|
|
f79c7d4ace | ||
|
|
01a44be0ca | ||
|
|
4dc6424fed | ||
|
|
a989fe06c2 | ||
|
|
1b5a877b98 | ||
|
|
9c9f79b0b6 | ||
|
|
6fef6d679e | ||
|
|
aa54b9dff9 | ||
|
|
05a704e7ec | ||
|
|
09e6518870 | ||
|
|
4d70d0998c | ||
|
|
9d0d957ec3 | ||
|
|
1be4c89f3c | ||
|
|
893e67dfa4 | ||
|
|
5066351b24 |
21
.github/workflows/CrossBuild.yml
vendored
21
.github/workflows/CrossBuild.yml
vendored
@@ -1,5 +1,4 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Cross-Build
|
||||
|
||||
on:
|
||||
@@ -26,13 +25,13 @@ jobs:
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
matrix:
|
||||
node: [I2S-4MFlash, ESP32-A1S, SqueezeAmp]
|
||||
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=V0." >> $GITHUB_ENV
|
||||
echo "build_version_prefix=1." >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 15
|
||||
@@ -41,10 +40,14 @@ jobs:
|
||||
id: cache-build
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: build
|
||||
key: ${{ runner.os }}-${{ matrix.node }}
|
||||
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\-~!@_\.]/}"
|
||||
@@ -54,7 +57,7 @@ jobs:
|
||||
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="dev.${BUILD_NUMBER}-${{matrix.depth}}#v4.0#${TARGET_BUILD_NAME}#${branch_name}"
|
||||
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"
|
||||
@@ -81,7 +84,9 @@ jobs:
|
||||
run: |
|
||||
env | grep "artifact\|tag\|GITHUB\|version\|NUMBER\|TARGET" >${TARGET_BUILD_NAME}-env.txt
|
||||
echo "${tag}" >version.txt
|
||||
docker run --env-file=${TARGET_BUILD_NAME}-env.txt --rm -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 }} && 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"
|
||||
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
|
||||
@@ -107,6 +112,8 @@ jobs:
|
||||
build/size_comp1.txt
|
||||
build/size_comp2.txt
|
||||
partitions.csv
|
||||
sdkconfig
|
||||
server_certs/github.pem
|
||||
build_output.zip
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -98,3 +98,5 @@ test/.vscode/
|
||||
|
||||
node_modules/*
|
||||
esp-dsp/
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -2,6 +2,9 @@ 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 )
|
||||
|
||||
211
README.md
211
README.md
@@ -7,13 +7,14 @@ Squeezelite-esp32 is an audio software suite made to run on espressif's ESP32 wi
|
||||
- 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 (you'll need *one* resistor...)
|
||||
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 I2S (supported drivers are SH1106, SSD1306, SSD1322, SSD1326/7, SSD1351, ST7735 and ST7789).
|
||||
- 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
|
||||
|
||||
@@ -23,24 +24,39 @@ Other features include
|
||||
- Full web interface for further configuration/management
|
||||
- Firmware over-the-air update
|
||||
|
||||
**Important note** (philippe44 writing)
|
||||
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
|
||||
|
||||
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. 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 very little tests have been done. You can chose to compile it in 32 bits mode by changing the cmake file in components/squeezelite. Note the following limitation in 32 bits
|
||||
## 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.
|
||||
|
||||
- no resampling
|
||||
- no equalizer
|
||||
- buffer are smaller, so crossfade will be at best 5s at 44.1 kHz
|
||||
- SPDIF is 20 bits maximum *(1)*
|
||||
- display will be slower
|
||||
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).
|
||||
|
||||
I've not tested all codecs, I've only verified it with TAS57xx DAC and in general I've not tested that mode more than a few minutes. I'm not very interested above 16 bits samples because it does not bring anything (I have an engineering background in theory of information). On memory Some might correctly comment that 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.
|
||||
| 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, then resampling *must* be enabled (using -R) option if you want to send audio with rate other than 44.1kHz. Similarly, when using SPDIF, only 44.1kHz and 48kHz are supported so you might have to enable resampling as well. If you connect a DAC, choice will depends on its capabilities. See below for more details.
|
||||
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
|
||||
@@ -55,7 +71,7 @@ NB: You can use the pre-build binaries SqueezeAMP4MBFlash which has all the hard
|
||||
- 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 shown above has the following IO set
|
||||
- amplifier: GPIO21
|
||||
@@ -72,11 +88,15 @@ The board shown above has the following IO set
|
||||
|
||||
So a possible config would be
|
||||
- set_GPIO: 21=amp,22=green:0,39=jack:0
|
||||
- dac_config: model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32
|
||||
- a button mapping:
|
||||
```
|
||||
[{"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
|
||||
|
||||
@@ -114,19 +134,21 @@ 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>[,mute=<gpio>[:0|1][,model=TAS57xx|TAS5713|AC101|I2S][,sda=<gpio>,scl=gpio[,i2c=<addr>]]
|
||||
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. I2C parameters are optional an only needed if your dac requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed.
|
||||
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.
|
||||
|
||||
The parameter "dac_controlset" allows definition of simple commands to be sent over i2c for init, power on and off using a JSON syntax:
|
||||
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"} ],
|
||||
@@ -134,7 +156,9 @@ The parameter "dac_controlset" allows definition of simple commands to be sent o
|
||||
```
|
||||
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 well-known configuration, this is ignored
|
||||
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.
|
||||
|
||||
@@ -147,11 +171,23 @@ 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>][,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[,rotate]]
|
||||
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
|
||||
@@ -160,15 +196,16 @@ SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,back=<gpio>][,reset=<gpio>][,speed
|
||||
- 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 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)
|
||||
- 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 256x64 grayscale 16-levels SPI in multiple sizes [here](https://www.buydisplay.com/oled-display/oled-display-module?resolution=159) - it is very nice
|
||||
- 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
|
||||
|
||||
To use the display on LMS, add repository https://raw.githubusercontent.com/sle118/squeezelite-esp32/master/plugin/repo.xml. You will then be able to tweak how the vu-meter and spectrum analyzer are displayed, as well as size of artwork. You can also install the excellent plugin "Music Information Screen" which is super useful to tweak the layout.
|
||||
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
|
||||
```
|
||||
@@ -208,6 +245,7 @@ Syntax is:
|
||||
<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 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
|
||||
```
|
||||
@@ -238,6 +276,7 @@ The SW gpio is optional, you can re-affect it to a pure button if you prefer but
|
||||
|
||||
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
|
||||
```
|
||||
@@ -272,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
|
||||
@@ -327,12 +367,55 @@ 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
|
||||
|
||||
### 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
|
||||
**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:
|
||||
```
|
||||
channel=0..7,scale=<scale>,cells=<2|3>
|
||||
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. 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>,cells=<2|3>[,atten=<0|1|2|3>]
|
||||
```
|
||||
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
|
||||
@@ -405,67 +488,25 @@ The above command will mount this repo into the docker container and start a bas
|
||||
for you to then follow the below build steps
|
||||
|
||||
### Manual Install of ESP-IDF
|
||||
<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/ or see here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/windows-setup.html for a direct install.
|
||||
|
||||
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/
|
||||
And then copying the i2s.c patch file from this repo over to the esp-idf folder
|
||||
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. As of this writing (08.2020), espressif has patched esp-dsp so this is no more needed
|
||||
**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
|
||||
Don't forget the 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)
|
||||
### Using make (deprecated)
|
||||
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
|
||||
When initially cloning the repo, make sure you do it recursively. For example: `git clone --recursive https://github.com/sle118/squeezelite-esp32.git`
|
||||
|
||||
```
|
||||
# Build recovery.bin, bootloader.bin, ota_data_initial.bin, partitions.bin
|
||||
# force appropriate rebuild by touching all the files which may have a RECOVERY_APPLICATION specific source compile logic
|
||||
find . \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -type f -print0 | xargs -0 grep -l "RECOVERY_APPLICATION" | xargs touch
|
||||
export PROJECT_NAME="recovery"
|
||||
make -j4 all EXTRA_CPPFLAGS='-DRECOVERY_APPLICATION=1'
|
||||
make flash
|
||||
#
|
||||
# Build squeezelite.bin
|
||||
# Now force a rebuild by touching all the files which may have a RECOVERY_APPLICATION specific source compile logic
|
||||
find . \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -type f -print0 | xargs -0 grep -l "RECOVERY_APPLICATION" | xargs touch
|
||||
export PROJECT_NAME="squeezelite"
|
||||
make -j4 app EXTRA_CPPFLAGS='-DRECOVERY_APPLICATION=0'
|
||||
python ${IDF_PATH}/components/esptool_py/esptool/esptool.py --chip esp32 --port ${ESPPORT} --baud ${ESPBAUD} --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0x150000 ./build/squeezelite.bin
|
||||
# monitor serial output
|
||||
make monitor
|
||||
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)
|
||||
|
||||
You can also manually download the recovery & initial boot
|
||||
```
|
||||
python ${IDF_PATH}/components/esptool_py/esptool/esptool.py --chip esp32 --port ${ESPPORT} --baud ${ESPBAUD} --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xd000 ./build/ota_data_initial.bin 0x1000 ./build/bootloader/bootloader.bin 0x10000 ./build/recovery.bin 0x8000 ./build/partitions.bin
|
||||
```
|
||||
### Using cmake
|
||||
Create you config using 'idf.py menuconfig' then build binaries using 'idf.py all'. It will build the recovery and the application (squeezelite) itself. See the recommended command to upload everything. Otherwise, if you just want to download squeezelite, do
|
||||
```
|
||||
python.exe <idf_path>\components\esptool_py\esptool\esptool.py -p COM<n> -b 921600 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x150000 build\squeezelite.bin
|
||||
```
|
||||
Use 'idf monitor' to monitor the application (see esp-idf documentation)
|
||||
## Additional misc notes to do you build (kitchen sink)
|
||||
- don't forget to set IDF_PATH, ESPPORT and ESPBAUD
|
||||
- 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
|
||||
- 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
|
||||
- misc compiler #define
|
||||
- use no resampling or set RESAMPLE (soxr - but overloads CPU) 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)
|
||||
### codecs
|
||||
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 (but using helixaac is a better option)
|
||||
@@ -480,7 +521,7 @@ Use 'idf monitor' to monitor the application (see esp-idf documentation)
|
||||
- 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
|
||||
- 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
|
||||
|
||||
@@ -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
|
||||
@@ -626,8 +629,8 @@ CONFIG_LWIP_MAX_SOCKETS=16
|
||||
CONFIG_LWIP_SO_REUSE=y
|
||||
CONFIG_LWIP_SO_REUSE_RXTOALL=y
|
||||
#CONFIG_LWIP_IP_REASSEMBLY is not set
|
||||
CONFIG_LWIP_IP6_REASSEMBLY=Y
|
||||
CONFIG_LWIP_IP4_REASSEMBLY=Y
|
||||
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
|
||||
|
||||
@@ -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
|
||||
#
|
||||
@@ -460,8 +463,8 @@ CONFIG_LWIP_SO_REUSE_RXTOALL=y
|
||||
# CONFIG_LWIP_STATS is not set
|
||||
# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set
|
||||
#CONFIG_LWIP_IP_REASSEMBLY is not set
|
||||
CONFIG_LWIP_IP6_REASSEMBLY=Y
|
||||
CONFIG_LWIP_IP4_REASSEMBLY=Y
|
||||
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
|
||||
|
||||
@@ -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
|
||||
#
|
||||
@@ -456,8 +459,8 @@ CONFIG_LWIP_MAX_SOCKETS=16
|
||||
CONFIG_LWIP_SO_REUSE=y
|
||||
CONFIG_LWIP_SO_REUSE_RXTOALL=y
|
||||
#CONFIG_LWIP_IP_REASSEMBLY is not set
|
||||
CONFIG_LWIP_IP6_REASSEMBLY=Y
|
||||
CONFIG_LWIP_IP4_REASSEMBLY=Y
|
||||
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
|
||||
|
||||
@@ -2,9 +2,14 @@ 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
|
||||
)
|
||||
|
||||
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 )
|
||||
|
||||
BIN
components/codecs/lib/libhelix-aac-sbr.a
Normal file
BIN
components/codecs/lib/libhelix-aac-sbr.a
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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 ); }
|
||||
@@ -135,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);
|
||||
|
||||
@@ -62,7 +62,7 @@ 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"};
|
||||
@@ -70,7 +70,7 @@ 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;
|
||||
|
||||
@@ -90,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);
|
||||
}
|
||||
|
||||
@@ -135,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
|
||||
};
|
||||
|
||||
@@ -284,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);
|
||||
}
|
||||
@@ -491,20 +493,29 @@ 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;
|
||||
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;
|
||||
rn_param.volume = s_volume;
|
||||
@@ -529,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;
|
||||
}
|
||||
@@ -597,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
|
||||
*/
|
||||
@@ -630,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");
|
||||
|
||||
@@ -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}\"")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}\" ")
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,6 +24,7 @@ const char * desc_dac= "DAC Options";
|
||||
const char * desc_spdif= "SPDIF Options";
|
||||
const char * desc_audio= "General Audio Options";
|
||||
const char * desc_bt_source= "Bluetooth Audio Output Options";
|
||||
const char * desc_rotary= "Rotary Control";
|
||||
|
||||
|
||||
#define CODECS_BASE "flac|pcm|mp3|ogg"
|
||||
@@ -70,7 +71,7 @@ typedef enum {
|
||||
} parse_state_t;
|
||||
static const char *TAG = "cmd_config";
|
||||
extern struct arg_end *getParmsEnd(struct arg_hdr * * argtable);
|
||||
//bck=<gpio>,ws=<gpio>,do=<gpio>[,mute=<gpio>[:0|1][,model=TAS57xx|TAS5713|AC101|I2S][,sda=<gpio>,scl=gpio[,i2c=<addr>]]
|
||||
//bck=<gpio>,ws=<gpio>,do=<gpio>[,mute=<gpio>[:0|1][,model=TAS57xx|TAS5713|AC101|WM8978|I2S][,sda=<gpio>,scl=gpio[,i2c=<addr>]]
|
||||
static struct {
|
||||
struct arg_str *model_name;
|
||||
struct arg_int *clock;
|
||||
@@ -85,6 +86,19 @@ static struct {
|
||||
struct arg_end *end;
|
||||
} i2s_args;
|
||||
|
||||
static struct {
|
||||
struct arg_rem * rem;
|
||||
struct arg_int * A;
|
||||
struct arg_int * B;
|
||||
struct arg_int * SW;
|
||||
struct arg_lit * volume_lock;
|
||||
struct arg_lit * longpress;
|
||||
struct arg_lit * knobonly;
|
||||
struct arg_int * timer;
|
||||
struct arg_lit * clear;
|
||||
struct arg_end * end;
|
||||
} rotary_args;
|
||||
//config_rotary_get
|
||||
static struct{
|
||||
struct arg_str *sink_name;
|
||||
struct arg_str *pin_code;
|
||||
@@ -142,7 +156,7 @@ static struct {
|
||||
struct arg_end *end;
|
||||
} squeezelite_args;
|
||||
|
||||
int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory){
|
||||
int is_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory, bool output){
|
||||
int res = 0;
|
||||
const char * name = gpio->hdr.longopts?gpio->hdr.longopts:gpio->hdr.glossary;
|
||||
*gpio_out=-1;
|
||||
@@ -152,7 +166,7 @@ int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandato
|
||||
fprintf(f,"Missing: %s\n", name);
|
||||
res++;
|
||||
}
|
||||
} else if(!GPIO_IS_VALID_OUTPUT_GPIO(t_gpio)){
|
||||
} else if((output && !GPIO_IS_VALID_OUTPUT_GPIO(t_gpio)) || (!GPIO_IS_VALID_GPIO(t_gpio))){
|
||||
fprintf(f,"Invalid %s gpio: [%d] %s\n",name, t_gpio, GPIO_IS_VALID_GPIO(t_gpio)?NOT_OUTPUT:NOT_GPIO );
|
||||
res++;
|
||||
}
|
||||
@@ -161,7 +175,9 @@ int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandato
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory){
|
||||
return is_gpio(gpio,f,gpio_out,mandatory,true);
|
||||
}
|
||||
int check_missing_parm(struct arg_int * int_parm, FILE * f){
|
||||
int res=0;
|
||||
const char * name = int_parm->hdr.longopts?int_parm->hdr.longopts:int_parm->hdr.glossary;
|
||||
@@ -290,6 +306,7 @@ static int do_bt_source_cmd(int argc, char **argv){
|
||||
}
|
||||
if(nerrors >0){
|
||||
arg_print_errors(f,bt_source_args.end,desc_bt_source);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -400,6 +417,7 @@ static int do_audio_cmd(int argc, char **argv){
|
||||
}
|
||||
if(nerrors >0){
|
||||
arg_print_errors(f,audio_args.end,desc_audio);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -463,6 +481,7 @@ static int do_spdif_cmd(int argc, char **argv){
|
||||
}
|
||||
if(nerrors >0){
|
||||
arg_print_errors(f,spdif_args.end,desc_dac);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
nerrors+=is_output_gpio(spdif_args.clock, f, &i2s_dac_pin.pin.bck_io_num, true);
|
||||
@@ -482,6 +501,61 @@ static int do_spdif_cmd(int argc, char **argv){
|
||||
return (nerrors==0 && err==ESP_OK)?0:1;
|
||||
}
|
||||
|
||||
static int do_rotary_cmd(int argc, char **argv){
|
||||
rotary_struct_t rotary={ .A = -1, .B = -1, .SW = -1, .longpress = 0, .knobonly=0,.volume_lock=false};
|
||||
esp_err_t err=ESP_OK;
|
||||
int nerrors = arg_parse(argc, argv,(void **)&rotary_args);
|
||||
if (rotary_args.clear->count) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING,"rotary config cleared\n");
|
||||
config_set_value(NVS_TYPE_STR, "rotary_config", "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.\n");
|
||||
return 1;
|
||||
}
|
||||
if(nerrors >0){
|
||||
arg_print_errors(f,rotary_args.end,desc_rotary);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
nerrors+=is_gpio(rotary_args.A, f, &rotary.A, true,false);
|
||||
nerrors+=is_gpio(rotary_args.B, f, &rotary.B, true,false);
|
||||
nerrors+=is_gpio(rotary_args.SW, f, &rotary.SW,false,false);
|
||||
|
||||
|
||||
if(rotary_args.knobonly->count>0 && (rotary_args.volume_lock->count>0 || rotary_args.longpress->count>0)){
|
||||
fprintf(f,"error: Cannot use volume lock or longpress option when knob only option selected\n");
|
||||
nerrors++;
|
||||
}
|
||||
if(rotary_args.timer->count>0 && rotary_args.timer->ival[0]<0){
|
||||
fprintf(f,"error: knob only timer should be greater than or equal to zero.\n");
|
||||
nerrors++;
|
||||
}
|
||||
else {
|
||||
rotary.timer = rotary_args.timer->count>0?rotary_args.timer->ival[0]:0;
|
||||
}
|
||||
rotary.knobonly = rotary_args.knobonly->count>0;
|
||||
rotary.volume_lock= rotary_args.volume_lock->count>0;
|
||||
rotary.longpress = rotary_args.longpress->count>0;
|
||||
if(!nerrors ){
|
||||
fprintf(f,"Storing rotary parameters.\n");
|
||||
nerrors+=(config_rotary_set(&rotary )!=ESP_OK);
|
||||
}
|
||||
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==0 && err==ESP_OK)?0:1;
|
||||
}
|
||||
|
||||
static int do_i2s_cmd(int argc, char **argv)
|
||||
{
|
||||
i2s_platform_config_t i2s_dac_pin = {
|
||||
@@ -514,6 +588,7 @@ static int do_i2s_cmd(int argc, char **argv)
|
||||
}
|
||||
if(nerrors >0){
|
||||
arg_print_errors(f,i2s_args.end,desc_dac);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
nerrors+=is_output_gpio(i2s_args.clock, f, &i2s_dac_pin.pin.bck_io_num, true);
|
||||
@@ -536,6 +611,7 @@ static int do_i2s_cmd(int argc, char **argv)
|
||||
}
|
||||
if(i2s_args.model_name->count>0 && strlen(i2s_args.model_name->sval[0])>0){
|
||||
strncpy(i2s_dac_pin.model,i2s_args.model_name->sval[0],sizeof(i2s_dac_pin.model));
|
||||
i2s_dac_pin.model[sizeof(i2s_dac_pin.model) - 1] = '\0';
|
||||
}
|
||||
if(!nerrors ){
|
||||
fprintf(f,"Storing i2s parameters.\n");
|
||||
@@ -624,6 +700,23 @@ cJSON * spdif_cb(){
|
||||
|
||||
return values;
|
||||
}
|
||||
cJSON * rotary_cb(){
|
||||
cJSON * values = cJSON_CreateObject();
|
||||
const rotary_struct_t *rotary= config_rotary_get();
|
||||
|
||||
if(GPIO_IS_VALID_GPIO(rotary->A ) && rotary->A>=0 && GPIO_IS_VALID_GPIO(rotary->B) && rotary->B>=0){
|
||||
cJSON_AddNumberToObject(values,"A",rotary->A);
|
||||
cJSON_AddNumberToObject(values,"B",rotary->B);
|
||||
if(GPIO_IS_VALID_GPIO(rotary->SW ) && rotary->SW>=0 ){
|
||||
cJSON_AddNumberToObject(values,"SW",rotary->SW);
|
||||
}
|
||||
cJSON_AddBoolToObject(values,"volume_lock",rotary->volume_lock);
|
||||
cJSON_AddBoolToObject(values,"longpress",rotary->longpress);
|
||||
cJSON_AddBoolToObject(values,"knobonly",rotary->knobonly);
|
||||
cJSON_AddNumberToObject(values,"timer",rotary->timer);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
cJSON * audio_cb(){
|
||||
cJSON * values = cJSON_CreateObject();
|
||||
char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
|
||||
@@ -718,6 +811,7 @@ cJSON * squeezelite_cb(){
|
||||
argv = (char **) calloc(22, sizeof(char *));
|
||||
if (argv == NULL) {
|
||||
FREE_AND_NULL(nvs_config);
|
||||
fclose(f);
|
||||
return values;
|
||||
}
|
||||
size_t argc = esp_console_split_argv(nvs_config, argv,22);
|
||||
@@ -774,7 +868,7 @@ static char * get_log_level_options(const char * longname){
|
||||
return options;
|
||||
}
|
||||
static void register_i2s_config(void){
|
||||
i2s_args.model_name = arg_str1(NULL,"model_name","TAS57xx|TAS5713|AC101|I2S","DAC Model Name");
|
||||
i2s_args.model_name = arg_str1(NULL,"model_name","TAS57xx|TAS5713|AC101|WM8978|I2S","DAC Model Name");
|
||||
i2s_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
|
||||
i2s_args.clock = arg_int1(NULL,"clock","<n>","Clock GPIO. e.g. 33");
|
||||
i2s_args.wordselect = arg_int1(NULL,"wordselect","<n>","Word Select GPIO. e.g. 25");
|
||||
@@ -815,6 +909,27 @@ static void register_bt_source_config(void){
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
}
|
||||
|
||||
static void register_rotary_config(void){
|
||||
rotary_args.rem = arg_rem("remark","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.\r\nEncoder is normally hard-coded to respectively knob left, right and push on LMS and to volume down/up/play toggle on BT and AirPlay.");
|
||||
rotary_args.A = arg_int1(NULL,"A","gpio","A/DT gpio");
|
||||
rotary_args.B = arg_int1(NULL,"B","gpio","B/CLK gpio");
|
||||
rotary_args.SW = arg_int0(NULL,"SW","gpio","Switch gpio");
|
||||
rotary_args.knobonly = arg_lit0(NULL,"knobonly","Single knob full navigation. Left, Right and Press is navigation, with Press always going to lower submenu item. Longpress is 'Play', Double press is 'Back', a quick left-right movement on the encoder is 'Pause'");
|
||||
rotary_args.timer = arg_int0(NULL,"timer","ms","The speed of double click (or left-right) when knob only option is enabled. Be aware that the longer you set double click speed, the less responsive the interface will be. ");
|
||||
rotary_args.volume_lock = arg_lit0(NULL,"volume_lock", "Force Volume down/up/play toggle all the time (even in LMS). ");
|
||||
rotary_args.longpress = arg_lit0(NULL,"longpress","Enable alternate mode mode on long-press. 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').");
|
||||
rotary_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
|
||||
rotary_args.end = arg_end(3);
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = CFG_TYPE_HW("rotary"),
|
||||
.help = desc_rotary,
|
||||
.hint = NULL,
|
||||
.func = &do_rotary_cmd,
|
||||
.argtable = &rotary_args
|
||||
};
|
||||
cmd_to_json_with_cb(&cmd,&rotary_cb);
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
}
|
||||
|
||||
static void register_audio_config(void){
|
||||
audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time.");
|
||||
@@ -828,7 +943,9 @@ static void register_audio_config(void){
|
||||
};
|
||||
cmd_to_json_with_cb(&cmd,&audio_cb);
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
}static void register_spdif_config(void){
|
||||
}
|
||||
|
||||
static void register_spdif_config(void){
|
||||
spdif_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
|
||||
spdif_args.clock = arg_int1(NULL,"clock","<n>","Clock GPIO. e.g. 33");
|
||||
spdif_args.wordselect = arg_int1(NULL,"wordselect","<n>","Word Select GPIO. e.g. 25");
|
||||
@@ -891,7 +1008,12 @@ void register_config_cmd(void){
|
||||
register_audio_config();
|
||||
// register_squeezelite_config();
|
||||
register_bt_source_config();
|
||||
if(!is_dac_config_locked()){
|
||||
register_i2s_config();
|
||||
}
|
||||
if(!is_spdif_config_locked()){
|
||||
register_spdif_config();
|
||||
}
|
||||
register_rotary_config();
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ static struct {
|
||||
|
||||
static struct {
|
||||
struct arg_int *data;
|
||||
struct arg_int *miso;
|
||||
struct arg_int *clk;
|
||||
struct arg_int *dc;
|
||||
struct arg_int *host;
|
||||
@@ -324,6 +325,7 @@ static int do_i2c_set_display(int argc, char **argv)
|
||||
}
|
||||
if(nerrors>0){
|
||||
arg_print_errors(f,i2cdisp_args.end,desc_display);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
/* Check "--type" option */
|
||||
@@ -446,11 +448,13 @@ static int do_spiconfig_cmd(int argc, char **argv){
|
||||
}
|
||||
if(nerrors>0){
|
||||
arg_print_errors(f,spiconfig_args.end,desc_spiconfig);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
/* Check "--clk" option */
|
||||
nerrors+=is_output_gpio(spiconfig_args.clk, f, &spi_config.sclk_io_num, true);
|
||||
nerrors+=is_output_gpio(spiconfig_args.data, f, &spi_config.mosi_io_num, true);
|
||||
nerrors+=is_output_gpio(spiconfig_args.miso, f, &spi_config.miso_io_num, true);
|
||||
nerrors+=is_output_gpio(spiconfig_args.dc, f, &dc, true);
|
||||
nerrors+=is_output_gpio(spiconfig_args.host, f, &host, true);
|
||||
|
||||
@@ -520,6 +524,7 @@ static int do_i2cconfig_cmd(int argc, char **argv)
|
||||
}
|
||||
if(nerrors>0){
|
||||
arg_print_errors(f,i2cconfig_args.end,desc_i2c);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
/* Check "--port" option */
|
||||
@@ -906,7 +911,7 @@ static void register_i2c_set_display(){
|
||||
i2cdisp_args.cs = arg_int0("b", "cs", "<n>","SPI Only. CS GPIO (for SPI displays)");
|
||||
i2cdisp_args.speed = arg_int0("s", "speed", "<n>","SPI Only. Bus Speed (Default 8000000). SPI interface can work up to 26MHz~40MHz");
|
||||
i2cdisp_args.back = arg_int0("b", "back", "<n>","Backlight GPIO (if applicable)");
|
||||
i2cdisp_args.depth = arg_int0("p", "depth", "1|4", "Bit Depth (only for SSD1326 displays)");
|
||||
i2cdisp_args.depth = arg_int0("p", "depth", "-1|1|4", "Bit Depth (only for SSD1326 displays)");
|
||||
i2cdisp_args.type = arg_str0("t", "type", "<I2C|SPI>", "Interface (default I2C)");
|
||||
i2cdisp_args.rotate = arg_lit0("r", "rotate", "Rotate 180 degrees");
|
||||
i2cdisp_args.clear = arg_lit0(NULL, "clear", "clear configuration and return");
|
||||
@@ -1027,7 +1032,8 @@ static void register_spiconfig(void)
|
||||
{
|
||||
spiconfig_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
|
||||
spiconfig_args.clk = arg_int0("k", "clk", "<n>", "Clock GPIO");
|
||||
spiconfig_args.data = arg_int0("d","data", "<n>","Data GPIO");
|
||||
spiconfig_args.data = arg_int0("d","data", "<n>","Data OUT GPIO");
|
||||
spiconfig_args.miso = arg_int0("d","miso", "<n>","Data IN GPIO");
|
||||
spiconfig_args.dc = arg_int0("c","dc", "<n>", "DC GPIO");
|
||||
spiconfig_args.host= arg_int0("h", "host", "1|2", "SPI Host Number");
|
||||
spiconfig_args.end = arg_end(4);
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#endif
|
||||
static struct {
|
||||
struct arg_str *scanmode;
|
||||
//disable_ps struct arg_lit *disable_power_save;
|
||||
struct arg_end *end;
|
||||
} wifi_parms_arg;
|
||||
static struct {
|
||||
@@ -387,6 +386,7 @@ int set_squeezelite_player_name(FILE * f,const char * name){
|
||||
|
||||
FREE_AND_NULL(nvs_config);
|
||||
FREE_AND_NULL(argv);
|
||||
free(cleaned_name);
|
||||
return nerrors;
|
||||
|
||||
}
|
||||
@@ -609,7 +609,6 @@ static int do_configure_wifi(int argc, char **argv){
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
|
||||
return 1;
|
||||
}
|
||||
// disable_ps nerrors += enable_disable(f,"disable_ps",wifi_parms_arg.disable_power_save);
|
||||
|
||||
if(wifi_parms_arg.scanmode->count>0){
|
||||
if(strcasecmp(wifi_parms_arg.scanmode->sval[0],"Comprehensive") == 0){
|
||||
@@ -689,11 +688,7 @@ static int do_set_services(int argc, char **argv)
|
||||
cJSON * configure_wifi_cb(){
|
||||
cJSON * values = cJSON_CreateObject();
|
||||
char * p=NULL;
|
||||
// disable_ps
|
||||
// if ((p = config_alloc_get(NVS_TYPE_STR, "disable_ps")) != NULL) {
|
||||
// cJSON_AddBoolToObject(values,"disable_power_save",strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
|
||||
// FREE_AND_NULL(p);
|
||||
// }
|
||||
|
||||
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);
|
||||
@@ -756,7 +751,6 @@ static void register_set_services(){
|
||||
|
||||
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.");
|
||||
//disable_ps wifi_parms_arg.disable_power_save = arg_lit0(NULL, "disable_power_save", "Disable Power Saving. This may help if the wifi connection is unstable.");
|
||||
wifi_parms_arg.end=arg_end(2);
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = CFG_TYPE_SYST("wifi"),
|
||||
|
||||
@@ -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) );
|
||||
|
||||
@@ -97,7 +97,8 @@ 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);
|
||||
|
||||
@@ -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)) );
|
||||
@@ -518,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;
|
||||
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
|
||||
@@ -118,7 +118,7 @@ static void set_i2s_pin(char *config, i2s_pin_config_t *pin_config) {
|
||||
* Get i2s config structure from config string
|
||||
*/
|
||||
const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){
|
||||
static i2s_platform_config_t i2s_dac_pin = {
|
||||
static EXT_RAM_ATTR i2s_platform_config_t i2s_dac_pin = {
|
||||
.i2c_addr = -1,
|
||||
.sda= -1,
|
||||
.scl = -1,
|
||||
@@ -142,12 +142,39 @@ const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){
|
||||
return &i2s_dac_pin;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Get eth config structure from config string
|
||||
*/
|
||||
const eth_config_t * config_get_eth_from_str(char * eth_config ){
|
||||
static EXT_RAM_ATTR eth_config_t eth_pin = {
|
||||
.rmii = false,
|
||||
.model = "",
|
||||
};
|
||||
char * p=NULL;
|
||||
|
||||
if ((p = strcasestr(eth_config, "model")) != NULL) sscanf(p, "%*[^=]=%15[^,]", eth_pin.model);
|
||||
if ((p = strcasestr(eth_config, "mdc")) != NULL) eth_pin.mdc = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "mdio")) != NULL) eth_pin.mdio = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "rst")) != NULL) eth_pin.rst = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "mosi")) != NULL) eth_pin.mosi = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "miso")) != NULL) eth_pin.miso = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "intr")) != NULL) eth_pin.intr = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "cs")) != NULL) eth_pin.cs = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "speed")) != NULL) eth_pin.speed = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "clk")) != NULL) eth_pin.clk = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(eth_config, "host")) != NULL) eth_pin.host = atoi(strchr(p, '=') + 1);
|
||||
|
||||
if (strcasestr(eth_pin.model, "lan8720")) eth_pin.rmii = true;
|
||||
|
||||
return ð_pin;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Get spdif config structure
|
||||
*/
|
||||
const i2s_platform_config_t * config_spdif_get( ){
|
||||
char * spdif_config = config_spdif_get_string();
|
||||
static i2s_platform_config_t i2s_dac_config;
|
||||
static EXT_RAM_ATTR i2s_platform_config_t i2s_dac_config;
|
||||
memcpy(&i2s_dac_config, config_get_i2s_from_str(spdif_config), sizeof(i2s_dac_config));
|
||||
free(spdif_config);
|
||||
return &i2s_dac_config;
|
||||
@@ -158,12 +185,34 @@ const i2s_platform_config_t * config_spdif_get( ){
|
||||
*/
|
||||
const i2s_platform_config_t * config_dac_get(){
|
||||
char * spdif_config = get_dac_config_string();
|
||||
static i2s_platform_config_t i2s_dac_config;
|
||||
static EXT_RAM_ATTR i2s_platform_config_t i2s_dac_config;
|
||||
memcpy(&i2s_dac_config, config_get_i2s_from_str(spdif_config), sizeof(i2s_dac_config));
|
||||
free(spdif_config);
|
||||
return &i2s_dac_config;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Get ethernet config structure
|
||||
*/
|
||||
const eth_config_t * config_eth_get( ){
|
||||
char * config = config_alloc_get_str("eth_config", CONFIG_ETH_CONFIG, "rst=" STR(CONFIG_ETH_PHY_RST_IO)
|
||||
#if defined(CONFIG_ETH_LAN8720)
|
||||
",model=lan8720"
|
||||
#elif defined(CONFIG_ETH_DM9051)
|
||||
",model=dm9051"
|
||||
#endif
|
||||
",mdc=" STR(CONFIG_ETH_MDC_IO) ",mdio=" STR(CONFIG_ETH_MDIO_IO)
|
||||
",host=" STR(CONFIG_ETH_SPI_HOST) ",cs=" STR(CONFIG_ETH_SPI_CS_IO)
|
||||
",mosi=" STR(CONFIG_ETH_SPI_MOSI_IO) ",miso=" STR(CONFIG_ETH_SPI_MISO_IO)
|
||||
",intr=" STR(CONFIG_ETH_SPI_INTR_IO)
|
||||
",clk=" STR(CONFIG_ETH_SPI_CLK_IO) ",speed=" STR(CONFIG_ETH_SPI_SPEED) );
|
||||
static EXT_RAM_ATTR eth_config_t eth_config;
|
||||
ESP_LOGD(TAG, "Ethernet config string %s", config);
|
||||
memcpy(ð_config, config_get_eth_from_str(config), sizeof(eth_config));
|
||||
free(config);
|
||||
return ð_config;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
@@ -183,6 +232,47 @@ esp_err_t config_i2c_set(const i2c_config_t * config, int port){
|
||||
return err;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
esp_err_t config_rotary_set(rotary_struct_t * config){
|
||||
int buffer_size=512;
|
||||
esp_err_t err=ESP_OK;
|
||||
char * config_buffer=calloc(buffer_size,1);
|
||||
char * config_buffer2=calloc(buffer_size,1);
|
||||
if(config_buffer && config_buffer2) {
|
||||
snprintf(config_buffer,buffer_size,"A=%i,B=%i",config->A, config->B);
|
||||
if(config->SW >=0 ){
|
||||
snprintf(config_buffer2,buffer_size,"%s,SW=%i",config_buffer,config->SW);
|
||||
strcpy(config_buffer,config_buffer2);
|
||||
}
|
||||
if(config->knobonly){
|
||||
strncat(config_buffer,",knobonly",buffer_size);
|
||||
if(config->timer>0){
|
||||
snprintf(config_buffer2,buffer_size,"%s=%i",config_buffer,config->timer);
|
||||
strcpy(config_buffer,config_buffer2);
|
||||
}
|
||||
}
|
||||
if(config->volume_lock){
|
||||
strncat(config_buffer,",volume",buffer_size);
|
||||
}
|
||||
if(config->longpress){
|
||||
strncat(config_buffer,",longpress",buffer_size);
|
||||
}
|
||||
log_send_messaging(MESSAGING_INFO,"Updating rotary configuration to %s",config_buffer);
|
||||
err = config_set_value(NVS_TYPE_STR, "rotary_config", config_buffer);
|
||||
if(err!=ESP_OK){
|
||||
log_send_messaging(MESSAGING_ERROR,"Error: %s",esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = ESP_ERR_NO_MEM;
|
||||
}
|
||||
FREE_AND_NULL(config_buffer);
|
||||
FREE_AND_NULL(config_buffer2);
|
||||
return err;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
@@ -192,7 +282,7 @@ esp_err_t config_display_set(const display_config_t * config){
|
||||
char * config_buffer=calloc(buffer_size,1);
|
||||
char * config_buffer2=calloc(buffer_size,1);
|
||||
if(config_buffer && config_buffer2) {
|
||||
snprintf(config_buffer,buffer_size,"%s:width=%i,height=%i",config->type,config->width,config->height);
|
||||
snprintf(config_buffer,buffer_size,"%s,width=%i,height=%i",config->type,config->width,config->height);
|
||||
if(strcasecmp("I2C",config->type)==0){
|
||||
if(config->address>0 ){
|
||||
snprintf(config_buffer2,buffer_size,"%s,address=%i",config_buffer,config->address);
|
||||
@@ -301,7 +391,7 @@ esp_err_t config_spi_set(const spi_bus_config_t * config, int host, int dc){
|
||||
esp_err_t err = ESP_OK;
|
||||
char * config_buffer=calloc(buffer_size,1);
|
||||
if(config_buffer) {
|
||||
snprintf(config_buffer,buffer_size,"data=%u,clk=%u,dc=%u,host=%u",config->mosi_io_num,config->sclk_io_num,dc,host);
|
||||
snprintf(config_buffer,buffer_size,"data=%u,clk=%u,dc=%u,host=%u,miso=%d",config->mosi_io_num,config->sclk_io_num,dc,host,config->miso_io_num);
|
||||
log_send_messaging(MESSAGING_INFO,"Updating SPI configuration to %s",config_buffer);
|
||||
err = config_set_value(NVS_TYPE_STR, "spi_config", config_buffer);
|
||||
if(err!=ESP_OK){
|
||||
@@ -471,7 +561,7 @@ const set_GPIO_struct_t * get_gpio_struct(){
|
||||
*/
|
||||
const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host) {
|
||||
char *nvs_item, *p;
|
||||
static spi_bus_config_t spi = {
|
||||
static EXT_RAM_ATTR spi_bus_config_t spi = {
|
||||
.mosi_io_num = -1,
|
||||
.sclk_io_num = -1,
|
||||
.miso_io_num = -1,
|
||||
@@ -482,6 +572,8 @@ const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host) {
|
||||
nvs_item = config_alloc_get_str("spi_config", CONFIG_SPI_CONFIG, NULL);
|
||||
if (nvs_item) {
|
||||
if ((p = strcasestr(nvs_item, "data")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(nvs_item, "mosi")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(nvs_item, "miso")) != NULL) spi.miso_io_num = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(nvs_item, "clk")) != NULL) spi.sclk_io_num = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(nvs_item, "dc")) != NULL) spi_system_dc_gpio = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(nvs_item, "host")) != NULL) spi_system_host = atoi(strchr(p, '=') + 1);
|
||||
@@ -510,6 +602,36 @@ void parse_set_GPIO(void (*cb)(int gpio, char *value)) {
|
||||
free(nvs_item);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
const rotary_struct_t * config_rotary_get() {
|
||||
|
||||
static rotary_struct_t rotary={ .A = -1, .B = -1, .SW = -1, .longpress = false, .knobonly=false,.timer=0,.volume_lock=false};
|
||||
char *config = config_alloc_get_default(NVS_TYPE_STR, "rotary_config", NULL, 0);
|
||||
if (config && *config) {
|
||||
char *p;
|
||||
|
||||
// parse config
|
||||
if ((p = strcasestr(config, "A")) != NULL) rotary.A = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(config, "B")) != NULL) rotary.B = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(config, "SW")) != NULL) rotary.SW = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(config, "knobonly")) != NULL) {
|
||||
p = strchr(p, '=');
|
||||
rotary.knobonly = true;
|
||||
rotary.timer = p ? atoi(p + 1) : 350;
|
||||
rotary.longpress = false;
|
||||
} else {
|
||||
rotary.knobonly = false;
|
||||
rotary.timer = 0;
|
||||
if ((p = strcasestr(config, "volume")) != NULL) rotary.volume_lock = true;
|
||||
if ((p = strcasestr(config, "longpress")) != NULL) rotary.longpress = true;
|
||||
}
|
||||
free(config);
|
||||
}
|
||||
return &rotary;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
@@ -522,6 +644,17 @@ cJSON * get_gpio_entry(const char * name, const char * prefix, int gpio, bool fi
|
||||
return entry;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
cJSON * add_gpio_for_value(cJSON * list,const char * name,int gpio, const char * prefix, bool fixed){
|
||||
cJSON * llist = list?list:cJSON_CreateArray();
|
||||
if(GPIO_IS_VALID_GPIO(gpio) && gpio>0){
|
||||
cJSON_AddItemToArray(llist,get_gpio_entry(name,prefix,gpio,fixed));
|
||||
}
|
||||
return llist;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
@@ -657,13 +790,11 @@ cJSON * get_SPDIF_GPIO(cJSON * list, bool fixed){
|
||||
*/
|
||||
cJSON * get_Rotary_GPIO(cJSON * list){
|
||||
cJSON * llist = list?list:cJSON_CreateArray();
|
||||
char *config = config_alloc_get_default(NVS_TYPE_STR, "rotary_config", NULL, 0);
|
||||
if(config){
|
||||
llist = add_gpio_for_name(llist,config,"A", "rotary", false);
|
||||
llist = add_gpio_for_name(llist,config,"B", "rotary", false);
|
||||
llist = add_gpio_for_name(llist,config,"SW", "rotary", false);
|
||||
free(config);
|
||||
}
|
||||
|
||||
const rotary_struct_t *rotary= config_rotary_get();
|
||||
add_gpio_for_value(llist,"A",rotary->A, "rotary", false);
|
||||
add_gpio_for_value(llist,"B",rotary->B, "rotary", false);
|
||||
add_gpio_for_value(llist,"SW",rotary->SW, "rotary", false);
|
||||
return llist;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,16 @@ typedef struct {
|
||||
bool rotate;
|
||||
} display_config_t;
|
||||
|
||||
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];
|
||||
@@ -57,6 +67,16 @@ typedef struct {
|
||||
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;
|
||||
char * name;
|
||||
@@ -65,6 +85,7 @@ typedef struct {
|
||||
} 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);
|
||||
@@ -82,3 +103,5 @@ gpio_entry_t * get_gpio_by_no(int gpionum, bool refresh);
|
||||
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);
|
||||
@@ -55,6 +55,7 @@ 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_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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,7 +940,6 @@ 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_right_first = 0;
|
||||
I2S[i2s_num]->conf.rx_slave_mod = 0; // Master
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -21,7 +21,7 @@ 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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,4 +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
|
||||
@@ -29,10 +29,16 @@ set_source_files_properties(flac.c
|
||||
-Wno-maybe-uninitialized
|
||||
)
|
||||
|
||||
add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DCUSTOM_VERSION=${BUILD_NUMBER})
|
||||
|
||||
if (${DEPTH} EQUAL "32")
|
||||
add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=8)
|
||||
add_definitions(-DBYTES_PER_FRAME=8)
|
||||
else()
|
||||
add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4)
|
||||
add_definitions(-DRESAMPLE16 -DBYTES_PER_FRAME=4)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED AAC_DISABLED_SBR)
|
||||
add_definitions(-DAAC_ENABLE_SBR)
|
||||
endif()
|
||||
|
||||
add_compile_options (-O3 )
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
172
components/squeezelite/adac_core.c
Normal file
172
components/squeezelite/adac_core.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -166,6 +166,26 @@ const actrls_t LMS_controls = {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
@@ -176,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);
|
||||
}
|
||||
|
||||
@@ -188,26 +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);
|
||||
closesocket(cli_sock);
|
||||
cli_sock = -1;
|
||||
}
|
||||
connect_cli_socket();
|
||||
|
||||
LOG_INFO("notified server %s hport %hu cport %hu", inet_ntoa(ip), hport, cport);
|
||||
|
||||
|
||||
@@ -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;
|
||||
int wake;
|
||||
bool owned;
|
||||
struct {
|
||||
SemaphoreHandle_t mutex;
|
||||
int width, height;
|
||||
bool dirty;
|
||||
bool owned;
|
||||
};
|
||||
} 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,19 +303,17 @@ 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);
|
||||
@@ -306,14 +326,7 @@ bool sb_display_init(void) {
|
||||
|
||||
// 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);
|
||||
|
||||
// create scroll 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);
|
||||
@@ -321,6 +334,26 @@ bool sb_display_init(void) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (led_display) {
|
||||
// PLACEHOLDER to init config
|
||||
led_visu.mode = VISU_VUMETER;
|
||||
}
|
||||
|
||||
// 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, "squeeze_displayer", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
|
||||
|
||||
// chain handlers
|
||||
slimp_handler_chain = slimp_handler;
|
||||
slimp_handler = 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,7 @@ 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
|
||||
@@ -825,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);
|
||||
}
|
||||
@@ -843,66 +884,17 @@ 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;
|
||||
}
|
||||
|
||||
int mode = visu.mode & ~VISU_ESP32;
|
||||
|
||||
// not enough frames
|
||||
if (visu_export.level < (mode == VISU_VUMETER ? RMS_LEN : FFT_LEN) && 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 = (s16_t*) visu_export.buffer + (BYTES_PER_FRAME / 4) - 1;
|
||||
|
||||
// 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 += BYTES_PER_FRAME / 4;
|
||||
visu.bars[1].current += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
|
||||
iptr += BYTES_PER_FRAME / 4;
|
||||
}
|
||||
|
||||
// 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 ? 8 : 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 {
|
||||
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
|
||||
visu.samples[i * 2 + 0] = (float) (*iptr + *(iptr+BYTES_PER_FRAME/4)) * visu.hanning[i];
|
||||
visu.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(visu.samples, FFT_LEN);
|
||||
dsps_bit_rev_fc32_ansi(visu.samples, FFT_LEN);
|
||||
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 < visu.n && j < (FFT_LEN / 2); i++) {
|
||||
for (int i = 0, j = 1; i < 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];
|
||||
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)) {
|
||||
@@ -910,28 +902,39 @@ static void visu_update(void) {
|
||||
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;
|
||||
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 = (visu.samples[2*j] * visu.samples[2*j] + visu.samples[2*j+1] * visu.samples[2*j+1]) / 2.;
|
||||
power = (samples[2*j] * samples[2*j] + samples[2*j+1] * 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 ? 256 : 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;
|
||||
}
|
||||
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);
|
||||
@@ -942,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;
|
||||
@@ -988,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
|
||||
@@ -1129,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;
|
||||
@@ -1144,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
|
||||
@@ -1156,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)
|
||||
@@ -1200,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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
custom_player_id = sb_display_init() ? 100 : 101;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ 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 times the 5s search for a server will happen before slimproto exits (0 = no limit)
|
||||
#define MAX_SERVER_RETRIES 5
|
||||
@@ -77,7 +77,10 @@ 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 {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "platform_config.h"
|
||||
#include "squeezelite.h"
|
||||
#include "equalizer.h"
|
||||
#include "esp_equalizer.h"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
125
components/squeezelite/external/dac_external.c
vendored
125
components/squeezelite/external/dac_external.c
vendored
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -425,7 +442,7 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,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");
|
||||
|
||||
@@ -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,10 +255,13 @@ frames_t _output_frames(frames_t avail) {
|
||||
|
||||
out_frames = !silence ? min(size, cont_frames) : size;
|
||||
|
||||
if (output.channels & 0x01) gainR |= MONO_FLAG;
|
||||
if (output.channels & 0x02) gainL |= MONO_FLAG;
|
||||
IF_DSD(
|
||||
if (output.outfmt != PCM) {
|
||||
flags = 0;
|
||||
}
|
||||
)
|
||||
|
||||
wrote = output.write_cb(out_frames, silence, gainL, gainR, cross_gain_in, cross_gain_out, &cross_ptr);
|
||||
wrote = output.write_cb(out_frames, silence, gainL, gainR, flags, cross_gain_in, cross_gain_out, &cross_ptr);
|
||||
|
||||
if (wrote <= 0) {
|
||||
frames -= size;
|
||||
|
||||
@@ -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,7 +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);
|
||||
}
|
||||
|
||||
_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);
|
||||
@@ -112,7 +112,7 @@ 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(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, ((gainL & ~MONO_FLAG) + (gainR & ~MONO_FLAG)) / 2);
|
||||
output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
|
||||
|
||||
oframes += out_frames;
|
||||
|
||||
|
||||
@@ -73,11 +73,15 @@ 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 ") || !strcasecmp(device, "BT")) {
|
||||
LOG_INFO("init Bluetooth");
|
||||
close_cb = &output_close_bt;
|
||||
@@ -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;
|
||||
|
||||
@@ -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,20 +403,24 @@ 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 (!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);
|
||||
}
|
||||
|
||||
_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 {
|
||||
memcpy(obuf + oframes * BYTES_PER_FRAME, silencebuf, out_frames * BYTES_PER_FRAME);
|
||||
}
|
||||
|
||||
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, ((gainL & ~MONO_FLAG) + (gainR & ~MONO_FLAG)) / 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;
|
||||
|
||||
return out_frames;
|
||||
@@ -420,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) {
|
||||
|
||||
@@ -464,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;
|
||||
@@ -524,12 +527,13 @@ 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
|
||||
@@ -538,10 +542,17 @@ static void *output_thread_i2s(void *arg) {
|
||||
#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 /= 16 / BYTES_PER_FRAME;
|
||||
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);
|
||||
@@ -560,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);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
@@ -608,65 +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;
|
||||
register size_t cnt = *count;
|
||||
|
||||
// frames are 2 channels of 16/32 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
|
||||
// invert if last preceeding bit is 1
|
||||
lo ^= ~((s16_t)hi) >> 16;
|
||||
|
||||
// first 16 bits
|
||||
*(dst+0) = ((u32_t)lo << 16) | hi;
|
||||
|
||||
// 4 bits auxillary-audio-databits, the first used as parity
|
||||
#if BYTES_PER_FRAME == 4
|
||||
aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);
|
||||
#else
|
||||
// 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 > 383) {
|
||||
*(dst+1) = VUCP | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
|
||||
cnt = 0;
|
||||
} else {
|
||||
*(dst+1) = VUCP | (((cnt & 0x01) ? PREAMBLE_W : PREAMBLE_M) << 16) | aux;
|
||||
}
|
||||
|
||||
src++;
|
||||
dst += 2;
|
||||
}
|
||||
|
||||
*count = cnt;
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -701,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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,29 +43,26 @@ 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 ((gainR & MONO_FLAG) && (gainL & MONO_FLAG)) {
|
||||
if ((flags & MONO_LEFT) && (flags & MONO_RIGHT)) {
|
||||
s32_t *ptr = inputptr;
|
||||
frames_t count = cnt;
|
||||
gainL &= ~MONO_FLAG; gainR &= ~MONO_FLAG;
|
||||
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 (gainL & MONO_FLAG) {
|
||||
} else if (flags & MONO_RIGHT) {
|
||||
s32_t *ptr = inputptr + 1;
|
||||
frames_t count = cnt;
|
||||
gainL &= ~MONO_FLAG;
|
||||
while (count--) {
|
||||
*(ptr - 1) = *ptr;
|
||||
ptr += 2;
|
||||
}
|
||||
} else if (gainR & MONO_FLAG) {
|
||||
} else if (flags & MONO_LEFT) {
|
||||
s32_t *ptr = inputptr;
|
||||
frames_t count = cnt;
|
||||
gainR &= ~MONO_FLAG;
|
||||
while (count--) {
|
||||
*(ptr + 1) = *ptr;
|
||||
ptr += 2;
|
||||
@@ -383,24 +380,24 @@ 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) {
|
||||
if (gainL == FIXED_ONE && gainR == FIXED_ONE) {
|
||||
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;
|
||||
} if ((gainR & MONO_FLAG) && (gainL & MONO_FLAG)) {
|
||||
} 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;
|
||||
gainL &= ~MONO_FLAG; gainR &= ~MONO_FLAG;
|
||||
while (count--) {
|
||||
*ptrL = *ptrR = (gain(gainL, *ptrL) + gain(gainR, *ptrR)) / 2;
|
||||
ptrL += 2; ptrR += 2;
|
||||
}
|
||||
} else if (gainL & MONO_FLAG) {
|
||||
|
||||
} 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 (gainR & MONO_FLAG) {
|
||||
} else if (flags & MONO_LEFT) {
|
||||
ISAMPLE_T *ptr = (ISAMPLE_T *)(void *)outputbuf->readp;
|
||||
while (count--) {
|
||||
*(ptr + 1) = *ptr = gain(gainL, *ptr);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#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;
|
||||
@@ -37,7 +37,7 @@ void output_visu_export(void *frames, frames_t out_frames, u32_t rate, bool sile
|
||||
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;
|
||||
|
||||
@@ -480,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");
|
||||
|
||||
@@ -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)
|
||||
@@ -472,7 +472,6 @@ void _wake_create(event_event*);
|
||||
#define MAX_SILENCE_FRAMES 2048
|
||||
|
||||
#define FIXED_ONE 0x10000
|
||||
#define MONO_FLAG 0x20000
|
||||
|
||||
#ifndef BYTES_PER_FRAME
|
||||
#define BYTES_PER_FRAME 8
|
||||
@@ -655,6 +654,8 @@ 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 }
|
||||
|
||||
@@ -675,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
|
||||
@@ -758,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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
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];
|
||||
}
|
||||
|
||||
98
components/squeezelite/wm8978/wm8978.c
Normal file
98
components/squeezelite/wm8978/wm8978.c
Normal 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];
|
||||
}
|
||||
@@ -148,7 +148,7 @@ void start_telnet(void * pvParameter){
|
||||
StackType_t *xStack = heap_caps_malloc(TELNET_STACK_SIZE,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT));
|
||||
|
||||
if(!isStarted && bIsEnabled) {
|
||||
xTaskCreateStatic( (TaskFunction_t) &telnet_task, "telnet", TELNET_STACK_SIZE, NULL, ESP_TASK_MAIN_PRIO , xStack, xTaskBuffer);
|
||||
xTaskCreateStatic( (TaskFunction_t) &telnet_task, "telnet", TELNET_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN, xStack, xTaskBuffer);
|
||||
isStarted=true;
|
||||
}
|
||||
}
|
||||
@@ -240,12 +240,7 @@ void process_received_data(const char * buffer, size_t size){
|
||||
const char * c=buffer;
|
||||
|
||||
// scrub from any escape command
|
||||
if(*c == '\e'){
|
||||
while(*(c++) !='n'){
|
||||
--size;
|
||||
};
|
||||
--size;
|
||||
}
|
||||
if(*c == '\e') while (size && size-- && *c++ != '\n');
|
||||
memcpy(command,c,size);
|
||||
command[size]='\0';
|
||||
if(command[0]!='\r' && command[0]!='\n'){
|
||||
|
||||
@@ -33,3 +33,4 @@ extern void console_start();
|
||||
extern pthread_cond_t wifi_connect_suspend_cond;
|
||||
extern pthread_t wifi_connect_suspend_mutex;
|
||||
|
||||
extern void (*server_notify)(in_addr_t ip, uint16_t hport, uint16_t cport);
|
||||
@@ -76,24 +76,7 @@ static esp_err_t _httpd_server_init(struct httpd_data *hd)
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int ctrl_fd = cs_create_ctrl_sock(hd->config.ctrl_port);
|
||||
if (ctrl_fd < 0) {
|
||||
ESP_LOGE(TAG, LOG_FMT("error in creating ctrl socket (%d)"), errno);
|
||||
close(fd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int msg_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (msg_fd < 0) {
|
||||
ESP_LOGE(TAG, LOG_FMT("error in creating msg socket (%d)"), errno);
|
||||
close(fd);
|
||||
close(ctrl_fd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
hd->listen_fd = fd;
|
||||
hd->ctrl_fd = ctrl_fd;
|
||||
hd->msg_fd = msg_fd;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -198,14 +181,6 @@ static esp_err_t _httpd_server(struct httpd_data *hd)
|
||||
}
|
||||
|
||||
/* Case0: Do we have a control message? */
|
||||
if (FD_ISSET(hd->ctrl_fd, &read_set)) {
|
||||
ESP_LOGD(TAG, LOG_FMT("processing ctrl message"));
|
||||
_httpd_process_ctrl_msg(hd);
|
||||
if (hd->hd_td.status == THREAD_STOPPING) {
|
||||
ESP_LOGD(TAG, LOG_FMT("stopping thread"));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Case1: Do we have any activity on the current data
|
||||
* sessions? */
|
||||
@@ -259,8 +234,6 @@ static void _httpd_thread(void *arg)
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, LOG_FMT("web server exiting"));
|
||||
close(hd->msg_fd);
|
||||
cs_free_ctrl_sock(hd->ctrl_fd);
|
||||
_httpd_close_all_sessions(hd);
|
||||
close(hd->listen_fd);
|
||||
hd->hd_td.status = THREAD_STOPPED;
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\test.js":"1","C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\custom.js":"2"},{"size":4775,"mtime":1608244817341,"results":"3","hashOfConfig":"4"},{"size":52524,"mtime":1608525668984,"results":"5","hashOfConfig":"4"},{"filePath":"6","messages":"7","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"8"},"1lj4yrw",{"filePath":"9","messages":"10","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\test.js",[],[],"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\custom.js",[]]
|
||||
[{"/Users/mh/SynologyDrive/git/squeezelite-esp32/components/wifi-manager/webapp/src/js/custom.js":"1"},{"size":59815,"mtime":1618633783112,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"15w6qa4","/Users/mh/SynologyDrive/git/squeezelite-esp32/components/wifi-manager/webapp/src/js/custom.js",[]]
|
||||
@@ -1,14 +1,6 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
// {
|
||||
// "label": "build",
|
||||
// "command": "webpack --config webpack/webpack.prod.js",
|
||||
// "type": "shell",
|
||||
// "group": { "kind": "build", "isDefault": true },
|
||||
// "isBackground": true
|
||||
// },
|
||||
|
||||
{
|
||||
"type": "npm",
|
||||
"label": "webpack: dev server",
|
||||
@@ -37,6 +29,35 @@
|
||||
"endsPattern": "Compiled successfully\\."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"label": "webpack: build",
|
||||
"script": "build",
|
||||
"promptOnClose": true,
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "webpack",
|
||||
"severity": "error",
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "ERROR in (.*)",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "\\((\\d+),(\\d+)\\):(.*)",
|
||||
"line": 1,
|
||||
"column": 2,
|
||||
"message": 3
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": "Compiling\\.\\.\\.",
|
||||
"endsPattern": "Compiled successfully\\."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"help": "DAC Options",
|
||||
"hascb": true,
|
||||
"argtable": [{
|
||||
"datatype": "TAS57xx|TAS5713|AC101|I2S",
|
||||
"datatype": "TAS57xx|TAS5713|AC101|WM8978|I2S",
|
||||
"glossary": "DAC Model Name",
|
||||
"longopts": "model_name",
|
||||
"checkbox": false,
|
||||
@@ -202,7 +202,7 @@
|
||||
"mincount": 0,
|
||||
"maxcount": 1
|
||||
}],
|
||||
"hint": " --model_name=TAS57xx|TAS5713|AC101|I2S --clock=<n> --wordselect=<n> --data=<n> [--mute_gpio=<n>] [--mute_level] [--dac_sda=<n>] [--dac_scl=<n>] [--dac_i2c=<n>] [--clear]",
|
||||
"hint": " --model_name=TAS57xx|TAS5713|AC101|WM8978|I2S --clock=<n> --wordselect=<n> --data=<n> [--mute_gpio=<n>] [--mute_level] [--dac_sda=<n>] [--dac_scl=<n>] [--dac_i2c=<n>] [--clear]",
|
||||
"name": "cfg-hw-dac"
|
||||
}, {
|
||||
"help": "SPDIF Options",
|
||||
|
||||
@@ -338,6 +338,11 @@
|
||||
"type": 33,
|
||||
"value": "D",
|
||||
"chg": false
|
||||
},
|
||||
"rel_api": {
|
||||
"type": 33,
|
||||
"value": "https://api.github.com/repos/sle118/squeezelite-esp32/releases",
|
||||
"chg": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,14 @@
|
||||
"sent_time": 6245,
|
||||
"current_time": 6364
|
||||
}, {
|
||||
"message": "[{\n\t\t\"name\":\t\"SMSL BT4.2\",\n\t\t\"rssi\":\t-129\n\t}]",
|
||||
"message": "[{\n\t\t\"name\":\t\"BOSE BT\",\n\t\t\"rssi\":\t-129\n\t}]",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_BT",
|
||||
"sent_time": 6259,
|
||||
"current_time": 6364
|
||||
}
|
||||
, {
|
||||
"message": "[{\n\t\t\"name\":\t\"BOSE2 BT\",\n\t\t\"rssi\":\t-50\n\t}]",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_BT",
|
||||
"sent_time": 6259,
|
||||
|
||||
123
components/wifi-manager/webapp/mock/messages_ota.json
Normal file
123
components/wifi-manager/webapp/mock/messages_ota.json
Normal file
@@ -0,0 +1,123 @@
|
||||
[
|
||||
{
|
||||
"message": "Save Success",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_SYSTEM",
|
||||
"sent_time": 14084,
|
||||
"current_time": 16958
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Initializing...\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 14110,
|
||||
"current_time": 16958
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Starting OTA...\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 14146,
|
||||
"current_time": 16958
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Downloading firmware\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 15208,
|
||||
"current_time": 16958
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Download success\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 18825,
|
||||
"current_time": 22219
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"New version is : mock version \",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 18849,
|
||||
"current_time": 22220
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Formatting OTA partition\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 18850,
|
||||
"current_time": 22220
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Erasing flash (1/11)\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 22217,
|
||||
"current_time": 22220
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Erasing flash (5/11)\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 29896,
|
||||
"current_time": 29902
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Erasing flash (7/11)\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 33395,
|
||||
"current_time": 33408
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Erasing flash (9/11)\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 36843,
|
||||
"current_time": 54597
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Erasing flash complete.\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 39463,
|
||||
"current_time": 54597
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":5}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 41862,
|
||||
"current_time": 54597
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":10}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 44003,
|
||||
"current_time": 54597
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":40}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 56678,
|
||||
"current_time": 65185
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":70}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 69407,
|
||||
"current_time": 77858
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":95}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 80010,
|
||||
"current_time": 82592
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":100}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 82084,
|
||||
"current_time": 82592
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Success!\",\"ota_pct\":100}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 119255,
|
||||
"current_time": 121838
|
||||
}
|
||||
|
||||
]
|
||||
21
components/wifi-manager/webapp/mock/messages_ota_fail.json
Normal file
21
components/wifi-manager/webapp/mock/messages_ota_fail.json
Normal file
@@ -0,0 +1,21 @@
|
||||
[{
|
||||
"message": "{\"ota_dsc\":\"Initializing...\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 119203,
|
||||
"current_time": 121837
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Starting OTA...\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 119255,
|
||||
"current_time": 121838
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Error: Failed to execute HTTP download. ERROR\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_ERROR",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 121954,
|
||||
"current_time": 124911
|
||||
}
|
||||
]
|
||||
117
components/wifi-manager/webapp/mock/messages_ota_flash.json
Normal file
117
components/wifi-manager/webapp/mock/messages_ota_flash.json
Normal file
@@ -0,0 +1,117 @@
|
||||
[
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Initializing...\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 14110,
|
||||
"current_time": 16958
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Starting OTA...\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 14146,
|
||||
"current_time": 16958
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"New version is : mock version \",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 18849,
|
||||
"current_time": 22220
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Formatting OTA partition\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 18850,
|
||||
"current_time": 22220
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Erasing flash (1/11)\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 22217,
|
||||
"current_time": 22220
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Erasing flash (5/11)\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 29896,
|
||||
"current_time": 29902
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Erasing flash (7/11)\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 33395,
|
||||
"current_time": 33408
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Erasing flash (9/11)\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 36843,
|
||||
"current_time": 54597
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Erasing flash complete.\",\"ota_pct\":0}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 39463,
|
||||
"current_time": 54597
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":5}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 41862,
|
||||
"current_time": 54597
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":25}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 50307,
|
||||
"current_time": 54598
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":30}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 52455,
|
||||
"current_time": 54598
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":35}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 54603,
|
||||
"current_time": 65184
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":55}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 63042,
|
||||
"current_time": 65185
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":60}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 65190,
|
||||
"current_time": 77858
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":95}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 80010,
|
||||
"current_time": 82592
|
||||
}, {
|
||||
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":100}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 82084,
|
||||
"current_time": 82592
|
||||
},
|
||||
{
|
||||
"message": "{\"ota_dsc\":\"Success!\",\"ota_pct\":100}",
|
||||
"type": "MESSAGING_INFO",
|
||||
"class": "MESSAGING_CLASS_OTA",
|
||||
"sent_time": 119255,
|
||||
"current_time": 121838
|
||||
}
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"project_name": "dev-server",
|
||||
"version": "webpack",
|
||||
"project_name": "SqueezeAMP",
|
||||
"version": "local.500.cmake-master",
|
||||
"recovery": 1,
|
||||
"Jack": "1",
|
||||
"Voltage": 0,
|
||||
@@ -14,5 +14,13 @@
|
||||
"ssid": "MyTestSSID",
|
||||
"ip": "192.168.10.225",
|
||||
"netmask": "255.255.255.0",
|
||||
"gw": "192.168.10.1"
|
||||
"gw": "192.168.10.1",
|
||||
"lms_cport": 9090,
|
||||
"lms_port": 9100,
|
||||
"lms_ip": "127.0.0.1",
|
||||
"platform_name": "SqueezeAmp",
|
||||
"mock_plugin_has_proxy": "x",
|
||||
"mock_fail_fw_update":"",
|
||||
"mock_fail_recovery":"",
|
||||
"mock_old_recovery":""
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
"popper": "^1.0.1",
|
||||
"react": "^17.0.1",
|
||||
"remixicon": "^2.5.0",
|
||||
"string-argv": "^0.3.1",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
"svgo": "^1.3.2",
|
||||
"webpack-icons-installer": "^2.0.0"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="d-flex flex-column">
|
||||
<div style="display:none">
|
||||
<% if (htmlWebpackPlugin.files.sprites) { %>
|
||||
<% for (var spriteFileName in htmlWebpackPlugin.files.sprites) { %>
|
||||
@@ -17,7 +17,7 @@
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
<nav class="navbar navbar-expand-sm navbar-dark bg-primary" id="mainnav">
|
||||
<header class="navbar navbar-expand-sm navbar-dark bg-primary sticky-top border-bottom border-dark" id="mainnav">
|
||||
<a class="navbar-brand" id="navtitle" href="#">SqueezeESP32</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
@@ -26,11 +26,11 @@
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="nav navbar-nav mr-auto">
|
||||
<li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#tab-wifi">WiFi</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-syslog">Status<span
|
||||
<li class="nav-item omsg"><a class="nav-link" data-toggle="tab" href="#tab-syslog">Status<span
|
||||
class="badge badge-pill badge-success" id="msgcnt"></span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-audio">Audio</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-syst">System</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-hw">Hardware</a></li>
|
||||
<li class="nav-item orec"><a class="nav-link" data-toggle="tab" href="#tab-cfg-audio">Audio</a></li>
|
||||
<li class="nav-item orec"><a class="nav-link" data-toggle="tab" href="#tab-cfg-syst">System</a></li>
|
||||
<li class="nav-item orec"><a class="nav-link" data-toggle="tab" href="#tab-cfg-hw">Hardware</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-fw">Updates</a></li>
|
||||
<div class="dropdown-divider"></div>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-nvs">NVS Editor</a></li>
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
</div>
|
||||
<div class="info navbar-right" style="display: inline-flex;">
|
||||
<svg class="recovery_element bg-primary" style="fill:orange; width:1.5rem; height: 1.5rem;">
|
||||
<svg class="recovery_element " style="fill:orange; width:1.5rem; height: 1.5rem;">
|
||||
<use xlink:href="#device-recover-fill"></use>
|
||||
</svg>
|
||||
|
||||
@@ -77,68 +77,135 @@
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
<div id="message"></div>
|
||||
<div id="content">
|
||||
<div id="myTabContent" class="tab-content mt-3">
|
||||
</header>
|
||||
<main role="main" class="flex-grow mt-1 mb-12" style="margin-bottom: 7rem;" id="content">
|
||||
<!-- Button trigger modal -->
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="otadiv" tabindex="-1" role="dialog" aria-labelledby="fwProgressLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="fwProgressLabel">Upgrade Progress</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span id="flash-status"></span>
|
||||
<div class="progress" id="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width:0%">
|
||||
0%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="myTabContent" class="tab-content">
|
||||
|
||||
<div class="tab-pane fade" id="tab-cfg-hw"></div>
|
||||
<div class="tab-pane fade" id="tab-cfg-syst"></div>
|
||||
<div class="tab-pane fade" id="tab-cfg-gen"></div>
|
||||
<div class="tab-pane fade" id="tab-cfg-fw">
|
||||
<div id="boot-div">
|
||||
<form id="boot-form" action="/recovery.json" method="post" target="dummyframe">
|
||||
<button id="boot-button" type="submit" class="btn btn-primary">Recovery</button>
|
||||
</form>
|
||||
</div>
|
||||
<h1>Check for firmware upgrade</h1>
|
||||
<div class="buttons">
|
||||
<input type="button" id="fwcheck" class="btn btn-info" value="Check for updates" />
|
||||
</div>
|
||||
<div id="searchfw" class="form-group">
|
||||
<select class="custom-select" id="fwbranch">
|
||||
<option selected="">Choose FW branch</option>
|
||||
</select>
|
||||
<input class="form-control form-control-sm" id="searchinput" type="text"
|
||||
placeholder="search releases" id="inputSmall">
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
|
||||
<div class="card text-white mb-3">
|
||||
<div class="card-header">Software Updates</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover table-striped table-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Firmware version</th>
|
||||
<th scope="col">Release date/time</th>
|
||||
<th scope="col">HW platform</th>
|
||||
<th scope="col">IDF version</th>
|
||||
<th scope="col">Branch</th>
|
||||
<th scope="col">Flash this FW</th>
|
||||
<th class="border-bottom-0 pb-0" scope="col">Version</th>
|
||||
<th class="border-bottom-0 pb-0" scope="col">Date/Time</th>
|
||||
<th class="border-bottom-0 pb-0" scope="col">Platform</th>
|
||||
<th class="border-bottom-0 pb-0" scope="col">Branch</th>
|
||||
<th class="border-bottom-0 pb-0" scope="col">Bit Depth</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="border-top-0 pt-0" scope="col"><input class="form-control-sm upSrch"
|
||||
id="svrs" type="text" placeholder="search releases"></th>
|
||||
<th class="border-top-0 pt-0" scope="col"></th>
|
||||
<th class="border-top-0 pt-0" scope="col"><input class="form-control-sm upSrch"
|
||||
id="splf" type="text" placeholder="search platform"></th>
|
||||
<th class="border-top-0 pt-0" scope="col"><select class="form-control-sm upSrch"
|
||||
id="fwbranch">
|
||||
<option selected="">Choose FW branch</option>
|
||||
</select>
|
||||
<th class="border-top-0 pt-0" scope="col"><input class="form-control-sm upSrch"
|
||||
id="bits" type="text" placeholder="search bit depth"></th>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="releaseTable">
|
||||
</tbody>
|
||||
<tbody id="rTable"></tbody>
|
||||
</table>
|
||||
<h2>Firmware URL:</h2>
|
||||
<textarea id="fwurl" maxlength="1000"></textarea>
|
||||
<div class="buttons">
|
||||
<input type="button" id="flash" class="btn btn-danger" value="Flash!" /><span
|
||||
id="flash-status"></span>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-auto">
|
||||
<button type="button" id="chkUpdates" class="btn btn-info btn-sm">Check for
|
||||
updates</button>
|
||||
</div>
|
||||
<div id="uploaddiv" class="recovery_element">
|
||||
<p>OR</p>
|
||||
<div class="form-group">
|
||||
<label class="col-auto col-form-label" for="fw-url-input">Firmware URL</label>
|
||||
<div class="col">
|
||||
<input type="text" class="form-control"
|
||||
placeholder="select entry from list or enter known url" id="fw-url-input">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" id='start-flash' data-toggle="modal" data-target="#uCnfrm"
|
||||
class="btn btn-warning btn-sm" style="display: none;">Flash Firmware</button>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button class="btn-warning ota_element" type="submit" onclick="handleReboot('recovery');" >Recovery</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal" id="uCnfrm">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Firmware Flash</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Flash URL <span id="selectedFWURL" class="text-break"></span> to device?</p>
|
||||
</div>
|
||||
<div class="modal-footer ">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-warning" data-dismiss="modal"
|
||||
onclick="hFlash();">Ok</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white mb-3" >
|
||||
<div class="card-header">Local Firmware Upload</div>
|
||||
<div class="card-body">
|
||||
<div id="uploaddiv" class="form-group row">
|
||||
<label for="flashfilename" class="col-auto col-form-label">Local File</label>
|
||||
<div class="col">
|
||||
<input type="file" class="form-control-file" id="flashfilename" aria-describedby="fileHelp">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="buttons">
|
||||
<button type="button" class="btn btn-danger" id="fwUpload">Upload!</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="otadiv" class="recovery_element">
|
||||
<div class="progress" id="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100"
|
||||
style="width:0%">
|
||||
0%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="tab-nvs">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
@@ -151,11 +218,8 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<div id="boot-div">
|
||||
<form id="reboot-form" action="/reboot.json" method="post" target="dummyframe">
|
||||
<button id="reboot-button" type="submit" class="btn btn-primary">Reboot</button>
|
||||
</form>
|
||||
</div>
|
||||
<button button id="reboot-button" class="btn btn-primary" style="float:right" type="submit" onclick="handleReboot('reboot');" >Reboot</button>
|
||||
|
||||
<input id="save-nvs" type="button" class="btn btn-success" value="Commit">
|
||||
<input id="save-as-nvs" type="button" class="btn btn-success" value="Download config">
|
||||
<input id="load-nvs" type="button" class="btn btn-success" value="Load File">
|
||||
@@ -165,7 +229,7 @@
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="tab-cfg-audio">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card text-white mb-3">
|
||||
<div class="card-header">Usage Templates</div>
|
||||
<div class="card-body">
|
||||
<fieldset>
|
||||
@@ -197,8 +261,8 @@
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" id="disable-squeezelite"
|
||||
value="" checked="">
|
||||
<input class="form-check-input" type="checkbox" id="disable-squeezelite" value=""
|
||||
checked="">
|
||||
Disable Squeezelite
|
||||
</label>
|
||||
</div>
|
||||
@@ -219,7 +283,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade active show" id="tab-wifi">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card text-white mb-3">
|
||||
<div class="card-header">WiFi Status</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover">
|
||||
@@ -246,7 +310,9 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Disconnect from network? After disconnecting, the system won't be accessible from the current address and will expose itself as access point name <span id="apName"></span> with password <span id="apPass"></span> </p>
|
||||
<p>Disconnect from network? After disconnecting, the system won't be accessible from
|
||||
the current address and will expose itself as access point name <span
|
||||
id="apName"></span> with password <span id="apPass"></span> </p>
|
||||
</div>
|
||||
<div class="modal-footer connecting-success connecting-status">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
@@ -260,7 +326,8 @@
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title connecting connecting-init connecting-fail">Connect to WiFi</h5>
|
||||
<h5 class="modal-title connecting connecting-init connecting-fail">Connect to WiFi
|
||||
</h5>
|
||||
<h5 class="modal-title connecting-status connecting-success">Status</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
@@ -268,9 +335,8 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<fieldset class="connecting-init connecting-fail">
|
||||
<div class="form-group"><label for="manual_ssid">Wifi Name</label><input
|
||||
type="text" class="form-control" placeholder="Enter Name"
|
||||
id="manual_ssid"></div>
|
||||
<div class="form-group"><label for="manual_ssid">Wifi Name</label><input type="text"
|
||||
class="form-control" placeholder="Enter Name" id="manual_ssid"></div>
|
||||
<div class="form-group"><label for="manual_pwd">Password</label><input
|
||||
type="password" class="form-control" placeholder="Enter Name"
|
||||
id="manual_pwd"></div>
|
||||
@@ -292,24 +358,27 @@
|
||||
</div>
|
||||
<div id="connect-fail" class="connecting-fail">
|
||||
<h3 class="text-error">Connection failed</h3>
|
||||
<p >Please double-check wifi password if any and make sure the access point has good signal.</p>
|
||||
<p>Please double-check wifi password if any and make sure the access point has
|
||||
good signal.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer ">
|
||||
<button type="button" class="btn btn-secondary connecting-init connecting-fail connecting" data-dismiss="modal">Close</button>
|
||||
<button type="button" id="btnJoin" class="btn btn-primary connecting-init connecting-fail"
|
||||
<button type="button"
|
||||
class="btn btn-secondary connecting-init connecting-fail connecting"
|
||||
data-dismiss="modal">Close</button>
|
||||
<button type="button" id="btnJoin"
|
||||
class="btn btn-primary connecting-init connecting-fail"
|
||||
onclick="handleConnect();">Join</button>
|
||||
<button type="button" class="connecting btn btn-primary" disabled>
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="spinner-border spinner-border-sm" role="status"
|
||||
aria-hidden="true"></span>
|
||||
<span class="sr-only">Connecting...</span></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer connecting-success connecting-status">
|
||||
<button type="button" class="btn btn-warning" data-toggle="modal"
|
||||
data-dismiss="modal" data-target="#WiFiDisconnectConfirm">Disconnect</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<div class="modal-footer connecting-success connecting-status justify-content-between" style=""><button type="button"
|
||||
class="btn btn-primary" data-dismiss="modal">Ok</button><button type="button" class="btn btn-danger"
|
||||
data-toggle="modal" data-dismiss="modal" data-target="#WiFiDisconnectConfirm">Disconnect</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -339,7 +408,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-primary mb-3">
|
||||
<div class="card border-primary mb-3" id="pins" style="display: none;">
|
||||
<div class="card-header">Pin Assignments</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover">
|
||||
@@ -378,7 +447,7 @@
|
||||
</div>
|
||||
<!-- syslog -->
|
||||
<div class="tab-pane fade " id="tab-credits">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card text-white mb-3">
|
||||
<div class="card-header">Credits</div>
|
||||
<div class="card-body">
|
||||
<p><strong><a
|
||||
@@ -404,7 +473,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card text-white mb-3">
|
||||
<div class="card-header">Extras/Overrides</div>
|
||||
<div class="card-body">
|
||||
<fieldset>
|
||||
@@ -424,16 +493,17 @@
|
||||
</div>
|
||||
<!-- credits -->
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="fixed-bottom d-flex justify-content-between border-top border-dark p-3 bg-primary">
|
||||
<span class="text-center" id="foot-fw"></span><button class="btn-warning ota_element " id="reboot_nav"
|
||||
type="submit" onclick="handleReboot('reboot');" style="display: none;">Reboot</button>
|
||||
<button class="btn-warning recovery_element" id="reboot_ota_nav" type="submit" onclick="handleReboot('reboot_ota');"
|
||||
style="display: none;">Exit Recovery</button><span class="text-center" id="foot-wifi"></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<footer class="footer bg-primary text.primary">
|
||||
<button class="btn-warning ota_element" id="reboot_nav" type="submit" onclick="handleReboot(false);"
|
||||
style="display: none;">Reboot</button>
|
||||
<button class="btn-danger recovery_element" id="reboot_ota_nav" type="submit" onclick="handleReboot(true);"
|
||||
style="display: none;">Exit Recovery</button><br>
|
||||
<span id="foot-fw"></span><span id="foot-wifi"></span>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -19,6 +19,6 @@ import '../node_modules/remixicon/icons/Media/play-circle-fill.svg';
|
||||
import '../node_modules/remixicon/icons/Media/pause-circle-fill.svg';
|
||||
import '../node_modules/remixicon/icons/System/lock-fill.svg';
|
||||
import '../node_modules/remixicon/icons/System/lock-unlock-fill.svg';
|
||||
|
||||
import './assets/images/favicon-32x32.png';
|
||||
import './js/custom.js';
|
||||
// <%= `<svg><use xlink:href="#${htmlWebpackPlugin.files.sprites.svg.defs.symbol[s].id}"></use></svg>` %>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +1,34 @@
|
||||
.features:hover {
|
||||
cursor: pointer;
|
||||
animation: jello-horizontal 1.2s;
|
||||
}
|
||||
// .features:hover {
|
||||
// cursor: pointer;
|
||||
// animation: jello-horizontal 1.2s;
|
||||
// }
|
||||
|
||||
@keyframes jello-horizontal {
|
||||
0% {
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
// @keyframes jello-horizontal {
|
||||
// 0% {
|
||||
// transform: scale3d(1, 1, 1);
|
||||
// }
|
||||
|
||||
30% {
|
||||
transform: scale3d(1.25, .75, 1);
|
||||
}
|
||||
// 30% {
|
||||
// transform: scale3d(1.25, .75, 1);
|
||||
// }
|
||||
|
||||
40% {
|
||||
transform: scale3d(.75, 1.25, 1);
|
||||
}
|
||||
// 40% {
|
||||
// transform: scale3d(.75, 1.25, 1);
|
||||
// }
|
||||
|
||||
50% {
|
||||
transform: scale3d(1.15, .85, 1);
|
||||
}
|
||||
// 50% {
|
||||
// transform: scale3d(1.15, .85, 1);
|
||||
// }
|
||||
|
||||
65% {
|
||||
transform: scale3d(.95, 1.05, 1);
|
||||
}
|
||||
// 65% {
|
||||
// transform: scale3d(.95, 1.05, 1);
|
||||
// }
|
||||
|
||||
75% {
|
||||
transform: scale3d(1.05, .95, 1);
|
||||
}
|
||||
// 75% {
|
||||
// transform: scale3d(1.05, .95, 1);
|
||||
// }
|
||||
|
||||
100% {
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
// 100% {
|
||||
// transform: scale3d(1, 1, 1);
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,348 +1,348 @@
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: .67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: .35em .75em .625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
// /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
// /* Document
|
||||
// ========================================================================== */
|
||||
|
||||
// /**
|
||||
// * 1. Correct the line height in all browsers.
|
||||
// * 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
// */
|
||||
|
||||
// html {
|
||||
// line-height: 1.15; /* 1 */
|
||||
// -webkit-text-size-adjust: 100%; /* 2 */
|
||||
// }
|
||||
|
||||
// /* Sections
|
||||
// ========================================================================== */
|
||||
|
||||
// /**
|
||||
// * Remove the margin in all browsers.
|
||||
// */
|
||||
|
||||
// body {
|
||||
// margin: 0;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Render the `main` element consistently in IE.
|
||||
// */
|
||||
|
||||
// main {
|
||||
// display: block;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Correct the font size and margin on `h1` elements within `section` and
|
||||
// * `article` contexts in Chrome, Firefox, and Safari.
|
||||
// */
|
||||
|
||||
// h1 {
|
||||
// font-size: 2em;
|
||||
// margin: .67em 0;
|
||||
// }
|
||||
|
||||
// /* Grouping content
|
||||
// ========================================================================== */
|
||||
|
||||
// /**
|
||||
// * 1. Add the correct box sizing in Firefox.
|
||||
// * 2. Show the overflow in Edge and IE.
|
||||
// */
|
||||
|
||||
// hr {
|
||||
// box-sizing: content-box; /* 1 */
|
||||
// height: 0; /* 1 */
|
||||
// overflow: visible; /* 2 */
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
// * 2. Correct the odd `em` font sizing in all browsers.
|
||||
// */
|
||||
|
||||
// pre {
|
||||
// font-family: monospace; /* 1 */
|
||||
// font-size: 1em; /* 2 */
|
||||
// }
|
||||
|
||||
// /* Text-level semantics
|
||||
// ========================================================================== */
|
||||
|
||||
// /**
|
||||
// * Remove the gray background on active links in IE 10.
|
||||
// */
|
||||
|
||||
// a {
|
||||
// background-color: transparent;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 1. Remove the bottom border in Chrome 57-
|
||||
// * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
// */
|
||||
|
||||
// abbr[title] {
|
||||
// border-bottom: none; /* 1 */
|
||||
// text-decoration: underline; /* 2 */
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Add the correct font weight in Chrome, Edge, and Safari.
|
||||
// */
|
||||
|
||||
// b,
|
||||
// strong {
|
||||
// font-weight: bolder;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
// * 2. Correct the odd `em` font sizing in all browsers.
|
||||
// */
|
||||
|
||||
// code,
|
||||
// kbd,
|
||||
// samp {
|
||||
// font-family: monospace; /* 1 */
|
||||
// font-size: 1em; /* 2 */
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Add the correct font size in all browsers.
|
||||
// */
|
||||
|
||||
// small {
|
||||
// font-size: 80%;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Prevent `sub` and `sup` elements from affecting the line height in
|
||||
// * all browsers.
|
||||
// */
|
||||
|
||||
// sub,
|
||||
// sup {
|
||||
// font-size: 75%;
|
||||
// line-height: 0;
|
||||
// position: relative;
|
||||
// vertical-align: baseline;
|
||||
// }
|
||||
|
||||
// sub {
|
||||
// bottom: -.25em;
|
||||
// }
|
||||
|
||||
// sup {
|
||||
// top: -.5em;
|
||||
// }
|
||||
|
||||
// /* Embedded content
|
||||
// ========================================================================== */
|
||||
|
||||
// /**
|
||||
// * Remove the border on images inside links in IE 10.
|
||||
// */
|
||||
|
||||
// img {
|
||||
// border-style: none;
|
||||
// }
|
||||
|
||||
// /* Forms
|
||||
// ========================================================================== */
|
||||
|
||||
// /**
|
||||
// * 1. Change the font styles in all browsers.
|
||||
// * 2. Remove the margin in Firefox and Safari.
|
||||
// */
|
||||
|
||||
// button,
|
||||
// input,
|
||||
// optgroup,
|
||||
// select,
|
||||
// textarea {
|
||||
// font-family: inherit; /* 1 */
|
||||
// font-size: 100%; /* 1 */
|
||||
// line-height: 1.15; /* 1 */
|
||||
// margin: 0; /* 2 */
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Show the overflow in IE.
|
||||
// * 1. Show the overflow in Edge.
|
||||
// */
|
||||
|
||||
// button,
|
||||
// input { /* 1 */
|
||||
// overflow: visible;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
// * 1. Remove the inheritance of text transform in Firefox.
|
||||
// */
|
||||
|
||||
// button,
|
||||
// select { /* 1 */
|
||||
// text-transform: none;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Correct the inability to style clickable types in iOS and Safari.
|
||||
// */
|
||||
|
||||
// button,
|
||||
// [type="button"],
|
||||
// [type="reset"],
|
||||
// [type="submit"] {
|
||||
// -webkit-appearance: button;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Remove the inner border and padding in Firefox.
|
||||
// */
|
||||
|
||||
// button::-moz-focus-inner,
|
||||
// [type="button"]::-moz-focus-inner,
|
||||
// [type="reset"]::-moz-focus-inner,
|
||||
// [type="submit"]::-moz-focus-inner {
|
||||
// border-style: none;
|
||||
// padding: 0;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Restore the focus styles unset by the previous rule.
|
||||
// */
|
||||
|
||||
// button:-moz-focusring,
|
||||
// [type="button"]:-moz-focusring,
|
||||
// [type="reset"]:-moz-focusring,
|
||||
// [type="submit"]:-moz-focusring {
|
||||
// outline: 1px dotted ButtonText;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Correct the padding in Firefox.
|
||||
// */
|
||||
|
||||
// fieldset {
|
||||
// padding: .35em .75em .625em;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 1. Correct the text wrapping in Edge and IE.
|
||||
// * 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
// * 3. Remove the padding so developers are not caught out when they zero out
|
||||
// * `fieldset` elements in all browsers.
|
||||
// */
|
||||
|
||||
// legend {
|
||||
// box-sizing: border-box; /* 1 */
|
||||
// color: inherit; /* 2 */
|
||||
// display: table; /* 1 */
|
||||
// max-width: 100%; /* 1 */
|
||||
// padding: 0; /* 3 */
|
||||
// white-space: normal; /* 1 */
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
// */
|
||||
|
||||
// progress {
|
||||
// vertical-align: baseline;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Remove the default vertical scrollbar in IE 10+.
|
||||
// */
|
||||
|
||||
// textarea {
|
||||
// overflow: auto;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 1. Add the correct box sizing in IE 10.
|
||||
// * 2. Remove the padding in IE 10.
|
||||
// */
|
||||
|
||||
// [type="checkbox"],
|
||||
// [type="radio"] {
|
||||
// box-sizing: border-box; /* 1 */
|
||||
// padding: 0; /* 2 */
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
// */
|
||||
|
||||
// [type="number"]::-webkit-inner-spin-button,
|
||||
// [type="number"]::-webkit-outer-spin-button {
|
||||
// height: auto;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 1. Correct the odd appearance in Chrome and Safari.
|
||||
// * 2. Correct the outline style in Safari.
|
||||
// */
|
||||
|
||||
// [type="search"] {
|
||||
// -webkit-appearance: textfield; /* 1 */
|
||||
// outline-offset: -2px; /* 2 */
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Remove the inner padding in Chrome and Safari on macOS.
|
||||
// */
|
||||
|
||||
// [type="search"]::-webkit-search-decoration {
|
||||
// -webkit-appearance: none;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
// * 2. Change font properties to `inherit` in Safari.
|
||||
// */
|
||||
|
||||
// ::-webkit-file-upload-button {
|
||||
// -webkit-appearance: button; /* 1 */
|
||||
// font: inherit; /* 2 */
|
||||
// }
|
||||
|
||||
// /* Interactive
|
||||
// ========================================================================== */
|
||||
|
||||
// /*
|
||||
// * Add the correct display in Edge, IE 10+, and Firefox.
|
||||
// */
|
||||
|
||||
// details {
|
||||
// display: block;
|
||||
// }
|
||||
|
||||
// /*
|
||||
// * Add the correct display in all browsers.
|
||||
// */
|
||||
|
||||
// summary {
|
||||
// display: list-item;
|
||||
// }
|
||||
|
||||
// /* Misc
|
||||
// ========================================================================== */
|
||||
|
||||
// /**
|
||||
// * Add the correct display in IE 10+.
|
||||
// */
|
||||
|
||||
// template {
|
||||
// display: none;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Add the correct display in IE 10.
|
||||
// */
|
||||
|
||||
// [hidden] {
|
||||
// display: none;
|
||||
// }
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
|
||||
/* Device = Most of the Smartphones Mobiles (Portrait) */
|
||||
$screen-xxs-min: 320px;
|
||||
$screen-xxs-max: 480px;
|
||||
// /* Device = Most of the Smartphones Mobiles (Portrait) */
|
||||
// $screen-xxs-min: 320px;
|
||||
// $screen-xxs-max: 480px;
|
||||
|
||||
/* Device = Low Resolution Tablets, Mobiles (Landscape) */
|
||||
$screen-xs-min: 481px;
|
||||
$screen-xs-max: 767px;
|
||||
// /* Device = Low Resolution Tablets, Mobiles (Landscape) */
|
||||
// $screen-xs-min: 481px;
|
||||
// $screen-xs-max: 767px;
|
||||
|
||||
/* Device = Tablets, Ipads (portrait) */
|
||||
$screen-sm-min: 768px;
|
||||
$screen-sm-max: 1024px;
|
||||
// /* Device = Tablets, Ipads (portrait) */
|
||||
// $screen-sm-min: 768px;
|
||||
// $screen-sm-max: 1024px;
|
||||
|
||||
/* Device = Laptops, Desktops */
|
||||
$screen-md-min: 1025px;
|
||||
$screen-md-max: 1280px;
|
||||
// /* Device = Laptops, Desktops */
|
||||
// $screen-md-min: 1025px;
|
||||
// $screen-md-max: 1280px;
|
||||
|
||||
/* Device = Desktops */
|
||||
$screen-lg-min: 1281px;
|
||||
$screen-lg-max: 1440px;
|
||||
// /* Device = Desktops */
|
||||
// $screen-lg-min: 1281px;
|
||||
// $screen-lg-max: 1440px;
|
||||
|
||||
/* Higher Resolution Screens */
|
||||
$screen-xlg-min: 1441px;
|
||||
$screen-xlg-max: 2560px;
|
||||
// /* Higher Resolution Screens */
|
||||
// $screen-xlg-min: 1441px;
|
||||
// $screen-xlg-max: 2560px;
|
||||
|
||||
@@ -1,9 +1,34 @@
|
||||
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
}
|
||||
.border-bottom {
|
||||
border-width:3px !important;
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-width:3px !important;
|
||||
}
|
||||
tr.hide {
|
||||
display: none;
|
||||
}
|
||||
.rebooting {
|
||||
display: none;
|
||||
}
|
||||
td.value {
|
||||
width: 80%;
|
||||
}
|
||||
#boot-div {
|
||||
float: right;
|
||||
}
|
||||
/* body {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
margin-bottom:50px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
padding-bottom: 45px;
|
||||
}
|
||||
a {
|
||||
color: #fff;
|
||||
@@ -135,7 +160,7 @@ h3 {
|
||||
.fr {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
}
|
||||
} */
|
||||
/* .w0 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTJDBGvsAAABzUlEQVRIS+WUTShEURTH3zyRhjQ+8hWxmCJMoSzEwsbCgi1LZRYW9pONptiwka9iI81CWFpYaEqNMkVKmpWN1IhYKN9ZDL/z3p3mxZh5g9X4168799xz/vPefedeLeuVC+3gdTgc07CsmCQ2DI2gg21Jci30wSpGt/CeghickTsHPVACDkgqp67rPgpO4E0ZZMIj7OHhxSvPtEyomcVDeFXJv+EZNvEsNa01rZfAuSUhThR2wU+ObJkbyhRNMMDaDIThBqy1MdZ3wAPawqfFC2Lj0Ab5kpBGxdAJs9TeW72ITUhCPZMjFYwwbwXpnkwlDzOIx50yXwP5c0MeggHGanNqSDqqBqQ7/Kxvg2zHAfMN8IE8uZhYO6eBnBXGKnOakLWfaQZ9jMRjSPXhZUuC5A9JjVFpKkeNSVVA0Tq8KJN0yFl4gilqbW2tm+SQKoybXIG8jcT34RSsh1Byt6iVg2ZLlRCg6JpROqEDpFheXZ5S9rcLFsl5YJwHad+MVA5y13w5lRY5oRsKjdm/Vz/7LR86zG+5wr+9NX+iOowjEO+aELEic+lv1ILppeUPosRst6QduTANgnE2mC+BnYswI1VwfYzCCL9dZij7pWkf6UeSTYAuE/QAAAAASUVORK5CYII=') no-repeat left top;
|
||||
height: 24px;
|
||||
@@ -157,7 +182,7 @@ h3 {
|
||||
height: 24px;
|
||||
} */
|
||||
/* SpinKit is licensed under the MIT License. Copyright (c) 2015 Tobias Ahlin */
|
||||
.spinner {
|
||||
/* .spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
@@ -197,11 +222,11 @@ h3 {
|
||||
transform: scale(1.0);
|
||||
-webkit-transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
} */
|
||||
/* end of SpinKit */
|
||||
|
||||
/* daduke stuff */
|
||||
input[type='text'], input[type='password'], textarea, select, option {
|
||||
/* input[type='text'], input[type='password'], textarea, select, option {
|
||||
background: #999;
|
||||
border: 0;
|
||||
padding: 4px;
|
||||
@@ -229,8 +254,8 @@ input[type='text'], input[type='password'], textarea, select, option {
|
||||
pointer-events: all;
|
||||
border-radius: 1rem;
|
||||
background-color: #f00;
|
||||
}
|
||||
|
||||
} */
|
||||
/*
|
||||
.custom-switch .custom-control-label::after {
|
||||
top: calc(0.25rem + 2px);
|
||||
left: calc(-2.25rem + 2px);
|
||||
@@ -246,8 +271,8 @@ input[type='text'], input[type='password'], textarea, select, option {
|
||||
.custom-switch .custom-control-label::after {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
} */
|
||||
/*
|
||||
.custom-switch .custom-control-input:checked ~ .custom-control-label::before {
|
||||
background-color: #0f0;
|
||||
}
|
||||
@@ -307,7 +332,7 @@ input, textarea {
|
||||
span#flash-status {
|
||||
padding-left: 15px;
|
||||
font-size: 120%;
|
||||
}
|
||||
} */
|
||||
|
||||
/* #info {
|
||||
padding-top: 7px;
|
||||
@@ -342,7 +367,7 @@ ul#navbar {
|
||||
border-top: 1px solid black;
|
||||
} */
|
||||
|
||||
.footer {
|
||||
/* .footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
@@ -350,8 +375,8 @@ ul#navbar {
|
||||
background-color: #555;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
} */
|
||||
/*
|
||||
.sl {
|
||||
background-color: #053c1e;
|
||||
}
|
||||
@@ -359,13 +384,9 @@ ul#navbar {
|
||||
background-color: #3c0505;
|
||||
}
|
||||
|
||||
td.value {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
#boot-div {
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
div#message {
|
||||
display: none;
|
||||
color: #000;
|
||||
@@ -377,23 +398,21 @@ div#message {
|
||||
width:20em;
|
||||
height:4em;
|
||||
text-align: center;
|
||||
margin-left: -10em; /*set to a negative number 1/2 of your width*/
|
||||
margin-top: -2em; /*set to a negative number 1/2 of your height*/
|
||||
margin-left: -10em;
|
||||
margin-top: -2em;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 5px 2px -5px rgba(255, 255, 255, 0.5) inset, 0px 10px 20px -5px rgba(255, 255, 255, 0.1) inset, 0 0px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 1px rgba(0, 0, 0, 0.12), 0 1px 10px 0 rgba(0, 0, 0, 0.3);
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
tr.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
} */
|
||||
/*
|
||||
*/
|
||||
/*
|
||||
#searchfw {
|
||||
float: right;
|
||||
display: none;
|
||||
}
|
||||
} */
|
||||
|
||||
button#updateAP {
|
||||
/* button#updateAP {
|
||||
float: right;
|
||||
display: inline;
|
||||
}
|
||||
} */
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<% } %>
|
||||
</div>
|
||||
<div id="allIcons"></div>
|
||||
|
||||
<div class="card border-primary mb-3">
|
||||
<div class="card-header">Status Variables</div>
|
||||
<div class="card-body">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/favicon-32x32.png BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/index.html.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.e644c0.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.e644c0.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.e644c0.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.0e064e.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.0e064e.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.0e064e.bundle.js.gz BINARY)
|
||||
|
||||
@@ -4,31 +4,31 @@ extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_s
|
||||
extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
|
||||
extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
|
||||
extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
|
||||
extern const uint8_t _index_e644c0_bundle_js_gz_start[] asm("_binary_index_e644c0_bundle_js_gz_start");
|
||||
extern const uint8_t _index_e644c0_bundle_js_gz_end[] asm("_binary_index_e644c0_bundle_js_gz_end");
|
||||
extern const uint8_t _node_modules_e644c0_bundle_js_gz_start[] asm("_binary_node_modules_e644c0_bundle_js_gz_start");
|
||||
extern const uint8_t _node_modules_e644c0_bundle_js_gz_end[] asm("_binary_node_modules_e644c0_bundle_js_gz_end");
|
||||
extern const uint8_t _runtime_e644c0_bundle_js_gz_start[] asm("_binary_runtime_e644c0_bundle_js_gz_start");
|
||||
extern const uint8_t _runtime_e644c0_bundle_js_gz_end[] asm("_binary_runtime_e644c0_bundle_js_gz_end");
|
||||
extern const uint8_t _index_0e064e_bundle_js_gz_start[] asm("_binary_index_0e064e_bundle_js_gz_start");
|
||||
extern const uint8_t _index_0e064e_bundle_js_gz_end[] asm("_binary_index_0e064e_bundle_js_gz_end");
|
||||
extern const uint8_t _node_modules_0e064e_bundle_js_gz_start[] asm("_binary_node_modules_0e064e_bundle_js_gz_start");
|
||||
extern const uint8_t _node_modules_0e064e_bundle_js_gz_end[] asm("_binary_node_modules_0e064e_bundle_js_gz_end");
|
||||
extern const uint8_t _runtime_0e064e_bundle_js_gz_start[] asm("_binary_runtime_0e064e_bundle_js_gz_start");
|
||||
extern const uint8_t _runtime_0e064e_bundle_js_gz_end[] asm("_binary_runtime_0e064e_bundle_js_gz_end");
|
||||
const char * resource_lookups[] = {
|
||||
"/dist/favicon-32x32.png",
|
||||
"/dist/index.html.gz",
|
||||
"/js/index.e644c0.bundle.js.gz",
|
||||
"/js/node-modules.e644c0.bundle.js.gz",
|
||||
"/js/runtime.e644c0.bundle.js.gz",
|
||||
"/favicon-32x32.png",
|
||||
"/index.html.gz",
|
||||
"/js/index.0e064e.bundle.js.gz",
|
||||
"/js/node-modules.0e064e.bundle.js.gz",
|
||||
"/js/runtime.0e064e.bundle.js.gz",
|
||||
""
|
||||
};
|
||||
const uint8_t * resource_map_start[] = {
|
||||
_favicon_32x32_png_start,
|
||||
_index_html_gz_start,
|
||||
_index_e644c0_bundle_js_gz_start,
|
||||
_node_modules_e644c0_bundle_js_gz_start,
|
||||
_runtime_e644c0_bundle_js_gz_start
|
||||
_index_0e064e_bundle_js_gz_start,
|
||||
_node_modules_0e064e_bundle_js_gz_start,
|
||||
_runtime_0e064e_bundle_js_gz_start
|
||||
};
|
||||
const uint8_t * resource_map_end[] = {
|
||||
_favicon_32x32_png_end,
|
||||
_index_html_gz_end,
|
||||
_index_e644c0_bundle_js_gz_end,
|
||||
_node_modules_e644c0_bundle_js_gz_end,
|
||||
_runtime_e644c0_bundle_js_gz_end
|
||||
_index_0e064e_bundle_js_gz_end,
|
||||
_node_modules_0e064e_bundle_js_gz_end,
|
||||
_runtime_0e064e_bundle_js_gz_end
|
||||
};
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
/***********************************
|
||||
webpack_headers
|
||||
Hash: e644c04d107606ae748d
|
||||
Version: webpack 4.44.2
|
||||
Time: 6142ms
|
||||
Built at: 2020-12-21 12 h 10 min 00 s
|
||||
Hash: 0e064eadc7c8b7881633
|
||||
Version: webpack 4.46.0
|
||||
Time: 9582ms
|
||||
Built at: 2021-07-09 11 h 14 min 41 s
|
||||
Asset Size Chunks Chunk Names
|
||||
./js/index.e644c0.bundle.js 230 KiB 0 [emitted] [immutable] index
|
||||
./js/index.e644c0.bundle.js.br 31.3 KiB [emitted]
|
||||
./js/index.e644c0.bundle.js.gz 40.9 KiB [emitted]
|
||||
./js/node-modules.e644c0.bundle.js 265 KiB 1 [emitted] [immutable] [big] node-modules
|
||||
./js/node-modules.e644c0.bundle.js.br 76.2 KiB [emitted]
|
||||
./js/node-modules.e644c0.bundle.js.gz 88.6 KiB [emitted]
|
||||
./js/runtime.e644c0.bundle.js 1.46 KiB 2 [emitted] [immutable] runtime
|
||||
./js/runtime.e644c0.bundle.js.br 644 bytes [emitted]
|
||||
./js/runtime.e644c0.bundle.js.gz 722 bytes [emitted]
|
||||
favicon-32x32.png 578 bytes [emitted]
|
||||
index.html 19.5 KiB [emitted]
|
||||
index.html.br 4.48 KiB [emitted]
|
||||
index.html.gz 5.46 KiB [emitted]
|
||||
./js/index.0e064e.bundle.js 232 KiB 0 [emitted] [immutable] index
|
||||
./js/index.0e064e.bundle.js.br 32.7 KiB [emitted]
|
||||
./js/index.0e064e.bundle.js.gz 42 KiB [emitted]
|
||||
./js/node-modules.0e064e.bundle.js 266 KiB 1 [emitted] [immutable] [big] node-modules
|
||||
./js/node-modules.0e064e.bundle.js.br 76.3 KiB [emitted]
|
||||
./js/node-modules.0e064e.bundle.js.gz 88.7 KiB [emitted]
|
||||
./js/runtime.0e064e.bundle.js 1.46 KiB 2 [emitted] [immutable] runtime
|
||||
./js/runtime.0e064e.bundle.js.br 644 bytes [emitted]
|
||||
./js/runtime.0e064e.bundle.js.gz 722 bytes [emitted]
|
||||
favicon-32x32.png 634 bytes [emitted]
|
||||
index.html 21.7 KiB [emitted]
|
||||
index.html.br 4.74 KiB [emitted]
|
||||
index.html.gz 5.75 KiB [emitted]
|
||||
sprite.svg 4.4 KiB [emitted]
|
||||
sprite.svg.br 912 bytes [emitted]
|
||||
Entrypoint index [big] = ./js/runtime.e644c0.bundle.js ./js/node-modules.e644c0.bundle.js ./js/index.e644c0.bundle.js
|
||||
sprite.svg.br 898 bytes [emitted]
|
||||
Entrypoint index [big] = ./js/runtime.0e064e.bundle.js ./js/node-modules.0e064e.bundle.js ./js/index.0e064e.bundle.js
|
||||
[6] ./node_modules/bootstrap/dist/js/bootstrap-exposed.js 437 bytes {1} [built]
|
||||
[11] ./src/sass/main.scss 1.55 KiB {0} [built]
|
||||
[16] ./node_modules/remixicon/icons/Device/signal-wifi-fill.svg 340 bytes {1} [built]
|
||||
@@ -35,22 +35,22 @@ Entrypoint index [big] = ./js/runtime.e644c0.bundle.js ./js/node-modules.e644c0.
|
||||
[25] ./node_modules/remixicon/icons/Device/device-recover-fill.svg 346 bytes {1} [built]
|
||||
[26] ./node_modules/remixicon/icons/Device/bluetooth-fill.svg 336 bytes {1} [built]
|
||||
[27] ./node_modules/remixicon/icons/Device/bluetooth-connect-fill.svg 352 bytes {1} [built]
|
||||
[37] ./src/index.ts + 1 modules 52.6 KiB {0} [built]
|
||||
| ./src/index.ts 1.36 KiB [built]
|
||||
| ./src/js/custom.js 51.2 KiB [built]
|
||||
+ 23 hidden modules
|
||||
[38] ./src/index.ts + 1 modules 62.6 KiB {0} [built]
|
||||
| ./src/index.ts 1.4 KiB [built]
|
||||
| ./src/js/custom.js 61 KiB [built]
|
||||
+ 24 hidden modules
|
||||
|
||||
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
|
||||
This can impact web performance.
|
||||
Assets:
|
||||
./js/node-modules.e644c0.bundle.js (265 KiB)
|
||||
./js/node-modules.0e064e.bundle.js (266 KiB)
|
||||
|
||||
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
|
||||
Entrypoints:
|
||||
index (497 KiB)
|
||||
./js/runtime.e644c0.bundle.js
|
||||
./js/node-modules.e644c0.bundle.js
|
||||
./js/index.e644c0.bundle.js
|
||||
index (499 KiB)
|
||||
./js/runtime.0e064e.bundle.js
|
||||
./js/node-modules.0e064e.bundle.js
|
||||
./js/index.0e064e.bundle.js
|
||||
|
||||
|
||||
WARNING in webpack performance recommendations:
|
||||
@@ -58,10 +58,10 @@ You can limit the size of your bundles by using import() or require.ensure to la
|
||||
For more info visit https://webpack.js.org/guides/code-splitting/
|
||||
Child html-webpack-plugin for "index.html":
|
||||
Asset Size Chunks Chunk Names
|
||||
index.html 556 KiB 0
|
||||
index.html 560 KiB 0
|
||||
Entrypoint undefined = index.html
|
||||
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.ejs 21.1 KiB {0} [built]
|
||||
[1] ./node_modules/lodash/lodash.js 530 KiB {0} [built]
|
||||
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.ejs 23.9 KiB {0} [built]
|
||||
[1] ./node_modules/lodash/lodash.js 531 KiB {0} [built]
|
||||
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
|
||||
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
|
||||
***********************************/
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 634 B |
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
9
components/wifi-manager/webapp/webpack/dist/js/index.0e064e.bundle.js
vendored
Normal file
9
components/wifi-manager/webapp/webpack/dist/js/index.0e064e.bundle.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
components/wifi-manager/webapp/webpack/dist/js/index.0e064e.bundle.js.br
vendored
Normal file
BIN
components/wifi-manager/webapp/webpack/dist/js/index.0e064e.bundle.js.br
vendored
Normal file
Binary file not shown.
BIN
components/wifi-manager/webapp/webpack/dist/js/index.0e064e.bundle.js.gz
vendored
Normal file
BIN
components/wifi-manager/webapp/webpack/dist/js/index.0e064e.bundle.js.gz
vendored
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user