Browse Source

Merge remote-tracking branch 'upstream/master'

master
kkloesener 5 years ago
parent
commit
7e0f1bb63d
  1. 6
      .gitignore
  2. 9
      Hardware-Plaforms/ESP32-A1S-Audiokit/src/main.cpp
  3. 21
      PCBs/ESP32_Breakout_Carrier/Readme.md
  4. 337
      PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2-cache.lib
  5. 2410
      PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.kicad_pcb
  6. 2383
      PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.kicad_pcb-bak
  7. 414
      PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.net
  8. 248
      PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.pro
  9. 585
      PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.sch
  10. 585
      PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.sch-bak
  11. 33
      PCBs/ESP32_Breakout_Carrier/_autosave-Tonuino-ESP32_2.pro
  12. BIN
      PCBs/ESP32_Breakout_Carrier/assembled.jpeg
  13. 1
      PCBs/ESP32_Breakout_Carrier/fp-info-cache
  14. 0
      PCBs/Headphone with PCM5102a and TDA1308/KiCad/Kicad-Files.zip
  15. 0
      PCBs/Headphone with PCM5102a and TDA1308/Pictures/3D-model_downside.jpeg
  16. 0
      PCBs/Headphone with PCM5102a and TDA1308/Pictures/3D-model_upside.jpeg
  17. 0
      PCBs/Headphone with PCM5102a and TDA1308/Pictures/Headphone-pcb.pdf
  18. 0
      PCBs/Headphone with PCM5102a and TDA1308/Pictures/Headphone-pcb_downside.jpg
  19. 0
      PCBs/Headphone with PCM5102a and TDA1308/Pictures/Headphone-pcb_upside.jpg
  20. 2
      PCBs/Headphone with PCM5102a and TDA1308/README.md
  21. BIN
      PCBs/Headphone with PCM5102a and TDA1308/gerber/gerber.zip
  22. BIN
      PCBs/Wemos Lolin D32/Gerber/gerber.zip
  23. BIN
      PCBs/Wemos Lolin D32/KiCad/KICad-Files.zip
  24. BIN
      PCBs/Wemos Lolin D32/Pictures/Schematics_D32.pdf
  25. BIN
      PCBs/Wemos Lolin D32/Pictures/Tonuino D32_downside.jpg
  26. BIN
      PCBs/Wemos Lolin D32/Pictures/Tonuino D32_upside.jpg
  27. 63
      PCBs/Wemos Lolin D32/README.md
  28. BIN
      PCBs/Wemos Lolin32/Gerber/gerber.zip
  29. BIN
      PCBs/Wemos Lolin32/KiCad/Kicad-files.zip
  30. BIN
      PCBs/Wemos Lolin32/Pictures/Soldered with peripherals1_rev1.jpg
  31. BIN
      PCBs/Wemos Lolin32/Pictures/Soldered with peripherals2_rev1.jpg
  32. BIN
      PCBs/Wemos Lolin32/Pictures/Soldered1_rev1.jpg
  33. BIN
      PCBs/Wemos Lolin32/Pictures/Soldered2_rev1.jpg
  34. BIN
      PCBs/Wemos Lolin32/Pictures/Tonuino V2-Schematics.pdf
  35. BIN
      PCBs/Wemos Lolin32/Pictures/Tonuino V2_down.jpg
  36. BIN
      PCBs/Wemos Lolin32/Pictures/Tonuino V2_up.jpg
  37. 66
      PCBs/Wemos Lolin32/README.md
  38. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/Gerber/gerber.zip
  39. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/KiCad/Kicad-files.zip
  40. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/3d-Model downside.jpg
  41. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/3d-Model upside.jpg
  42. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Soldered with peripherals1.jpg
  43. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Soldered with peripherals2.jpg
  44. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Soldered1.jpg
  45. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Soldered2.jpg
  46. BIN
      PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Tonuino-Lolin32-Schematics.pdf
  47. 256
      README.md
  48. 64
      html/accesspoint.html
  49. 64
      html/accesspoint_EN.html
  50. 727
      html/management.html
  51. 727
      html/management_EN.html
  52. 359
      html/website.html
  53. 23
      html/websiteBasic.html
  54. 23
      html/websiteBasic_EN.html
  55. 263
      html/websiteMgmt.h
  56. 359
      html/website_EN.html
  57. BIN
      pictures/Mgmt-GUI1.jpg
  58. BIN
      pictures/Mgmt-GUI2.jpg
  59. BIN
      pictures/Mgmt-GUI3.jpg
  60. BIN
      pictures/Mgmt-GUI4.jpg
  61. BIN
      pictures/Mgmt-GUI5.jpg
  62. BIN
      pictures/Mgmt-GUI6.jpg
  63. BIN
      pictures/Mgmt-GUI7.jpg
  64. BIN
      pictures/Mgmt-GUI_connection_broken.jpg
  65. BIN
      pictures/Mgmt_GUI_action_ok.jpg
  66. 55
      platformio.ini
  67. 101
      processHtml.py
  68. 65
      src/HTMLaccesspoint.h
  69. 2
      src/HTMLaccesspoint_EN.h
  70. 728
      src/HTMLmanagement.h
  71. 24
      src/HTMLmanagement_EN.h
  72. 28
      src/logmessages.h
  73. 26
      src/logmessages_EN.h
  74. 1054
      src/main.cpp
  75. 66
      src/settings-espa1s.h
  76. 61
      src/settings-lolin32.h
  77. 117
      src/settings.h
  78. 24
      src/websiteBasic.h
  79. 360
      src/websiteMgmt.h

6
.gitignore

@ -1,6 +1,12 @@
.pio
venv/
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.DS_Store
.idea/
CMakeLists.txt
CMakeListsPrivate.txt
cmake-build-az-delivery-devkit-v4/
cmake-build-debug/

9
Hardware-Plaforms/ESP32-A1S-Audiokit/src/main.cpp

@ -1521,8 +1521,6 @@ void rfidScanner(void *parameter) {
//static MFRC522 mfrc522(MFRC522_CS_PIN, MFRC522_RST_PIN);
//static MFRC522 mfrc522(RFID_CS, RST_PIN);
//SPI.begin();
pinMode(MFRC522_CS_PIN, OUTPUT);
digitalWrite(MFRC522_CS_PIN, HIGH);
mfrc522.PCD_Init();
mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader detail
delay(4);
@ -3054,6 +3052,11 @@ void setup() {
prefsRfid.begin((char *) FPSTR(prefsRfidNamespace));
prefsSettings.begin((char *) FPSTR(prefsSettingsNamespace));
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
pinMode(MFRC522_CS_PIN, OUTPUT);
digitalWrite(MFRC522_CS_PIN, HIGH);
SPI_MFRC.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
SPI_MFRC.setFrequency(1000000);
@ -3094,8 +3097,6 @@ void setup() {
#endif
// Init uSD-SPI
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
/*SPI_MFRC.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
SPI_MFRC.setFrequency(1000000);*/
while (!SD.begin(SD_CS, SPI_MFRC)) {

21
PCBs/ESP32_Breakout_Carrier/Readme.md

@ -0,0 +1,21 @@
## Disclaimer
This PCB was kindly provided by a user (@mariolukas) of my Tonuino-fork and I didn't test it myself.
## ESP32 I2S Breakout Carrier PCB
This is a simple PCB which makes wiring the whole stuff much easier. It contains connections for all components which are described in the projects wiring table. An ESP32 from AZ-Delivery was used but every pin-Compatible ESP32-board will do the job.
![Assembled Board](assembled.jpeg)
The image shows my first boards which came with a litte wiring bug. The bug is fixed in the current version. I ordered my board at aisler.net, there is already and upload available.
[https://aisler.net/p/YTYZJZMM](https://aisler.net/p/YTYZJZMM)
## Todos
This PCB does not provide an option for charing or power-management (including step down/up). It provides a pinout for 5V-input.
## Antenna-improvements
It is recommended to solder an external antenna to the ESP32 for preventing connection issues. Cheap ESP32 boards come with an antenna which is laying on the PCB. The newer ones provide an ESP32-board where the antenna-part protrudes over the edge.
You can find some useful tips for soldering an antenna to the old ones here:
[https://community.home-assistant.io/t/how-to-add-an-external-antenna-to-an-esp-board/131601](https://community.home-assistant.io/t/how-to-add-an-external-antenna-to-an-esp-board/131601)
## Disclaimer
PCB-circuit is provided 'as is' without warranty. It was made to keep wiring much simpler. However: it runs fine without any problems in my Tonuinos.

337
PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2-cache.lib

@ -0,0 +1,337 @@
EESchema-LIBRARY Version 2.4
#encoding utf-8
#
# Connector_Conn_01x02_Male
#
DEF Connector_Conn_01x02_Male J 0 40 Y N 1 F N
F0 "J" 0 100 50 H V C CNN
F1 "Connector_Conn_01x02_Male" 0 -200 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
X Pin_1 1 200 0 150 L 50 50 1 1 P
X Pin_2 2 200 -100 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x03_Male
#
DEF Connector_Conn_01x03_Male J 0 40 Y N 1 F N
F0 "J" 0 200 50 H V C CNN
F1 "Connector_Conn_01x03_Male" 0 -200 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
S 34 105 0 95 1 1 6 F
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
P 2 1 1 6 50 100 34 100 N
X Pin_1 1 200 100 150 L 50 50 1 1 P
X Pin_2 2 200 0 150 L 50 50 1 1 P
X Pin_3 3 200 -100 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x05_Male
#
DEF Connector_Conn_01x05_Male J 0 40 Y N 1 F N
F0 "J" 0 300 50 H V C CNN
F1 "Connector_Conn_01x05_Male" 0 -300 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -195 0 -205 1 1 6 F
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
S 34 105 0 95 1 1 6 F
S 34 205 0 195 1 1 6 F
P 2 1 1 6 50 -200 34 -200 N
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
P 2 1 1 6 50 100 34 100 N
P 2 1 1 6 50 200 34 200 N
X Pin_1 1 200 200 150 L 50 50 1 1 P
X Pin_2 2 200 100 150 L 50 50 1 1 P
X Pin_3 3 200 0 150 L 50 50 1 1 P
X Pin_4 4 200 -100 150 L 50 50 1 1 P
X Pin_5 5 200 -200 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x06_Male
#
DEF Connector_Conn_01x06_Male J 0 40 Y N 1 F N
F0 "J" 0 300 50 H V C CNN
F1 "Connector_Conn_01x06_Male" 0 -400 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -295 0 -305 1 1 6 F
S 34 -195 0 -205 1 1 6 F
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
S 34 105 0 95 1 1 6 F
S 34 205 0 195 1 1 6 F
P 2 1 1 6 50 -300 34 -300 N
P 2 1 1 6 50 -200 34 -200 N
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
P 2 1 1 6 50 100 34 100 N
P 2 1 1 6 50 200 34 200 N
X Pin_1 1 200 200 150 L 50 50 1 1 P
X Pin_2 2 200 100 150 L 50 50 1 1 P
X Pin_3 3 200 0 150 L 50 50 1 1 P
X Pin_4 4 200 -100 150 L 50 50 1 1 P
X Pin_5 5 200 -200 150 L 50 50 1 1 P
X Pin_6 6 200 -300 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x07_Male
#
DEF Connector_Conn_01x07_Male J 0 40 Y N 1 F N
F0 "J" 0 400 50 H V C CNN
F1 "Connector_Conn_01x07_Male" 0 -400 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -295 0 -305 1 1 6 F
S 34 -195 0 -205 1 1 6 F
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
S 34 105 0 95 1 1 6 F
S 34 205 0 195 1 1 6 F
S 34 305 0 295 1 1 6 F
P 2 1 1 6 50 -300 34 -300 N
P 2 1 1 6 50 -200 34 -200 N
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
P 2 1 1 6 50 100 34 100 N
P 2 1 1 6 50 200 34 200 N
P 2 1 1 6 50 300 34 300 N
X Pin_1 1 200 300 150 L 50 50 1 1 P
X Pin_2 2 200 200 150 L 50 50 1 1 P
X Pin_3 3 200 100 150 L 50 50 1 1 P
X Pin_4 4 200 0 150 L 50 50 1 1 P
X Pin_5 5 200 -100 150 L 50 50 1 1 P
X Pin_6 6 200 -200 150 L 50 50 1 1 P
X Pin_7 7 200 -300 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x08_Male
#
DEF Connector_Conn_01x08_Male J 0 40 Y N 1 F N
F0 "J" 0 400 50 H V C CNN
F1 "Connector_Conn_01x08_Male" 0 -500 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -395 0 -405 1 1 6 F
S 34 -295 0 -305 1 1 6 F
S 34 -195 0 -205 1 1 6 F
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
S 34 105 0 95 1 1 6 F
S 34 205 0 195 1 1 6 F
S 34 305 0 295 1 1 6 F
P 2 1 1 6 50 -400 34 -400 N
P 2 1 1 6 50 -300 34 -300 N
P 2 1 1 6 50 -200 34 -200 N
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
P 2 1 1 6 50 100 34 100 N
P 2 1 1 6 50 200 34 200 N
P 2 1 1 6 50 300 34 300 N
X Pin_1 1 200 300 150 L 50 50 1 1 P
X Pin_2 2 200 200 150 L 50 50 1 1 P
X Pin_3 3 200 100 150 L 50 50 1 1 P
X Pin_4 4 200 0 150 L 50 50 1 1 P
X Pin_5 5 200 -100 150 L 50 50 1 1 P
X Pin_6 6 200 -200 150 L 50 50 1 1 P
X Pin_7 7 200 -300 150 L 50 50 1 1 P
X Pin_8 8 200 -400 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_R
#
DEF Device_R R 0 0 N Y 1 F N
F0 "R" 80 0 50 V V C CNN
F1 "Device_R" 0 0 50 V V C CNN
F2 "" -70 0 50 V I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
R_*
$ENDFPLIST
DRAW
S -40 -100 40 100 0 1 10 N
X ~ 1 0 150 50 D 50 50 1 1 P
X ~ 2 0 -150 50 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# ESP32-DEVKITC-32U_ESP32-DEVKITC-32U
#
DEF ESP32-DEVKITC-32U_ESP32-DEVKITC-32U U 0 40 Y Y 1 L N
F0 "U" -601 1026 50 H V L BNN
F1 "ESP32-DEVKITC-32U_ESP32-DEVKITC-32U" -601 -1101 50 H V L BNN
F2 "MODULE_ESP32-DEVKITC-32U" 0 0 50 H I L BNN
F3 "Manufacturer Recommendations" 0 0 50 H I L BNN
F4 "ESPRESSIF" 0 0 50 H I L BNN
F5 "N/A" 0 0 50 H I L BNN
DRAW
P 2 0 0 10 -600 -1000 -600 1000 N
P 2 0 0 10 -600 1000 600 1000 N
P 2 0 0 10 600 -1000 -600 -1000 N
P 2 0 0 10 600 1000 600 -1000 N
X 3V3 1 -800 900 200 R 40 40 0 0 W
X IO26 10 -800 0 200 R 40 40 0 0 B
X IO27 11 -800 -100 200 R 40 40 0 0 B
X IO14 12 -800 -200 200 R 40 40 0 0 B
X IO12 13 -800 -300 200 R 40 40 0 0 B
X GND1 14 -800 -400 200 R 40 40 0 0 W
X IO13 15 -800 -500 200 R 40 40 0 0 B
X SD2 16 -800 -600 200 R 40 40 0 0 B
X SD3 17 -800 -700 200 R 40 40 0 0 B
X CMD 18 -800 -800 200 R 40 40 0 0 B
X EXT_5V 19 -800 -900 200 R 40 40 0 0 W
X EN 2 -800 800 200 R 40 40 0 0 I
X CLK 20 800 -900 200 L 40 40 0 0 I C
X SD0 21 800 -800 200 L 40 40 0 0 B
X SD1 22 800 -700 200 L 40 40 0 0 B
X IO15 23 800 -600 200 L 40 40 0 0 B
X IO2 24 800 -500 200 L 40 40 0 0 B
X IO0 25 800 -400 200 L 40 40 0 0 B
X IO4 26 800 -300 200 L 40 40 0 0 B
X IO16 27 800 -200 200 L 40 40 0 0 B
X IO17 28 800 -100 200 L 40 40 0 0 B
X IO5 29 800 0 200 L 40 40 0 0 B
X SENSOR_VP 3 -800 700 200 R 40 40 0 0 I
X IO18 30 800 100 200 L 40 40 0 0 B
X IO19 31 800 200 200 L 40 40 0 0 B
X GND2 32 800 300 200 L 40 40 0 0 W
X IO21 33 800 400 200 L 40 40 0 0 B
X RXD0 34 800 500 200 L 40 40 0 0 I
X TXD0 35 800 600 200 L 40 40 0 0 O
X IO22 36 800 700 200 L 40 40 0 0 B
X IO23 37 800 800 200 L 40 40 0 0 B
X GND3 38 800 900 200 L 40 40 0 0 W
X SENSOR_VN 4 -800 600 200 R 40 40 0 0 I
X IO34 5 -800 500 200 R 40 40 0 0 B
X IO35 6 -800 400 200 R 40 40 0 0 B
X IO32 7 -800 300 200 R 40 40 0 0 B
X IO33 8 -800 200 200 R 40 40 0 0 B
X IO25 9 -800 100 200 R 40 40 0 0 B
ENDDRAW
ENDDEF
#
# Transistor_BJT_BC337
#
DEF Transistor_BJT_BC337 Q 0 0 Y N 1 F N
F0 "Q" 200 75 50 H V L CNN
F1 "Transistor_BJT_BC337" 200 0 50 H V L CNN
F2 "Package_TO_SOT_THT:TO-92_Inline" 200 -75 50 H I L CIN
F3 "" 0 0 50 H I L CNN
ALIAS BC546 BC548 BC549 BC550 BC337 BC338
$FPLIST
TO?92*
$ENDFPLIST
DRAW
C 50 0 111 0 1 10 N
P 2 0 1 0 0 0 25 0 N
P 2 0 1 0 25 25 100 100 N
P 3 0 1 0 25 -25 100 -100 100 -100 N
P 3 0 1 20 25 75 25 -75 25 -75 N
P 5 0 1 0 50 -70 70 -50 90 -90 50 -70 50 -70 F
X C 1 100 200 100 D 50 50 1 1 P
X B 2 -200 0 200 R 50 50 1 1 I
X E 3 100 -200 100 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Transistor_FET_IRF9540N
#
DEF Transistor_FET_IRF9540N Q 0 20 Y N 1 F N
F0 "Q" 200 75 50 H V L CNN
F1 "Transistor_FET_IRF9540N" 200 0 50 H V L CNN
F2 "Package_TO_SOT_THT:TO-220-3_Vertical" 200 -75 50 H I L CIN
F3 "" 0 0 50 H I L CNN
ALIAS IRF4905 FQP27P06
$FPLIST
TO?220*
$ENDFPLIST
DRAW
C 65 0 110 0 1 10 N
C 100 -70 10 0 1 0 F
C 100 70 10 0 1 0 F
P 2 0 1 0 10 0 -100 0 N
P 2 0 1 10 10 75 10 -75 N
P 2 0 1 10 30 -50 30 -90 N
P 2 0 1 10 30 20 30 -20 N
P 2 0 1 10 30 90 30 50 N
P 2 0 1 0 100 100 100 70 N
P 3 0 1 0 100 -100 100 0 30 0 N
P 4 0 1 0 30 70 130 70 130 -70 30 -70 N
P 4 0 1 0 90 0 50 15 50 -15 90 0 F
P 4 0 1 0 110 -20 115 -15 145 -15 150 -10 N
P 4 0 1 0 130 -15 115 10 145 10 130 -15 N
X G 1 -200 0 100 R 50 50 1 1 I
X D 2 100 200 100 D 50 50 1 1 P
X S 3 100 -200 100 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# power_GND
#
DEF power_GND #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -250 50 H I C CNN
F1 "power_GND" 0 -150 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
DRAW
P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N
X GND 1 0 0 0 D 50 50 1 1 W N
ENDDRAW
ENDDEF
#
# power_VCC
#
DEF power_VCC #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -150 50 H I C CNN
F1 "power_VCC" 0 150 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
DRAW
P 2 0 1 0 -30 50 0 100 N
P 2 0 1 0 0 0 0 100 N
P 2 0 1 0 0 100 30 50 N
X VCC 1 0 0 0 U 50 50 1 1 W N
ENDDRAW
ENDDEF
#
#End Library

2410
PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.kicad_pcb
File diff suppressed because it is too large
View File

2383
PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.kicad_pcb-bak
File diff suppressed because it is too large
View File

414
PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.net

@ -0,0 +1,414 @@
(export (version D)
(design
(source /Users/mariolukas/Documents/KiCad/projects/Tonuino-ESP32_2/Tonuino-ESP32_2.sch)
(date "2020 October 11, Sunday 21:15:14")
(tool "Eeschema (5.1.7-0-10_14)")
(sheet (number 1) (name /) (tstamps /)
(title_block
(title)
(company)
(rev)
(date)
(source Tonuino-ESP32_2.sch)
(comment (number 1) (value ""))
(comment (number 2) (value ""))
(comment (number 3) (value ""))
(comment (number 4) (value "")))))
(components
(comp (ref U1)
(value ESP32-DEVKITC-32U)
(footprint ESP32-DEVKITC-32U:MODULE_ESP32-DEVKITC-32U)
(datasheet "Manufacturer Recommendations")
(fields
(field (name Field4) ESPRESSIF)
(field (name Field5) N/A))
(libsource (lib ESP32-DEVKITC-32U) (part ESP32-DEVKITC-32U) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F79096D))
(comp (ref J4)
(value "Rotary Encode")
(footprint mod_s:PinHeader_1x05_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x05_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F7BD0A2))
(comp (ref J1)
(value "SD-Card Reader")
(footprint mod_s:PinHeader_1x06_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x06_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F7ED32A))
(comp (ref J3)
(value "Adafruit MAX98357")
(footprint mod_s:PinHeader_1x07_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x07_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F81C714))
(comp (ref J5)
(value Next)
(footprint mod_s:PinHeader_1x02_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x02_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F9085EF))
(comp (ref J6)
(value Prev)
(footprint mod_s:PinHeader_1x02_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x02_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F90EAED))
(comp (ref J7)
(value Pause)
(footprint mod_s:PinHeader_1x02_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x02_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F90F726))
(comp (ref J2)
(value RFID-RC522)
(footprint mod_s:PinHeader_1x08_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x08_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F92011C))
(comp (ref J8)
(value WS2812)
(footprint mod_s:PinHeader_1x03_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x03_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F936A23))
(comp (ref J9)
(value Power)
(footprint mod_s:PinHeader_1x02_P2.54mm_Vertical)
(libsource (lib Connector) (part Conn_01x02_Male) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F945F11))
(comp (ref R5)
(value 4,7k)
(footprint adafruit:0207_9)
(libsource (lib Device) (part R) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F7995ED))
(comp (ref R4)
(value 10k)
(footprint adafruit:0207_9)
(libsource (lib Device) (part R) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F798E7D))
(comp (ref Q1)
(value BC337)
(footprint Package_TO_SOT_THT:TO-92_Inline_Wide)
(libsource (lib Transistor_BJT) (part BC337) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F78DA7C))
(comp (ref Q2)
(value IRF9540N)
(footprint Package_TO_SOT_THT:TO-220-3_Vertical)
(libsource (lib Transistor_FET) (part IRF9540N) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F78F572))
(comp (ref R1)
(value 10k)
(footprint adafruit:0207_9)
(libsource (lib Device) (part R) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5F79873B)))
(libparts
(libpart (lib Connector) (part Conn_01x02_Male)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x02_Male))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))))
(libpart (lib Connector) (part Conn_01x03_Male)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x03_Male))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))
(pin (num 3) (name Pin_3) (type passive))))
(libpart (lib Connector) (part Conn_01x05_Male)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x05_Male))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))
(pin (num 3) (name Pin_3) (type passive))
(pin (num 4) (name Pin_4) (type passive))
(pin (num 5) (name Pin_5) (type passive))))
(libpart (lib Connector) (part Conn_01x06_Male)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x06_Male))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))
(pin (num 3) (name Pin_3) (type passive))
(pin (num 4) (name Pin_4) (type passive))
(pin (num 5) (name Pin_5) (type passive))
(pin (num 6) (name Pin_6) (type passive))))
(libpart (lib Connector) (part Conn_01x07_Male)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x07_Male))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))
(pin (num 3) (name Pin_3) (type passive))
(pin (num 4) (name Pin_4) (type passive))
(pin (num 5) (name Pin_5) (type passive))
(pin (num 6) (name Pin_6) (type passive))
(pin (num 7) (name Pin_7) (type passive))))
(libpart (lib Connector) (part Conn_01x08_Male)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x08_Male))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))
(pin (num 3) (name Pin_3) (type passive))
(pin (num 4) (name Pin_4) (type passive))
(pin (num 5) (name Pin_5) (type passive))
(pin (num 6) (name Pin_6) (type passive))
(pin (num 7) (name Pin_7) (type passive))
(pin (num 8) (name Pin_8) (type passive))))
(libpart (lib Device) (part R)
(footprints
(fp R_*))
(fields
(field (name Reference) R)
(field (name Value) R))
(pins
(pin (num 1) (name ~) (type passive))
(pin (num 2) (name ~) (type passive))))
(libpart (lib ESP32-DEVKITC-32U) (part ESP32-DEVKITC-32U)
(fields
(field (name Reference) U)
(field (name Value) ESP32-DEVKITC-32U)
(field (name Footprint) MODULE_ESP32-DEVKITC-32U)
(field (name Datasheet) "Manufacturer Recommendations")
(field (name Field4) ESPRESSIF)
(field (name Field5) N/A))
(pins
(pin (num 1) (name 3V3) (type power_in))
(pin (num 2) (name EN) (type input))
(pin (num 3) (name SENSOR_VP) (type input))
(pin (num 4) (name SENSOR_VN) (type input))
(pin (num 5) (name IO34) (type BiDi))
(pin (num 6) (name IO35) (type BiDi))
(pin (num 7) (name IO32) (type BiDi))
(pin (num 8) (name IO33) (type BiDi))
(pin (num 9) (name IO25) (type BiDi))
(pin (num 10) (name IO26) (type BiDi))
(pin (num 11) (name IO27) (type BiDi))
(pin (num 12) (name IO14) (type BiDi))
(pin (num 13) (name IO12) (type BiDi))
(pin (num 14) (name GND1) (type power_in))
(pin (num 15) (name IO13) (type BiDi))
(pin (num 16) (name SD2) (type BiDi))
(pin (num 17) (name SD3) (type BiDi))
(pin (num 18) (name CMD) (type BiDi))
(pin (num 19) (name EXT_5V) (type power_in))
(pin (num 20) (name CLK) (type input))
(pin (num 21) (name SD0) (type BiDi))
(pin (num 22) (name SD1) (type BiDi))
(pin (num 23) (name IO15) (type BiDi))
(pin (num 24) (name IO2) (type BiDi))
(pin (num 25) (name IO0) (type BiDi))
(pin (num 26) (name IO4) (type BiDi))
(pin (num 27) (name IO16) (type BiDi))
(pin (num 28) (name IO17) (type BiDi))
(pin (num 29) (name IO5) (type BiDi))
(pin (num 30) (name IO18) (type BiDi))
(pin (num 31) (name IO19) (type BiDi))
(pin (num 32) (name GND2) (type power_in))
(pin (num 33) (name IO21) (type BiDi))
(pin (num 34) (name RXD0) (type input))
(pin (num 35) (name TXD0) (type output))
(pin (num 36) (name IO22) (type BiDi))
(pin (num 37) (name IO23) (type BiDi))
(pin (num 38) (name GND3) (type power_in))))
(libpart (lib Transistor_BJT) (part BC547)
(aliases
(alias BC546)
(alias BC548)
(alias BC549)
(alias BC550)
(alias BC337)
(alias BC338))
(footprints
(fp TO?92*))
(fields
(field (name Reference) Q)
(field (name Value) BC547)
(field (name Footprint) Package_TO_SOT_THT:TO-92_Inline))
(pins
(pin (num 1) (name C) (type passive))
(pin (num 2) (name B) (type input))
(pin (num 3) (name E) (type passive))))
(libpart (lib Transistor_FET) (part IRF9540N)
(aliases
(alias IRF4905)
(alias FQP27P06))
(footprints
(fp TO?220*))
(fields
(field (name Reference) Q)
(field (name Value) IRF9540N)
(field (name Footprint) Package_TO_SOT_THT:TO-220-3_Vertical))
(pins
(pin (num 1) (name G) (type input))
(pin (num 2) (name D) (type passive))
(pin (num 3) (name S) (type passive)))))
(libraries
(library (logical Connector)
(uri /Users/mariolukas/Documents/KiCad/libs/Connector.lib))
(library (logical Device)
(uri /Users/mariolukas/Documents/KiCad/libs/Device.lib))
(library (logical ESP32-DEVKITC-32U)
(uri /Users/mariolukas/Documents/KiCad/libs/ESP32-DEVKITC-32U.lib))
(library (logical Transistor_BJT)
(uri /Users/mariolukas/Documents/KiCad/libs/Transistor_BJT.lib))
(library (logical Transistor_FET)
(uri /Users/mariolukas/Documents/KiCad/libs/Transistor_FET.lib)))
(nets
(net (code 1) (name "Net-(U1-Pad2)")
(node (ref U1) (pin 2)))
(net (code 2) (name "Net-(U1-Pad3)")
(node (ref U1) (pin 3)))
(net (code 3) (name "Net-(U1-Pad4)")
(node (ref U1) (pin 4)))
(net (code 4) (name "Net-(U1-Pad16)")
(node (ref U1) (pin 16)))
(net (code 5) (name "Net-(U1-Pad17)")
(node (ref U1) (pin 17)))
(net (code 6) (name "Net-(U1-Pad18)")
(node (ref U1) (pin 18)))
(net (code 7) (name "Net-(U1-Pad20)")
(node (ref U1) (pin 20)))
(net (code 8) (name "Net-(U1-Pad21)")
(node (ref U1) (pin 21)))
(net (code 9) (name "Net-(U1-Pad22)")
(node (ref U1) (pin 22)))
(net (code 10) (name "Net-(U1-Pad24)")
(node (ref U1) (pin 24)))
(net (code 11) (name "Net-(U1-Pad25)")
(node (ref U1) (pin 25)))
(net (code 12) (name "Net-(U1-Pad34)")
(node (ref U1) (pin 34)))
(net (code 13) (name "Net-(U1-Pad35)")
(node (ref U1) (pin 35)))
(net (code 14) (name "Net-(J3-Pad3)")
(node (ref J3) (pin 3)))
(net (code 15) (name "Net-(J3-Pad4)")
(node (ref J3) (pin 4)))
(net (code 16) (name GPIO-17)
(node (ref J2) (pin 1))
(node (ref U1) (pin 28))
(node (ref R5) (pin 2)))
(net (code 17) (name SWITCHED_VCC)
(node (ref J8) (pin 3))
(node (ref J3) (pin 1))
(node (ref J1) (pin 2))
(node (ref Q2) (pin 2)))
(net (code 18) (name GND)
(node (ref U1) (pin 38))
(node (ref J4) (pin 1))
(node (ref Q1) (pin 3))
(node (ref R4) (pin 2))
(node (ref J1) (pin 1))
(node (ref J9) (pin 1))
(node (ref U1) (pin 32))
(node (ref J3) (pin 2))
(node (ref J8) (pin 1))
(node (ref J2) (pin 3))
(node (ref J7) (pin 1))
(node (ref J6) (pin 1))
(node (ref J5) (pin 1))
(node (ref U1) (pin 14)))
(net (code 19) (name 3.3V)
(node (ref U1) (pin 1))
(node (ref J4) (pin 2)))
(net (code 20) (name SD-CARD_MISO)
(node (ref J1) (pin 3))
(node (ref U1) (pin 27)))
(net (code 21) (name SD-CARD_MOSI)
(node (ref U1) (pin 15))
(node (ref J1) (pin 4)))
(net (code 22) (name SD-CARD_SCK)
(node (ref U1) (pin 12))
(node (ref J1) (pin 5)))
(net (code 23) (name SD-CARD_CS)
(node (ref J1) (pin 6))
(node (ref U1) (pin 23)))
(net (code 24) (name MAX98357_DIN)
(node (ref U1) (pin 9))
(node (ref J3) (pin 5)))
(net (code 25) (name MAX98357_BLCK)
(node (ref J3) (pin 6))
(node (ref U1) (pin 11)))
(net (code 26) (name MAX98357_LRC)
(node (ref U1) (pin 10))
(node (ref J3) (pin 7)))
(net (code 27) (name ROTARY_CLR)
(node (ref U1) (pin 5))
(node (ref J4) (pin 3)))
(net (code 28) (name ROTARY_DT)
(node (ref U1) (pin 6))
(node (ref J4) (pin 4)))
(net (code 29) (name ROTARY_BUTTON)
(node (ref J4) (pin 5))
(node (ref U1) (pin 7)))
(net (code 30) (name NEOPIXEL_DI)
(node (ref U1) (pin 13))
(node (ref J8) (pin 2)))
(net (code 31) (name VCC)
(node (ref U1) (pin 19))
(node (ref Q2) (pin 3))
(node (ref R1) (pin 1))
(node (ref J9) (pin 2)))
(net (code 32) (name BUTTON_PREVIOUS_PIN)
(node (ref J5) (pin 2))
(node (ref U1) (pin 8)))
(net (code 33) (name BUTTON_NEXT_PIN)
(node (ref U1) (pin 26))
(node (ref J6) (pin 2)))
(net (code 34) (name BUTTON_PLAY_PAUSE_PIN)
(node (ref U1) (pin 29))
(node (ref J7) (pin 2)))
(net (code 35) (name RFID_RST)
(node (ref J2) (pin 2))
(node (ref U1) (pin 36)))
(net (code 36) (name "Net-(J2-Pad4)")
(node (ref J2) (pin 4)))
(net (code 37) (name RFID_MISO)
(node (ref J2) (pin 5))
(node (ref U1) (pin 31)))
(net (code 38) (name RFID_MOSI)
(node (ref J2) (pin 6))
(node (ref U1) (pin 37)))
(net (code 39) (name RFID_SCK)
(node (ref J2) (pin 7))
(node (ref U1) (pin 30)))
(net (code 40) (name RFID_SDA)
(node (ref J2) (pin 8))
(node (ref U1) (pin 33)))
(net (code 41) (name "Net-(Q1-Pad2)")
(node (ref R4) (pin 1))
(node (ref R5) (pin 1))
(node (ref Q1) (pin 2)))
(net (code 42) (name "Net-(Q1-Pad1)")
(node (ref Q1) (pin 1))
(node (ref Q2) (pin 1))
(node (ref R1) (pin 2)))))

248
PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.pro

@ -0,0 +1,248 @@
update=2020 October 04, Sunday 16:49:12
version=1
last_client=kicad
[general]
version=1
RootSch=
BoardNm=
[cvpcb]
version=1
NetIExt=net
[eeschema]
version=1
LibDir=
[eeschema/libraries]
[schematic_editor]
version=1
PageLayoutDescrFile=
PlotDirectoryName=
SubpartIdSeparator=0
SubpartFirstId=65
NetFmtName=Pcbnew
SpiceAjustPassiveValues=0
LabSize=50
ERC_TestSimilarLabels=1
[pcbnew]
version=1
PageLayoutDescrFile=
LastNetListRead=
CopperLayerCount=2
BoardThickness=1.6
AllowMicroVias=0
AllowBlindVias=0
RequireCourtyardDefinitions=0
ProhibitOverlappingCourtyards=1
MinTrackWidth=0.2
MinViaDiameter=0.4
MinViaDrill=0.3
MinMicroViaDiameter=0.2
MinMicroViaDrill=0.09999999999999999
MinHoleToHole=0.25
TrackWidth1=0.4
ViaDiameter1=0.8
ViaDrill1=0.4
dPairWidth1=0.2
dPairGap1=0.25
dPairViaGap1=0.25
SilkLineWidth=0.12
SilkTextSizeV=1
SilkTextSizeH=1
SilkTextSizeThickness=0.15
SilkTextItalic=0
SilkTextUpright=1
CopperLineWidth=0.2
CopperTextSizeV=1.5
CopperTextSizeH=1.5
CopperTextThickness=0.3
CopperTextItalic=0
CopperTextUpright=1
EdgeCutLineWidth=0.05
CourtyardLineWidth=0.05
OthersLineWidth=0.15
OthersTextSizeV=1
OthersTextSizeH=1
OthersTextSizeThickness=0.15
OthersTextItalic=0
OthersTextUpright=1
SolderMaskClearance=0
SolderMaskMinWidth=0
SolderPasteClearance=0
SolderPasteRatio=-0
[pcbnew/Layer.F.Cu]
Name=F.Cu
Type=0
Enabled=1
[pcbnew/Layer.In1.Cu]
Name=In1.Cu
Type=0
Enabled=0
[pcbnew/Layer.In2.Cu]
Name=In2.Cu
Type=0
Enabled=0
[pcbnew/Layer.In3.Cu]
Name=In3.Cu
Type=0
Enabled=0
[pcbnew/Layer.In4.Cu]
Name=In4.Cu
Type=0
Enabled=0
[pcbnew/Layer.In5.Cu]
Name=In5.Cu
Type=0
Enabled=0
[pcbnew/Layer.In6.Cu]
Name=In6.Cu
Type=0
Enabled=0
[pcbnew/Layer.In7.Cu]
Name=In7.Cu
Type=0
Enabled=0
[pcbnew/Layer.In8.Cu]
Name=In8.Cu
Type=0
Enabled=0
[pcbnew/Layer.In9.Cu]
Name=In9.Cu
Type=0
Enabled=0
[pcbnew/Layer.In10.Cu]
Name=In10.Cu
Type=0
Enabled=0
[pcbnew/Layer.In11.Cu]
Name=In11.Cu
Type=0
Enabled=0
[pcbnew/Layer.In12.Cu]
Name=In12.Cu
Type=0
Enabled=0
[pcbnew/Layer.In13.Cu]
Name=In13.Cu
Type=0
Enabled=0
[pcbnew/Layer.In14.Cu]
Name=In14.Cu
Type=0
Enabled=0
[pcbnew/Layer.In15.Cu]
Name=In15.Cu
Type=0
Enabled=0
[pcbnew/Layer.In16.Cu]
Name=In16.Cu
Type=0
Enabled=0
[pcbnew/Layer.In17.Cu]
Name=In17.Cu
Type=0
Enabled=0
[pcbnew/Layer.In18.Cu]
Name=In18.Cu
Type=0
Enabled=0
[pcbnew/Layer.In19.Cu]
Name=In19.Cu
Type=0
Enabled=0
[pcbnew/Layer.In20.Cu]
Name=In20.Cu
Type=0
Enabled=0
[pcbnew/Layer.In21.Cu]
Name=In21.Cu
Type=0
Enabled=0
[pcbnew/Layer.In22.Cu]
Name=In22.Cu
Type=0
Enabled=0
[pcbnew/Layer.In23.Cu]
Name=In23.Cu
Type=0
Enabled=0
[pcbnew/Layer.In24.Cu]
Name=In24.Cu
Type=0
Enabled=0
[pcbnew/Layer.In25.Cu]
Name=In25.Cu
Type=0
Enabled=0
[pcbnew/Layer.In26.Cu]
Name=In26.Cu
Type=0
Enabled=0
[pcbnew/Layer.In27.Cu]
Name=In27.Cu
Type=0
Enabled=0
[pcbnew/Layer.In28.Cu]
Name=In28.Cu
Type=0
Enabled=0
[pcbnew/Layer.In29.Cu]
Name=In29.Cu
Type=0
Enabled=0
[pcbnew/Layer.In30.Cu]
Name=In30.Cu
Type=0
Enabled=0
[pcbnew/Layer.B.Cu]
Name=B.Cu
Type=0
Enabled=1
[pcbnew/Layer.B.Adhes]
Enabled=1
[pcbnew/Layer.F.Adhes]
Enabled=1
[pcbnew/Layer.B.Paste]
Enabled=1
[pcbnew/Layer.F.Paste]
Enabled=1
[pcbnew/Layer.B.SilkS]
Enabled=1
[pcbnew/Layer.F.SilkS]
Enabled=1
[pcbnew/Layer.B.Mask]
Enabled=1
[pcbnew/Layer.F.Mask]
Enabled=1
[pcbnew/Layer.Dwgs.User]
Enabled=1
[pcbnew/Layer.Cmts.User]
Enabled=1
[pcbnew/Layer.Eco1.User]
Enabled=1
[pcbnew/Layer.Eco2.User]
Enabled=1
[pcbnew/Layer.Edge.Cuts]
Enabled=1
[pcbnew/Layer.Margin]
Enabled=1
[pcbnew/Layer.B.CrtYd]
Enabled=1
[pcbnew/Layer.F.CrtYd]
Enabled=1
[pcbnew/Layer.B.Fab]
Enabled=1
[pcbnew/Layer.F.Fab]
Enabled=1
[pcbnew/Layer.Rescue]
Enabled=0
[pcbnew/Netclasses]
[pcbnew/Netclasses/Default]
Name=Default
Clearance=0.2
TrackWidth=0.4
ViaDiameter=0.8
ViaDrill=0.4
uViaDiameter=0.3
uViaDrill=0.1
dPairWidth=0.2
dPairGap=0.25
dPairViaGap=0.25

585
PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.sch

@ -0,0 +1,585 @@
EESchema Schematic File Version 4
EELAYER 30 0
EELAYER END
$Descr A4 11693 8268
encoding utf-8
Sheet 1 1
Title ""
Date ""
Rev ""
Comp ""
Comment1 ""
Comment2 ""
Comment3 ""
Comment4 ""
$EndDescr
$Comp
L ESP32-DEVKITC-32U:ESP32-DEVKITC-32U U1
U 1 1 5F79096D
P 8100 2650
F 0 "U1" H 8100 3817 50 0000 C CNN
F 1 "ESP32-DEVKITC-32U" H 8100 3726 50 0000 C CNN
F 2 "ESP32-DEVKITC-32U:MODULE_ESP32-DEVKITC-32U" H 8100 2650 50 0001 L BNN
F 3 "Manufacturer Recommendations" H 8100 2650 50 0001 L BNN
F 4 "ESPRESSIF" H 8100 2650 50 0001 L BNN "Field4"
F 5 "N/A" H 8100 2650 50 0001 L BNN "Field5"
1 8100 2650
1 0 0 -1
$EndComp
$Comp
L Connector:Conn_01x05_Male J4
U 1 1 5F7BD0A2
P 2550 6550
F 0 "J4" H 2658 6931 50 0000 C CNN
F 1 "Rotary Encode" H 2658 6840 50 0000 C CNN
F 2 "mod_s:PinHeader_1x05_P2.54mm_Vertical" H 2550 6550 50 0001 C CNN
F 3 "" H 2550 6550 50 0001 C CNN
1 2550 6550
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x06_Male J1
U 1 1 5F7ED32A
P 6250 4700
F 0 "J1" V 6312 4944 50 0000 L CNN
F 1 "SD-Card Reader" V 6403 4944 50 0000 L CNN
F 2 "mod_s:PinHeader_1x06_P2.54mm_Vertical" H 6250 4700 50 0001 C CNN
F 3 "" H 6250 4700 50 0001 C CNN
1 6250 4700
0 1 1 0
$EndComp
Text Notes 4200 4300 0 197 ~ 0
Breakout Boards\n
Text Notes 850 4300 0 197 ~ 0
Inputs Interfaces
Text Notes 850 1400 0 197 ~ 0
Switched 5V (Deep Sleep)\n
Text Notes 7500 1250 0 197 ~ 0
ESP-32\n
$Comp
L Connector:Conn_01x07_Male J3
U 1 1 5F81C714
P 4650 6450
F 0 "J3" V 4485 6428 50 0000 C CNN
F 1 "Adafruit MAX98357" V 4576 6428 50 0000 C CNN
F 2 "mod_s:PinHeader_1x07_P2.54mm_Vertical" H 4650 6450 50 0001 C CNN
F 3 "" H 4650 6450 50 0001 C CNN
1 4650 6450
0 1 1 0
$EndComp
Text GLabel 9050 2750 2 50 Input ~ 0
GPIO-17
Wire Wire Line
4950 6850 4950 6650
Text GLabel 6350 5050 3 50 Input ~ 0
SWITCHED_VCC
Wire Wire Line
6350 5050 6350 4900
$Comp
L power:GND #PWR0103
U 1 1 5F829524
P 6600 5100
F 0 "#PWR0103" H 6600 4850 50 0001 C CNN
F 1 "GND" H 6605 4927 50 0000 C CNN
F 2 "" H 6600 5100 50 0001 C CNN
F 3 "" H 6600 5100 50 0001 C CNN
1 6600 5100
1 0 0 -1
$EndComp
Wire Wire Line
6600 5100 6450 5100
Wire Wire Line
6450 5100 6450 4900
Text GLabel 7050 1750 0 50 Input ~ 0
3.3V
Wire Wire Line
7300 1750 7050 1750
Wire Wire Line
8900 2750 9050 2750
Text GLabel 5050 5250 3 50 Input ~ 0
GPIO-17
$Comp
L power:GND #PWR0104
U 1 1 5F82B98B
P 4850 5250
F 0 "#PWR0104" H 4850 5000 50 0001 C CNN
F 1 "GND" H 4855 5077 50 0000 C CNN
F 2 "" H 4850 5250 50 0001 C CNN
F 3 "" H 4850 5250 50 0001 C CNN
1 4850 5250
1 0 0 -1
$EndComp
Text GLabel 6250 5050 3 50 Input ~ 0
SD-CARD_MISO
Text GLabel 6150 5050 3 50 Input ~ 0
SD-CARD_MOSI
Wire Wire Line
6250 5050 6250 4900
Wire Wire Line
6150 5050 6150 4900
Text GLabel 6050 5050 3 50 Input ~ 0
SD-CARD_SCK
Text GLabel 5950 5050 3 50 Input ~ 0
SD-CARD_CS
Wire Wire Line
6050 5050 6050 4900
Wire Wire Line
5950 5050 5950 4900
Text GLabel 4950 5250 3 50 Input ~ 0
RFID_RST
Text GLabel 4350 5250 3 50 Input ~ 0
RFID_SDA
Text GLabel 4450 5250 3 50 Input ~ 0
RFID_SCK
Text GLabel 4550 5250 3 50 Input ~ 0
RFID_MOSI
Text GLabel 4650 5250 3 50 Input ~ 0
RFID_MISO
Wire Wire Line
4850 6750 4850 6650
Text GLabel 4550 6850 3 50 Input ~ 0
MAX98357_DIN
Wire Wire Line
4550 6850 4550 6650
Text GLabel 4450 6850 3 50 Input ~ 0
MAX98357_BLCK
Wire Wire Line
4450 6850 4450 6650
Text GLabel 4350 6850 3 50 Input ~ 0
MAX98357_LRC
Wire Wire Line
4350 6850 4350 6650
Text GLabel 2550 6900 3 50 Input ~ 0
ROTARY_CLR
Text GLabel 2450 6900 3 50 Input ~ 0
ROTARY_DT
Text GLabel 2350 6900 3 50 Input ~ 0
ROTARY_BUTTON
Text GLabel 2650 6900 3 50 Input ~ 0
3.3V
Wire Wire Line
2750 6900 2750 6750
Wire Wire Line
2650 6900 2650 6750
Wire Wire Line
2450 6900 2450 6750
Text GLabel 2500 5100 3 50 Input ~ 0
BUTTON_PLAY_PAUSE_PIN
Text GLabel 2100 5100 3 50 Input ~ 0
BUTTON_NEXT_PIN
Text GLabel 1600 5100 3 50 Input ~ 0
BUTTON_PREVIOUS_PIN
$Comp
L power:GND #PWR0106
U 1 1 5F842383
P 1700 5100
F 0 "#PWR0106" H 1700 4850 50 0001 C CNN
F 1 "GND" H 1705 4927 50 0000 C CNN
F 2 "" H 1700 5100 50 0001 C CNN
F 3 "" H 1700 5100 50 0001 C CNN
1 1700 5100
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0107
U 1 1 5F8429CB
P 2200 5100
F 0 "#PWR0107" H 2200 4850 50 0001 C CNN
F 1 "GND" H 2205 4927 50 0000 C CNN
F 2 "" H 2200 5100 50 0001 C CNN
F 3 "" H 2200 5100 50 0001 C CNN
1 2200 5100
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0108
U 1 1 5F843BDD
P 2600 5100
F 0 "#PWR0108" H 2600 4850 50 0001 C CNN
F 1 "GND" H 2605 4927 50 0000 C CNN
F 2 "" H 2600 5100 50 0001 C CNN
F 3 "" H 2600 5100 50 0001 C CNN
1 2600 5100
1 0 0 -1
$EndComp
Text GLabel 5750 6900 3 50 Input ~ 0
NEOPIXEL_DI
Wire Wire Line
5650 6900 5650 6750
Wire Wire Line
5750 6900 5750 6750
Wire Wire Line
5850 6900 5850 6750
Text GLabel 9050 1950 2 50 Input ~ 0
RFID_RST
Wire Wire Line
8900 1950 9050 1950
Text GLabel 9050 2250 2 50 Input ~ 0
RFID_SDA
Wire Wire Line
8900 2250 9050 2250
Text GLabel 9050 1850 2 50 Input ~ 0
RFID_MOSI
Wire Wire Line
8900 1850 9050 1850
Text GLabel 9050 2450 2 50 Input ~ 0
RFID_MISO
Wire Wire Line
9050 2450 8900 2450
Text GLabel 9050 2550 2 50 Input ~ 0
RFID_SCK
Wire Wire Line
8900 2550 9050 2550
$Comp
L power:GND #PWR0110
U 1 1 5F85DB8C
P 9050 1750
F 0 "#PWR0110" H 9050 1500 50 0001 C CNN
F 1 "GND" V 9050 1600 50 0000 R CNN
F 2 "" H 9050 1750 50 0001 C CNN
F 3 "" H 9050 1750 50 0001 C CNN
1 9050 1750
0 -1 -1 0
$EndComp
Wire Wire Line
8900 2350 9050 2350
Wire Wire Line
9050 1750 8900 1750
Text GLabel 7050 2550 0 50 Input ~ 0
MAX98357_DIN
Text GLabel 7050 2750 0 50 Input ~ 0
MAX98357_BLCK
Text GLabel 7050 2650 0 50 Input ~ 0
MAX98357_LRC
Wire Wire Line
7050 2550 7300 2550
Wire Wire Line
7050 2650 7300 2650
Wire Wire Line
7050 2750 7300 2750
Text GLabel 9050 3250 2 50 Input ~ 0
SD-CARD_CS
Text GLabel 7050 2850 0 50 Input ~ 0
SD-CARD_SCK
Text GLabel 7050 3150 0 50 Input ~ 0
SD-CARD_MOSI
Text GLabel 9050 2850 2 50 Input ~ 0
SD-CARD_MISO
Wire Wire Line
9050 2850 8900 2850
Wire Wire Line
9050 3250 8900 3250
Wire Wire Line
7300 3150 7050 3150
Wire Wire Line
7050 2850 7300 2850
Text GLabel 7050 2450 0 50 Input ~ 0
BUTTON_PREVIOUS_PIN
Text GLabel 9050 2950 2 50 Input ~ 0
BUTTON_NEXT_PIN
Text GLabel 9050 2650 2 50 Input ~ 0
BUTTON_PLAY_PAUSE_PIN
Wire Wire Line
8900 2650 9050 2650
Wire Wire Line
8900 2950 9050 2950
Wire Wire Line
7300 2450 7050 2450
Text GLabel 7050 2150 0 50 Input ~ 0
ROTARY_CLR
Text GLabel 7050 2250 0 50 Input ~ 0
ROTARY_DT
Text GLabel 7050 2350 0 50 Input ~ 0
ROTARY_BUTTON
Wire Wire Line
7050 2350 7300 2350
Wire Wire Line
7050 2250 7300 2250
Wire Wire Line
7050 2150 7300 2150
Text GLabel 7050 2950 0 50 Input ~ 0
NEOPIXEL_DI
Wire Wire Line
7050 2950 7300 2950
$Comp
L power:VCC #PWR0112
U 1 1 5F87B792
P 7000 3550
F 0 "#PWR0112" H 7000 3400 50 0001 C CNN
F 1 "VCC" H 7015 3723 50 0000 C CNN
F 2 "" H 7000 3550 50 0001 C CNN
F 3 "" H 7000 3550 50 0001 C CNN
1 7000 3550
0 -1 -1 0
$EndComp
Wire Wire Line
7300 3550 7000 3550
Text GLabel 5650 6900 3 50 Input ~ 0
SWITCHED_VCC
$Comp
L power:VCC #PWR0114
U 1 1 5F8848E9
P 8750 4900
F 0 "#PWR0114" H 8750 4750 50 0001 C CNN
F 1 "VCC" H 8765 5073 50 0000 C CNN
F 2 "" H 8750 4900 50 0001 C CNN
F 3 "" H 8750 4900 50 0001 C CNN
1 8750 4900
-1 0 0 1
$EndComp
$Comp
L power:GND #PWR0115
U 1 1 5F885DD6
P 8850 4900
F 0 "#PWR0115" H 8850 4650 50 0001 C CNN
F 1 "GND" H 8855 4727 50 0000 C CNN
F 2 "" H 8850 4900 50 0001 C CNN
F 3 "" H 8850 4900 50 0001 C CNN
1 8850 4900
1 0 0 -1
$EndComp
Text Notes 7750 4300 0 197 ~ 0
Power Supply\n
Wire Wire Line
2550 6900 2550 6750
Wire Wire Line
2350 6900 2350 6750
$Comp
L power:GND #PWR0105
U 1 1 5F872A11
P 9050 2350
F 0 "#PWR0105" H 9050 2100 50 0001 C CNN
F 1 "GND" V 9055 2222 50 0000 R CNN
F 2 "" H 9050 2350 50 0001 C CNN
F 3 "" H 9050 2350 50 0001 C CNN
1 9050 2350
0 -1 -1 0
$EndComp
$Comp
L power:GND #PWR0109
U 1 1 5F8787E6
P 2750 6900
F 0 "#PWR0109" H 2750 6650 50 0001 C CNN
F 1 "GND" V 2750 6750 50 0000 R CNN
F 2 "" H 2750 6900 50 0001 C CNN
F 3 "" H 2750 6900 50 0001 C CNN
1 2750 6900
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0111
U 1 1 5F878CF4
P 7300 3050
F 0 "#PWR0111" H 7300 2800 50 0001 C CNN
F 1 "GND" V 7300 2900 50 0000 R CNN
F 2 "" H 7300 3050 50 0001 C CNN
F 3 "" H 7300 3050 50 0001 C CNN
1 7300 3050
0 1 1 0
$EndComp
$Comp
L power:GND #PWR0117
U 1 1 5F885952
P 4850 6750
F 0 "#PWR0117" H 4850 6500 50 0001 C CNN
F 1 "GND" V 4850 6600 50 0000 R CNN
F 2 "" H 4850 6750 50 0001 C CNN
F 3 "" H 4850 6750 50 0001 C CNN
1 4850 6750
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0118
U 1 1 5F8874D1
P 5850 6900
F 0 "#PWR0118" H 5850 6650 50 0001 C CNN
F 1 "GND" V 5850 6750 50 0000 R CNN
F 2 "" H 5850 6900 50 0001 C CNN
F 3 "" H 5850 6900 50 0001 C CNN
1 5850 6900
1 0 0 -1
$EndComp
Text GLabel 4950 6850 3 50 Input ~ 0
SWITCHED_VCC
$Comp
L Connector:Conn_01x02_Male J5
U 1 1 5F9085EF
P 1700 4900
F 0 "J5" V 1762 4944 50 0000 L CNN
F 1 "Next" V 1853 4944 50 0000 L CNN
F 2 "mod_s:PinHeader_1x02_P2.54mm_Vertical" H 1700 4900 50 0001 C CNN
F 3 "" H 1700 4900 50 0001 C CNN
1 1700 4900
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x02_Male J6
U 1 1 5F90EAED
P 2200 4900
F 0 "J6" V 2262 4944 50 0000 L CNN
F 1 "Prev" V 2353 4944 50 0000 L CNN
F 2 "mod_s:PinHeader_1x02_P2.54mm_Vertical" H 2200 4900 50 0001 C CNN
F 3 "" H 2200 4900 50 0001 C CNN
1 2200 4900
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x02_Male J7
U 1 1 5F90F726
P 2600 4900
F 0 "J7" V 2662 4944 50 0000 L CNN
F 1 "Pause" V 2753 4944 50 0000 L CNN
F 2 "mod_s:PinHeader_1x02_P2.54mm_Vertical" H 2600 4900 50 0001 C CNN
F 3 "" H 2600 4900 50 0001 C CNN
1 2600 4900
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x08_Male J2
U 1 1 5F92011C
P 4750 5050
F 0 "J2" V 4585 4978 50 0000 C CNN
F 1 "RFID-RC522" V 4676 4978 50 0000 C CNN
F 2 "mod_s:PinHeader_1x08_P2.54mm_Vertical" H 4750 5050 50 0001 C CNN
F 3 "" H 4750 5050 50 0001 C CNN
1 4750 5050
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x03_Male J8
U 1 1 5F936A23
P 5750 6550
F 0 "J8" V 5812 6694 50 0000 L CNN
F 1 "WS2812" V 5903 6694 50 0000 L CNN
F 2 "mod_s:PinHeader_1x03_P2.54mm_Vertical" H 5750 6550 50 0001 C CNN
F 3 "" H 5750 6550 50 0001 C CNN
1 5750 6550
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x02_Male J9
U 1 1 5F945F11
P 8850 4700
F 0 "J9" V 8912 4744 50 0000 L CNN
F 1 "Power" V 9003 4744 50 0000 L CNN
F 2 "mod_s:PinHeader_1x02_P2.54mm_Vertical" H 8850 4700 50 0001 C CNN
F 3 "" H 8850 4700 50 0001 C CNN
1 8850 4700
0 1 1 0
$EndComp
Wire Wire Line
1600 2850 1850 2850
Text GLabel 1600 2850 0 50 Input ~ 0
GPIO-17
Wire Wire Line
2300 2400 2300 2550
$Comp
L power:GND #PWR0102
U 1 1 5F7B65C3
P 2300 2400
F 0 "#PWR0102" H 2300 2150 50 0001 C CNN
F 1 "GND" H 2305 2227 50 0000 C CNN
F 2 "" H 2300 2400 50 0001 C CNN
F 3 "" H 2300 2400 50 0001 C CNN
1 2300 2400
-1 0 0 1
$EndComp
Connection ~ 2300 2850
Wire Wire Line
2300 2850 2150 2850
Wire Wire Line
2450 2850 2300 2850
$Comp
L Device:R R5
U 1 1 5F7995ED
P 2000 2850
F 0 "R5" H 2070 2896 50 0000 L CNN
F 1 "4,7k" H 2070 2805 50 0000 L CNN
F 2 "adafruit:0207_9" V 1930 2850 50 0001 C CNN
F 3 "" H 2000 2850 50 0001 C CNN
1 2000 2850
0 1 1 0
$EndComp
$Comp
L Device:R R4
U 1 1 5F798E7D
P 2300 2700
F 0 "R4" H 2370 2746 50 0000 L CNN
F 1 "10k" H 2370 2655 50 0000 L CNN
F 2 "adafruit:0207_9" V 2230 2700 50 0001 C CNN
F 3 "" H 2300 2700 50 0001 C CNN
1 2300 2700
-1 0 0 1
$EndComp
$Comp
L Transistor_BJT:BC337 Q1
U 1 1 5F78DA7C
P 2650 2850
F 0 "Q1" H 2841 2896 50 0000 L CNN
F 1 "BC337" H 2841 2805 50 0000 L CNN
F 2 "Package_TO_SOT_THT:TO-92_Inline_Wide" H 2850 2775 50 0001 L CIN
F 3 "" H 2650 2850 50 0001 L CNN
1 2650 2850
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0101
U 1 1 5F7B5A15
P 2750 3300
F 0 "#PWR0101" H 2750 3050 50 0001 C CNN
F 1 "GND" H 2755 3127 50 0000 C CNN
F 2 "" H 2750 3300 50 0001 C CNN
F 3 "" H 2750 3300 50 0001 C CNN
1 2750 3300
1 0 0 -1
$EndComp
Wire Wire Line
2750 3050 2750 3300
$Comp
L Transistor_FET:IRF9540N Q2
U 1 1 5F78F572
P 2750 2100
F 0 "Q2" H 2954 2146 50 0000 L CNN
F 1 "IRF9540N" H 2954 2055 50 0000 L CNN
F 2 "Package_TO_SOT_THT:TO-220-3_Vertical" H 2950 2025 50 0001 L CIN
F 3 "" H 2750 2100 50 0001 L CNN
1 2750 2100
0 -1 -1 0
$EndComp
$Comp
L Device:R R1
U 1 1 5F79873B
P 3050 2150
F 0 "R1" H 3120 2196 50 0000 L CNN
F 1 "10k" H 3120 2105 50 0000 L CNN
F 2 "adafruit:0207_9" V 2980 2150 50 0001 C CNN
F 3 "" H 3050 2150 50 0001 C CNN
1 3050 2150
1 0 0 -1
$EndComp
Wire Wire Line
3050 2000 2950 2000
Wire Wire Line
2750 2300 3050 2300
Wire Wire Line
3300 2000 3050 2000
$Comp
L power:VCC #PWR0113
U 1 1 5F87F1D8
P 3300 2000
F 0 "#PWR0113" H 3300 1850 50 0001 C CNN
F 1 "VCC" V 3315 2127 50 0000 L CNN
F 2 "" H 3300 2000 50 0001 C CNN
F 3 "" H 3300 2000 50 0001 C CNN
1 3300 2000
0 1 1 0
$EndComp
Text GLabel 2350 2000 0 50 Input ~ 0
SWITCHED_VCC
Wire Wire Line
2350 2000 2550 2000
Connection ~ 3050 2000
Wire Wire Line
2750 2300 2750 2650
Connection ~ 2750 2300
$EndSCHEMATC

585
PCBs/ESP32_Breakout_Carrier/Tonuino-ESP32_2.sch-bak

@ -0,0 +1,585 @@
EESchema Schematic File Version 4
EELAYER 30 0
EELAYER END
$Descr A4 11693 8268
encoding utf-8
Sheet 1 1
Title ""
Date ""
Rev ""
Comp ""
Comment1 ""
Comment2 ""
Comment3 ""
Comment4 ""
$EndDescr
$Comp
L ESP32-DEVKITC-32U:ESP32-DEVKITC-32U U1
U 1 1 5F79096D
P 8100 2650
F 0 "U1" H 8100 3817 50 0000 C CNN
F 1 "ESP32-DEVKITC-32U" H 8100 3726 50 0000 C CNN
F 2 "ESP32-DEVKITC-32U:MODULE_ESP32-DEVKITC-32U" H 8100 2650 50 0001 L BNN
F 3 "Manufacturer Recommendations" H 8100 2650 50 0001 L BNN
F 4 "ESPRESSIF" H 8100 2650 50 0001 L BNN "Field4"
F 5 "N/A" H 8100 2650 50 0001 L BNN "Field5"
1 8100 2650
1 0 0 -1
$EndComp
$Comp
L Connector:Conn_01x05_Male J4
U 1 1 5F7BD0A2
P 2550 6550
F 0 "J4" H 2658 6931 50 0000 C CNN
F 1 "Rotary Encode" H 2658 6840 50 0000 C CNN
F 2 "mod_s:PinHeader_1x05_P2.54mm_Vertical" H 2550 6550 50 0001 C CNN
F 3 "" H 2550 6550 50 0001 C CNN
1 2550 6550
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x06_Male J1
U 1 1 5F7ED32A
P 6250 4700
F 0 "J1" V 6312 4944 50 0000 L CNN
F 1 "SD-Card Reader" V 6403 4944 50 0000 L CNN
F 2 "mod_s:PinHeader_1x06_P2.54mm_Vertical" H 6250 4700 50 0001 C CNN
F 3 "" H 6250 4700 50 0001 C CNN
1 6250 4700
0 1 1 0
$EndComp
Text Notes 4200 4300 0 197 ~ 0
Breakout Boards\n
Text Notes 850 4300 0 197 ~ 0
Inputs Interfaces
Text Notes 850 1400 0 197 ~ 0
Switched 5V (Deep Sleep)\n
Text Notes 7500 1250 0 197 ~ 0
ESP-32\n
$Comp
L Connector:Conn_01x07_Male J3
U 1 1 5F81C714
P 4650 6450
F 0 "J3" V 4485 6428 50 0000 C CNN
F 1 "Adafruit MAX98357" V 4576 6428 50 0000 C CNN
F 2 "mod_s:PinHeader_1x07_P2.54mm_Vertical" H 4650 6450 50 0001 C CNN
F 3 "" H 4650 6450 50 0001 C CNN
1 4650 6450
0 1 1 0
$EndComp
Text GLabel 9050 2750 2 50 Input ~ 0
GPIO-17
Wire Wire Line
4950 6850 4950 6650
Text GLabel 6350 5050 3 50 Input ~ 0
SWITCHED_VCC
Wire Wire Line
6350 5050 6350 4900
$Comp
L power:GND #PWR0103
U 1 1 5F829524
P 6600 5100
F 0 "#PWR0103" H 6600 4850 50 0001 C CNN
F 1 "GND" H 6605 4927 50 0000 C CNN
F 2 "" H 6600 5100 50 0001 C CNN
F 3 "" H 6600 5100 50 0001 C CNN
1 6600 5100
1 0 0 -1
$EndComp
Wire Wire Line
6600 5100 6450 5100
Wire Wire Line
6450 5100 6450 4900
Text GLabel 7050 1750 0 50 Input ~ 0
3.3V
Wire Wire Line
7300 1750 7050 1750
Wire Wire Line
8900 2750 9050 2750
Text GLabel 5050 5250 3 50 Input ~ 0
GPIO-17
$Comp
L power:GND #PWR0104
U 1 1 5F82B98B
P 4850 5250
F 0 "#PWR0104" H 4850 5000 50 0001 C CNN
F 1 "GND" H 4855 5077 50 0000 C CNN
F 2 "" H 4850 5250 50 0001 C CNN
F 3 "" H 4850 5250 50 0001 C CNN
1 4850 5250
1 0 0 -1
$EndComp
Text GLabel 6250 5050 3 50 Input ~ 0
SD-CARD_MISO
Text GLabel 6150 5050 3 50 Input ~ 0
SD-CARD_MOSI
Wire Wire Line
6250 5050 6250 4900
Wire Wire Line
6150 5050 6150 4900
Text GLabel 6050 5050 3 50 Input ~ 0
SD-CARD_SCK
Text GLabel 5950 5050 3 50 Input ~ 0
SD-CARD_CS
Wire Wire Line
6050 5050 6050 4900
Wire Wire Line
5950 5050 5950 4900
Text GLabel 4950 5250 3 50 Input ~ 0
RFID_RST
Text GLabel 4350 5250 3 50 Input ~ 0
RFID_SDA
Text GLabel 4450 5250 3 50 Input ~ 0
RFID_SCK
Text GLabel 4550 5250 3 50 Input ~ 0
RFID_MOSI
Text GLabel 4650 5250 3 50 Input ~ 0
RFID_MISO
Wire Wire Line
4850 6750 4850 6650
Text GLabel 4550 6850 3 50 Input ~ 0
MAX98357_DIN
Wire Wire Line
4550 6850 4550 6650
Text GLabel 4450 6850 3 50 Input ~ 0
MAX98357_BLCK
Wire Wire Line
4450 6850 4450 6650
Text GLabel 4350 6850 3 50 Input ~ 0
MAX98357_LRC
Wire Wire Line
4350 6850 4350 6650
Text GLabel 2550 6900 3 50 Input ~ 0
ROTARY_CLR
Text GLabel 2450 6900 3 50 Input ~ 0
ROTARY_DT
Text GLabel 2350 6900 3 50 Input ~ 0
ROTARY_BUTTON
Text GLabel 2650 6900 3 50 Input ~ 0
3.3V
Wire Wire Line
2750 6900 2750 6750
Wire Wire Line
2650 6900 2650 6750
Wire Wire Line
2450 6900 2450 6750
Text GLabel 2500 5100 3 50 Input ~ 0
BUTTON_PLAY_PAUSE_PIN
Text GLabel 2100 5100 3 50 Input ~ 0
BUTTON_NEXT_PIN
Text GLabel 1600 5100 3 50 Input ~ 0
BUTTON_PREVIOUS_PIN
$Comp
L power:GND #PWR0106
U 1 1 5F842383
P 1700 5100
F 0 "#PWR0106" H 1700 4850 50 0001 C CNN
F 1 "GND" H 1705 4927 50 0000 C CNN
F 2 "" H 1700 5100 50 0001 C CNN
F 3 "" H 1700 5100 50 0001 C CNN
1 1700 5100
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0107
U 1 1 5F8429CB
P 2200 5100
F 0 "#PWR0107" H 2200 4850 50 0001 C CNN
F 1 "GND" H 2205 4927 50 0000 C CNN
F 2 "" H 2200 5100 50 0001 C CNN
F 3 "" H 2200 5100 50 0001 C CNN
1 2200 5100
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0108
U 1 1 5F843BDD
P 2600 5100
F 0 "#PWR0108" H 2600 4850 50 0001 C CNN
F 1 "GND" H 2605 4927 50 0000 C CNN
F 2 "" H 2600 5100 50 0001 C CNN
F 3 "" H 2600 5100 50 0001 C CNN
1 2600 5100
1 0 0 -1
$EndComp
Text GLabel 5750 6900 3 50 Input ~ 0
NEOPIXEL_DI
Wire Wire Line
5650 6900 5650 6750
Wire Wire Line
5750 6900 5750 6750
Wire Wire Line
5850 6900 5850 6750
Text GLabel 9050 1950 2 50 Input ~ 0
RFID_RST
Wire Wire Line
8900 1950 9050 1950
Text GLabel 9050 2250 2 50 Input ~ 0
RFID_SDA
Wire Wire Line
8900 2250 9050 2250
Text GLabel 9050 1850 2 50 Input ~ 0
RFID_MOSI
Wire Wire Line
8900 1850 9050 1850
Text GLabel 9050 2450 2 50 Input ~ 0
RFID_MISO
Wire Wire Line
9050 2450 8900 2450
Text GLabel 9050 2550 2 50 Input ~ 0
RFID_SCK
Wire Wire Line
8900 2550 9050 2550
$Comp
L power:GND #PWR0110
U 1 1 5F85DB8C
P 9050 1750
F 0 "#PWR0110" H 9050 1500 50 0001 C CNN
F 1 "GND" V 9050 1600 50 0000 R CNN
F 2 "" H 9050 1750 50 0001 C CNN
F 3 "" H 9050 1750 50 0001 C CNN
1 9050 1750
0 -1 -1 0
$EndComp
Wire Wire Line
8900 2350 9050 2350
Wire Wire Line
9050 1750 8900 1750
Text GLabel 7050 2550 0 50 Input ~ 0
MAX98357_DIN
Text GLabel 7050 2750 0 50 Input ~ 0
MAX98357_BLCK
Text GLabel 7050 2650 0 50 Input ~ 0
MAX98357_LRC
Wire Wire Line
7050 2550 7300 2550
Wire Wire Line
7050 2650 7300 2650
Wire Wire Line
7050 2750 7300 2750
Text GLabel 9050 3250 2 50 Input ~ 0
SD-CARD_CS
Text GLabel 7050 2850 0 50 Input ~ 0
SD-CARD_SCK
Text GLabel 7050 3150 0 50 Input ~ 0
SD-CARD_MOSI
Text GLabel 9050 2850 2 50 Input ~ 0
SD-CARD_MISO
Wire Wire Line
9050 2850 8900 2850
Wire Wire Line
9050 3250 8900 3250
Wire Wire Line
7300 3150 7050 3150
Wire Wire Line
7050 2850 7300 2850
Text GLabel 7050 2450 0 50 Input ~ 0
BUTTON_PREVIOUS_PIN
Text GLabel 9050 2950 2 50 Input ~ 0
BUTTON_NEXT_PIN
Text GLabel 9050 2650 2 50 Input ~ 0
BUTTON_PLAY_PAUSE_PIN
Wire Wire Line
8900 2650 9050 2650
Wire Wire Line
8900 2950 9050 2950
Wire Wire Line
7300 2450 7050 2450
Text GLabel 7050 2150 0 50 Input ~ 0
ROTARY_CLR
Text GLabel 7050 2250 0 50 Input ~ 0
ROTARY_DT
Text GLabel 7050 2350 0 50 Input ~ 0
ROTARY_BUTTON
Wire Wire Line
7050 2350 7300 2350
Wire Wire Line
7050 2250 7300 2250
Wire Wire Line
7050 2150 7300 2150
Text GLabel 7050 2950 0 50 Input ~ 0
NEOPIXEL_DI
Wire Wire Line
7050 2950 7300 2950
$Comp
L power:VCC #PWR0112
U 1 1 5F87B792
P 7000 3550
F 0 "#PWR0112" H 7000 3400 50 0001 C CNN
F 1 "VCC" H 7015 3723 50 0000 C CNN
F 2 "" H 7000 3550 50 0001 C CNN
F 3 "" H 7000 3550 50 0001 C CNN
1 7000 3550
0 -1 -1 0
$EndComp
Wire Wire Line
7300 3550 7000 3550
Text GLabel 5650 6900 3 50 Input ~ 0
SWITCHED_VCC
$Comp
L power:VCC #PWR0114
U 1 1 5F8848E9
P 8750 4900
F 0 "#PWR0114" H 8750 4750 50 0001 C CNN
F 1 "VCC" H 8765 5073 50 0000 C CNN
F 2 "" H 8750 4900 50 0001 C CNN
F 3 "" H 8750 4900 50 0001 C CNN
1 8750 4900
-1 0 0 1
$EndComp
$Comp
L power:GND #PWR0115
U 1 1 5F885DD6
P 8850 4900
F 0 "#PWR0115" H 8850 4650 50 0001 C CNN
F 1 "GND" H 8855 4727 50 0000 C CNN
F 2 "" H 8850 4900 50 0001 C CNN
F 3 "" H 8850 4900 50 0001 C CNN
1 8850 4900
1 0 0 -1
$EndComp
Text Notes 7750 4300 0 197 ~ 0
Power Supply\n
Wire Wire Line
2550 6900 2550 6750
Wire Wire Line
2350 6900 2350 6750
$Comp
L power:GND #PWR0105
U 1 1 5F872A11
P 9050 2350
F 0 "#PWR0105" H 9050 2100 50 0001 C CNN
F 1 "GND" V 9055 2222 50 0000 R CNN
F 2 "" H 9050 2350 50 0001 C CNN
F 3 "" H 9050 2350 50 0001 C CNN
1 9050 2350
0 -1 -1 0
$EndComp
$Comp
L power:GND #PWR0109
U 1 1 5F8787E6
P 2750 6900
F 0 "#PWR0109" H 2750 6650 50 0001 C CNN
F 1 "GND" V 2750 6750 50 0000 R CNN
F 2 "" H 2750 6900 50 0001 C CNN
F 3 "" H 2750 6900 50 0001 C CNN
1 2750 6900
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0111
U 1 1 5F878CF4
P 7300 3050
F 0 "#PWR0111" H 7300 2800 50 0001 C CNN
F 1 "GND" V 7300 2900 50 0000 R CNN
F 2 "" H 7300 3050 50 0001 C CNN
F 3 "" H 7300 3050 50 0001 C CNN
1 7300 3050
0 1 1 0
$EndComp
$Comp
L power:GND #PWR0117
U 1 1 5F885952
P 4850 6750
F 0 "#PWR0117" H 4850 6500 50 0001 C CNN
F 1 "GND" V 4850 6600 50 0000 R CNN
F 2 "" H 4850 6750 50 0001 C CNN
F 3 "" H 4850 6750 50 0001 C CNN
1 4850 6750
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0118
U 1 1 5F8874D1
P 5850 6900
F 0 "#PWR0118" H 5850 6650 50 0001 C CNN
F 1 "GND" V 5850 6750 50 0000 R CNN
F 2 "" H 5850 6900 50 0001 C CNN
F 3 "" H 5850 6900 50 0001 C CNN
1 5850 6900
1 0 0 -1
$EndComp
Text GLabel 4950 6850 3 50 Input ~ 0
SWITCHED_VCC
$Comp
L Connector:Conn_01x02_Male J5
U 1 1 5F9085EF
P 1700 4900
F 0 "J5" V 1762 4944 50 0000 L CNN
F 1 "Next" V 1853 4944 50 0000 L CNN
F 2 "mod_s:PinHeader_1x02_P2.54mm_Vertical" H 1700 4900 50 0001 C CNN
F 3 "" H 1700 4900 50 0001 C CNN
1 1700 4900
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x02_Male J6
U 1 1 5F90EAED
P 2200 4900
F 0 "J6" V 2262 4944 50 0000 L CNN
F 1 "Prev" V 2353 4944 50 0000 L CNN
F 2 "mod_s:PinHeader_1x02_P2.54mm_Vertical" H 2200 4900 50 0001 C CNN
F 3 "" H 2200 4900 50 0001 C CNN
1 2200 4900
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x02_Male J7
U 1 1 5F90F726
P 2600 4900
F 0 "J7" V 2662 4944 50 0000 L CNN
F 1 "Pause" V 2753 4944 50 0000 L CNN
F 2 "mod_s:PinHeader_1x02_P2.54mm_Vertical" H 2600 4900 50 0001 C CNN
F 3 "" H 2600 4900 50 0001 C CNN
1 2600 4900
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x08_Male J2
U 1 1 5F92011C
P 4750 5050
F 0 "J2" V 4585 4978 50 0000 C CNN
F 1 "RFID-RC522" V 4676 4978 50 0000 C CNN
F 2 "mod_s:PinHeader_1x08_P2.54mm_Vertical" H 4750 5050 50 0001 C CNN
F 3 "" H 4750 5050 50 0001 C CNN
1 4750 5050
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x03_Male J8
U 1 1 5F936A23
P 5750 6550
F 0 "J8" V 5812 6694 50 0000 L CNN
F 1 "WS2812" V 5903 6694 50 0000 L CNN
F 2 "mod_s:PinHeader_1x03_P2.54mm_Vertical" H 5750 6550 50 0001 C CNN
F 3 "" H 5750 6550 50 0001 C CNN
1 5750 6550
0 1 1 0
$EndComp
$Comp
L Connector:Conn_01x02_Male J9
U 1 1 5F945F11
P 8850 4700
F 0 "J9" V 8912 4744 50 0000 L CNN
F 1 "Power" V 9003 4744 50 0000 L CNN
F 2 "mod_s:PinHeader_1x02_P2.54mm_Vertical" H 8850 4700 50 0001 C CNN
F 3 "" H 8850 4700 50 0001 C CNN
1 8850 4700
0 1 1 0
$EndComp
Wire Wire Line
1600 2850 1850 2850
Text GLabel 1600 2850 0 50 Input ~ 0
GPIO-17
Wire Wire Line
2300 2400 2300 2550
$Comp
L power:GND #PWR0102
U 1 1 5F7B65C3
P 2300 2400
F 0 "#PWR0102" H 2300 2150 50 0001 C CNN
F 1 "GND" H 2305 2227 50 0000 C CNN
F 2 "" H 2300 2400 50 0001 C CNN
F 3 "" H 2300 2400 50 0001 C CNN
1 2300 2400
-1 0 0 1
$EndComp
Connection ~ 2300 2850
Wire Wire Line
2300 2850 2150 2850
Wire Wire Line
2450 2850 2300 2850
$Comp
L Device:R R5
U 1 1 5F7995ED
P 2000 2850
F 0 "R5" H 2070 2896 50 0000 L CNN
F 1 "4,7k" H 2070 2805 50 0000 L CNN
F 2 "adafruit:0207_9" V 1930 2850 50 0001 C CNN
F 3 "" H 2000 2850 50 0001 C CNN
1 2000 2850
0 1 1 0
$EndComp
$Comp
L Device:R R4
U 1 1 5F798E7D
P 2300 2700
F 0 "R4" H 2370 2746 50 0000 L CNN
F 1 "10k" H 2370 2655 50 0000 L CNN
F 2 "adafruit:0207_9" V 2230 2700 50 0001 C CNN
F 3 "" H 2300 2700 50 0001 C CNN
1 2300 2700
-1 0 0 1
$EndComp
$Comp
L Transistor_BJT:BC337 Q1
U 1 1 5F78DA7C
P 2650 2850
F 0 "Q1" H 2841 2896 50 0000 L CNN
F 1 "BC337" H 2841 2805 50 0000 L CNN
F 2 "Package_TO_SOT_THT:TO-92_Inline_Wide" H 2850 2775 50 0001 L CIN
F 3 "" H 2650 2850 50 0001 L CNN
1 2650 2850
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0101
U 1 1 5F7B5A15
P 2750 3300
F 0 "#PWR0101" H 2750 3050 50 0001 C CNN
F 1 "GND" H 2755 3127 50 0000 C CNN
F 2 "" H 2750 3300 50 0001 C CNN
F 3 "" H 2750 3300 50 0001 C CNN
1 2750 3300
1 0 0 -1
$EndComp
Wire Wire Line
2750 3050 2750 3300
$Comp
L Transistor_FET:IRF9540N Q2
U 1 1 5F78F572
P 2750 2100
F 0 "Q2" H 2954 2146 50 0000 L CNN
F 1 "IRF9540N" H 2954 2055 50 0000 L CNN
F 2 "Package_TO_SOT_THT:TO-220-3_Vertical" H 2950 2025 50 0001 L CIN
F 3 "" H 2750 2100 50 0001 L CNN
1 2750 2100
0 -1 -1 0
$EndComp
$Comp
L Device:R R1
U 1 1 5F79873B
P 3050 2150
F 0 "R1" H 3120 2196 50 0000 L CNN
F 1 "10k" H 3120 2105 50 0000 L CNN
F 2 "adafruit:0207_9" V 2980 2150 50 0001 C CNN
F 3 "" H 3050 2150 50 0001 C CNN
1 3050 2150
1 0 0 -1
$EndComp
Wire Wire Line
3050 2000 2950 2000
Wire Wire Line
2750 2300 3050 2300
Wire Wire Line
3300 2000 3050 2000
$Comp
L power:VCC #PWR0113
U 1 1 5F87F1D8
P 3300 2000
F 0 "#PWR0113" H 3300 1850 50 0001 C CNN
F 1 "VCC" V 3315 2127 50 0000 L CNN
F 2 "" H 3300 2000 50 0001 C CNN
F 3 "" H 3300 2000 50 0001 C CNN
1 3300 2000
0 1 1 0
$EndComp
Text GLabel 2350 2000 0 50 Input ~ 0
SWITCHED_VCC
Wire Wire Line
2350 2000 2550 2000
Connection ~ 3050 2000
Wire Wire Line
2750 2300 2750 2650
Connection ~ 2750 2300
$EndSCHEMATC

33
PCBs/ESP32_Breakout_Carrier/_autosave-Tonuino-ESP32_2.pro

@ -0,0 +1,33 @@
update=22/05/2015 07:44:53
version=1
last_client=kicad
[general]
version=1
RootSch=
BoardNm=
[pcbnew]
version=1
LastNetListRead=
UseCmpFile=1
PadDrill=0.600000000000
PadDrillOvalY=0.600000000000
PadSizeH=1.500000000000
PadSizeV=1.500000000000
PcbTextSizeV=1.500000000000
PcbTextSizeH=1.500000000000
PcbTextThickness=0.300000000000
ModuleTextSizeV=1.000000000000
ModuleTextSizeH=1.000000000000
ModuleTextSizeThickness=0.150000000000
SolderMaskClearance=0.000000000000
SolderMaskMinWidth=0.000000000000
DrawSegmentWidth=0.200000000000
BoardOutlineThickness=0.100000000000
ModuleOutlineThickness=0.150000000000
[cvpcb]
version=1
NetIExt=net
[eeschema]
version=1
LibDir=
[eeschema/libraries]

BIN
PCBs/ESP32_Breakout_Carrier/assembled.jpeg

After

Width: 1600  |  Height: 1200  |  Size: 182 KiB

1
PCBs/ESP32_Breakout_Carrier/fp-info-cache

@ -0,0 +1 @@
0

0
PCBs/Headphone with PCM5102a and TMD1308/Kicad.zip → PCBs/Headphone with PCM5102a and TDA1308/KiCad/Kicad-Files.zip

0
PCBs/Headphone with PCM5102a and TMD1308/3D-model_downside.jpeg → PCBs/Headphone with PCM5102a and TDA1308/Pictures/3D-model_downside.jpeg

Before

Width: 1272  |  Height: 883  |  Size: 270 KiB

After

Width: 1272  |  Height: 883  |  Size: 270 KiB

0
PCBs/Headphone with PCM5102a and TMD1308/3D-model_upside.jpeg → PCBs/Headphone with PCM5102a and TDA1308/Pictures/3D-model_upside.jpeg

Before

Width: 1272  |  Height: 883  |  Size: 290 KiB

After

Width: 1272  |  Height: 883  |  Size: 290 KiB

0
PCBs/Headphone with PCM5102a and TMD1308/Headphone-pcb.pdf → PCBs/Headphone with PCM5102a and TDA1308/Pictures/Headphone-pcb.pdf

0
PCBs/Headphone with PCM5102a and TMD1308/Headphone-pcb_downside.jpg → PCBs/Headphone with PCM5102a and TDA1308/Pictures/Headphone-pcb_downside.jpg

Before

Width: 2048  |  Height: 1796  |  Size: 1.1 MiB

After

Width: 2048  |  Height: 1796  |  Size: 1.1 MiB

0
PCBs/Headphone with PCM5102a and TMD1308/Headphone-pcb_upside.jpg → PCBs/Headphone with PCM5102a and TDA1308/Pictures/Headphone-pcb_upside.jpg

Before

Width: 3024  |  Height: 3024  |  Size: 1.6 MiB

After

Width: 3024  |  Height: 3024  |  Size: 1.6 MiB

2
PCBs/Headphone with PCM5102a and TMD1308/README.md → PCBs/Headphone with PCM5102a and TDA1308/README.md

@ -1,6 +1,6 @@
# Headphone-PCB
This is a pcb, that was kindly provided by a user of my Tonuino-fork. It makes use of a DAC named 'PCM5102A' with a TDA1308 as amp. PCM5102A supports I2S, so it can connected in parallel to MAX98357a to the I2S-pins BCLK, LRC and DIN. Of course, it needs 3.3V and GND, too. Please note that SMD-technique is used. <br />
The 6th pin of connector J1 is used to indicate whether there's a plug or not. That means if there is a plug, this pin is pulled to GND and therefore can be used to connect to MAX98357.SD. Doing so will mute MAX's amp and vice versa. Off course you need a special [headphone jack](https://www.conrad.de/de/p/cliff-fcr1295-klinken-steckverbinder-3-5-mm-buchse-einbau-horizontal-polzahl-3-stereo-schwarz-1-st-705830.html) to use this feature. Additionaly this 6th pin can be connected to the ESP32 in order to make use of the feature `HEADPHONE_ADJUST_ENABLE`. Doing so can optionally limit the headphone's maximum volume (configureable via GUI). <br />
The 6th pin of connector J1 is used to indicate whether there's a plug or not. That means if there is a plug, this pin is pulled to GND and therefore can be used to connect to MAX98357.SD. Doing so will mute MAX's amp and vice versa. Of course you need a special [headphone jack](https://www.conrad.de/de/p/cliff-fcr1295-klinken-steckverbinder-3-5-mm-buchse-einbau-horizontal-polzahl-3-stereo-schwarz-1-st-705830.html) to use this feature. Additionaly this 6th pin can be connected to the ESP32 in order to make use of the feature `HEADPHONE_ADJUST_ENABLE`. Doing so can optionally limit the headphone's maximum volume (configureable via GUI). <br />
## Disclaimer
PCB-circuit is provided 'as is' without warranty. As previously stated it was kindly provided by a user and I only can give limited support to it. However: it runs fine without any problems in my Tonuinos.

BIN
PCBs/Headphone with PCM5102a and TDA1308/gerber/gerber.zip

BIN
PCBs/Wemos Lolin D32/Gerber/gerber.zip

BIN
PCBs/Wemos Lolin D32/KiCad/KICad-Files.zip

BIN
PCBs/Wemos Lolin D32/Pictures/Schematics_D32.pdf

BIN
PCBs/Wemos Lolin D32/Pictures/Tonuino D32_downside.jpg

After

Width: 1438  |  Height: 799  |  Size: 45 KiB

BIN
PCBs/Wemos Lolin D32/Pictures/Tonuino D32_upside.jpg

After

Width: 1438  |  Height: 799  |  Size: 55 KiB

63
PCBs/Wemos Lolin D32/README.md

@ -0,0 +1,63 @@
# Tonuino-PCB based on Wemos' Lolin D32
## Attention
* In rev1 I made a mistake that leads to a short circuit. Hopefully nobody ordered it :-( Current revision is 1.1
* This PCB is not yet tested!
## Features
* Fits Wemos Lolin D32 (not Lolin32, Lolin D32 pro or Lolin 32 lite!)
* Outer diameter: 53 x 81mm
* JST-PH 2.0-connectors for buttons, rotary encoder, Neopixel, RFID and reset (not 2.54mm pitch!)
* 2.54mm-connectors for MAX98357a and uSD-card-reader
* Mosfet-circuit that switches off MAX98357a, Neopixel, headphone-pcb and uSD-card-reader automatically when deepsleep is active
* All peripherals are solely driven at 3.3V! Keep this especially in mind when choosing uSD-reader. If in doubts use one without voltage-regulator (link below).
* If [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308) is used, MAX98357a is automatically muted when there's a headphone plugged in and vice versa.
* If `HEADPHONE_ADJUST_ENABLE` is set and a headphone is plugged in, an alternative maximum volume is activated. I added this feature because [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308) makes use of an amp that (probably) "allows" children to damage ears. This maximum volume can be set and re-adjusted via webgui.
* Reset-button
## Prerequisites
* If no [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308) is connected, make sure `HEADPHONE_ADJUST_ENABLE` is not active.
* Make sure to change values of voltage divider's restors. They can be configured in `settings.h` as `rdiv1` and `rdiv2` and need to be set to 100k/100k.
* Change `VOLTAGE_READ_PIN` to 35.
## Things to mention
* Better don't solder Lolin D32 directly to the PCB. I recommend to make use of female connectors instead (link below).
* When ordering a LiPo-battery, make sure to use [one](https://www.eremit.de/p/eremit-3-7v-2500mah-lipo-104050-jst-ph-2-0mm) with deep discharge protection! This is really really really important!!!
* Doesn't (currently) support SD-MMC and RFID-reader PN5180
## Hardware-setup
The heart of my project is an ESP32 on a [Wemos Lolin D32 development-board](https://de.aliexpress.com/item/32808551116.html). Make sure to install the drivers for the USB/Serial-chip (CP2102 e.g.).
* [MAX98357A (like Adafruit's)](https://de.aliexpress.com/item/32999952454.html)
* [uSD-card-reader 3.3V only](https://www.ebay.de/itm/Micro-SPI-Kartenleser-Card-Reader-2GB-SD-8GB-SDHC-Card-3-3V-ESP8266-Arduino-NEU/333796577968)
* [RFID-reader](https://www.amazon.de/AZDelivery-Reader-Arduino-Raspberry-gratis/dp/B074S8MRQ7)
* [RFID-tags](https://www.amazon.de/AZDelivery-Keycard-56MHz-Schlüsselkarte-Karte/dp/B07TVJPTM7)
* [Neopixel-ring](https://de.aliexpress.com/item/32673883645.html)
* [Rotary Encoder](https://de.aliexpress.com/item/33041814942.html)
* [Buttons](https://de.aliexpress.com/item/32896285438.html)
* [Speaker](https://www.visaton.de/de/produkte/chassiszubehoer/breitband-systeme/fr-7-4-ohm)
* uSD-card: doesn't have to be a super-fast one; uC is limiting the throughput. Tested 32GB without any problems.
* [JSP PH-2.0-connectors](https://de.aliexpress.com/item/32968344273.html)
* [Female connector](https://de.aliexpress.com/item/32724478308.html)
* [(optional) IDC-connector female 6pin for headphone-pcb](https://de.aliexpress.com/item/33029492417.html)
* [(optional) IDC-connector male 6pin for headphone-pcb](https://de.aliexpress.com/item/1005001400147026.html)
* [LiPo-battery (2500 mAh) with connector JST PH 2.0mm](https://www.eremit.de/p/eremit-3-7v-2500mah-lipo-104050-jst-ph-2-0mm)
## Parts
* 1x IRF530NPbF (N-channel MOSFET)
* 1x NDP6020P (P-channel MOSFET)
* 1x 1k resistor
* 1x 10k resistor
* 2x 100k resistor
* 4x JST-PH2.0-connector (2 Pins)
* 3x JST-PH2.0-connector (3 Pins)
* 1x JST-PH2.0-connector (5 Pins)
* Female connector as socket for Lolin32, uSD-reader and MAX98357a
* (optional for headphone-PCB) 1x IDC-connecor female (6pin)
* (optional for headphone-PCB) 1x IDC-connecor male (6pin)
## Where to order?
* I ordered my PCBs at [jlcpcb](https://jlcpcb.com/). You have to order at least 5 pcs, which is only at 2$ + shipping. It took two weeks to arrive. If you want to have a look at the PCBs first (without having KiCad installed), visit [Gerberlook](https://www.gerblook.org/) and upload `gerber.zip` from the Gerberfiles-folder.
* Enter 31 x 31mm as dimensions in JLCPCB's order-form.
## Do I need to install KiCad?
Unless you don't want to change anything: no! All you need to provide are the gerberfiles (`gerber.zip`) to your manufactur (e.g. [jlcpcb](https://jlcpcb.com/)). However, all Kicad-files used are provided as well.

BIN
PCBs/Wemos Lolin32/Gerber/gerber.zip

BIN
PCBs/Wemos Lolin32/KiCad/Kicad-files.zip

BIN
PCBs/Wemos Lolin32/Pictures/Soldered with peripherals1_rev1.jpg

After

Width: 2048  |  Height: 1152  |  Size: 916 KiB

BIN
PCBs/Wemos Lolin32/Pictures/Soldered with peripherals2_rev1.jpg

After

Width: 2048  |  Height: 1152  |  Size: 824 KiB

BIN
PCBs/Wemos Lolin32/Pictures/Soldered1_rev1.jpg

After

Width: 2048  |  Height: 1152  |  Size: 677 KiB

BIN
PCBs/Wemos Lolin32/Pictures/Soldered2_rev1.jpg

After

Width: 2048  |  Height: 1152  |  Size: 789 KiB

BIN
PCBs/Wemos Lolin32/Pictures/Tonuino V2-Schematics.pdf

BIN
PCBs/Wemos Lolin32/Pictures/Tonuino V2_down.jpg

After

Width: 1438  |  Height: 799  |  Size: 52 KiB

BIN
PCBs/Wemos Lolin32/Pictures/Tonuino V2_up.jpg

After

Width: 1438  |  Height: 799  |  Size: 62 KiB

66
PCBs/Wemos Lolin32/README.md

@ -0,0 +1,66 @@
# Tonuino-PCB based on Wemos' Lolin32
## Introduction
After I've been asked many times to provide a PCB, I finally did so :-) It makes use of Wemos' Lolin32 which is the predecessor of Lolin D32. D32's advantage over Lolin32 is especially, that a voltage-divider for measuring battery's voltage is already integrated (fixed-wired to GPIO 35). However, as I wasn't aware of that when buying Lolin32 and because of now, that multiple Lolin32 are here on my desk, my reasonable intention was to use them. So things would have been a bit easier with D32 but in the end it works the same way with Lolin32. As per rev2 of the PCB, I added a 10 uF-capacitor to smoothen the battery-measurement. Ii's optional but be advised values measured are a bit more volatile without capacitor-stabilization.
## Features
* Fits Wemos Lolin32 (not Lolin D32, Lolin D32 pro or Lolin 32 lite!)
* Outer diameter: 56 x 93mm
* JST-PH 2.0-connectors for buttons, rotary encoder, Neopixel, RFID, reset and battery (not 2.54mm pitch!)
* 2.54mm-connectors for MAX98357a and uSD-card-reader
* Mosfet-circuit that switches off MAX98357a, Neopixel, headphone-pcb and uSD-card-reader automatically when deepsleep is active
* All peripherals are solely driven at 3.3V! Keep this especially in mind when choosing uSD-reader. If in doubts use one without voltage-regulator (link below).
* If [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308) is used, MAX98357a is automatically muted when there's a headphone plugged in and vice versa.
* If `HEADPHONE_ADJUST_ENABLE` is enabled and a headphone is plugged in, an alternative maximum volume is activated. I added this feature because [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308) makes use of an amp that (probably) "allows" children to damage ears. This maximum volume can be set and re-adjusted via webgui.
* rev2: Reset-pinheader added. Can be used to connect e.g. a [micro switch](https://www.ebay.de/itm/10x-Mini-Taster-Drucktaster-klein-Mikroschalter-6x6x5mm-Arduino-Raspberry-Pi/333273061003) to it, so you can reset Tonuino even in battery-mode from the outside of the enclosure. Micro switch can be placed somewhat hidden at the enclosure.
## Prerequisites
* If no [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308) is connected, make sure `HEADPHONE_ADJUST_ENABLE` is not active.
* I used 390/130 kOhms-resistors as voltage-divider. However, make sure to use a multimeter to determine their exact values in order to achieve a better battery-measurement. They can be configured in `settings.h` as `rdiv1` and `rdiv2`. Hint: for Lolin D32's battery-measurement 100k+100k were used. However, I decided to change the ratio from 50/50% to 25/75% to have a "better" signal. 100/100 might be work as well; didn't test it.
## Things to mention
* RFID: In order to avoid buying a 6pin-JST-PH-connector I used 2x3pin instead. This is because I already had ten of them (see link below).
* In contrast to Lolin D32, Lolin32 doesn't feature an integrated voltage-divider. That's why on the lower left there's a JST-PH2.0-connector to connect the LiPo-battery. Make sure to connect (+) to the left und GND to the right. From there you need to solder two short wires (5cm or so) onto the pcb with a JST-PH2.0-connector attached on the other side. This one needs to be plugged into Lolin32 (see pictures-folder). Please note: Lolin's JST-PH2.0-connector needs (+) left side and GND right side. Don't be confused if black/red-colouring of the JST-wires used seems "weird" because is reversed (black => (+); red => GND).
* Better don't solder Lolin32 directly to the PCB. I recommend to make use of female connectors instead (link below).
* When ordering a LiPo-battery, make sure to use [one](https://www.eremit.de/p/eremit-3-7v-2500mah-lipo-104050-jst-ph-2-0mm) with deep discharge protection! This is really really really important!!!
* Doesn't (currently) support SD-MMC and RFID-reader PN5180
## Hardware-setup
The heart of my project is an ESP32 on a [Wemos Lolin32 development-board](https://www.ebay.de/itm/4MB-Flash-WEMOS-Lolin32-V1-0-0-WIFI-Bluetooth-Card-Based-ESP-32-ESP-WROOM-32/162716855489). Make sure to install the drivers for the USB/Serial-chip (CP2102 e.g.).
* [MAX98357A (like Adafruit's)](https://de.aliexpress.com/item/32999952454.html)
* [uSD-card-reader 3.3V only](https://www.ebay.de/itm/Micro-SPI-Kartenleser-Card-Reader-2GB-SD-8GB-SDHC-Card-3-3V-ESP8266-Arduino-NEU/333796577968)
* [RFID-reader](https://www.amazon.de/AZDelivery-Reader-Arduino-Raspberry-gratis/dp/B074S8MRQ7)
* [RFID-tags](https://www.amazon.de/AZDelivery-Keycard-56MHz-Schlüsselkarte-Karte/dp/B07TVJPTM7)
* [Neopixel-ring](https://de.aliexpress.com/item/32673883645.html)
* [Rotary Encoder](https://de.aliexpress.com/item/33041814942.html)
* [Buttons](https://de.aliexpress.com/item/32896285438.html)
* [Speaker](https://www.visaton.de/de/produkte/chassiszubehoer/breitband-systeme/fr-7-4-ohm)
* uSD-card: doesn't have to be a super-fast one; uC is limiting the throughput. Tested 32GB without any problems.
* [JSP PH-2.0-connectors](https://de.aliexpress.com/item/32968344273.html)
* [Female connector](https://de.aliexpress.com/item/32724478308.html)
* [(optional) IDC-connector female 6pin for headphone-pcb](https://de.aliexpress.com/item/33029492417.html)
* [(optional) IDC-connector male 6pin for headphone-pcb](https://de.aliexpress.com/item/1005001400147026.html)
* [(optional) Mikro switch](https://www.ebay.de/itm/10x-Mini-Taster-Drucktaster-klein-Mikroschalter-6x6x5mm-Arduino-Raspberry-Pi/333273061003)
* [LiPo-battery (2500 mAh) with connector JST PH 2.0mm](https://www.eremit.de/p/eremit-3-7v-2500mah-lipo-104050-jst-ph-2-0mm)
## Parts
* 1x IRF530NPbF (N-channel MOSFET)
* 1x NDP6020P (P-channel MOSFET)
* 1x 1k resistor
* 1x 10k resistor
* 2x 100k resistor
* 1x 130k resistor (can be replaced by 100k)
* 1x 390k resistor (can be replaced by 100k)
* 5x JST-PH2.0-connector (2 Pins)
* 3x JST-PH2.0-connector (3 Pins)
* 1x JST-PH2.0-connector (5 Pins)
* Female connector as socket for Lolin32, uSD-reader and MAX98357a
* (optional for headphone-PCB) 1x IDC-connecor female (6pin)
* (optional for headphone-PCB) 1x IDC-connecor male (6pin)
* rev2: (optional) 1x 10 uF capacitor (2.0mm-pitch)
## Where to order?
I ordered my PCBs at [jlcpcb](https://jlcpcb.com/). You have to order at least 5 pcs, which is only at 2$ + shipping. It took two weeks to arrive. If you want to have a look at the PCBs first (without having KiCad installed), visit [Gerberlook](https://www.gerblook.org/) and upload `gerber.zip` from the Gerberfiles-folder.
## Do I need to install KiCad?
Unless you don't want to change anything: no! All you need to provide are the gerberfiles (`gerber.zip`) to your manufactur (e.g. [jlcpcb](https://jlcpcb.com/)). However, all Kicad-files used are provided as well.

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/Gerber/gerber.zip

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/KiCad/Kicad-files.zip

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/3d-Model downside.jpg

After

Width: 1438  |  Height: 799  |  Size: 53 KiB

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/3d-Model upside.jpg

After

Width: 1438  |  Height: 799  |  Size: 65 KiB

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Soldered with peripherals1.jpg

After

Width: 2048  |  Height: 1152  |  Size: 916 KiB

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Soldered with peripherals2.jpg

After

Width: 2048  |  Height: 1152  |  Size: 824 KiB

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Soldered1.jpg

After

Width: 2048  |  Height: 1152  |  Size: 677 KiB

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Soldered2.jpg

After

Width: 2048  |  Height: 1152  |  Size: 789 KiB

BIN
PCBs/Wemos Lolin32/_Archiv/rev1/Pictures/Tonuino-Lolin32-Schematics.pdf

256
README.md

@ -1,8 +1,33 @@
# Tonuino based on ESP32 with I2S-DAC-support
## NEWS
Currently I'm working on a new Tonuino that is completely based on 3.3V and makes use of an (optional) [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TMD1308). As uC-develboard a Lolin32 is used and it's (optionally) battery-powered. So stay tuned...
Finally, the long announced Tonuino-PCB for Wemos' Lolin32 is [there](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Wemos%20Lolin32). It can (optionally) be used alongside with a [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308). As uC-develboard a Lolin32 is used and it's (optionally) battery-powered. Peripherals (Neopixel, RFID, headphone-pcb and MAX98357a) are driven at 3.3V solely.
## History
[...]
* 11.07.2020: Added support for reversed Neopixel addressing.
* 09.10.2020: mqttUser / mqttPassword can now be configured via webgui.
* 16.10.2020: Added English as supported lanuage.
* 01.11.2020: Added directive `SD_NOT_MANDATORY_ENABLE`: for debugging puposes SD can be bypassed at boot.
* 17.11.2020: Introduced a distinct volume for headphone for optional headphone-PCB.
* 20.11.2020: Added directive `MEASURE_BATTERY_VOLTAGE`: monitoring battery's voltage is now supported.
* 25.11.2020: WiFi can now be enabled/disabled instantly by pressing two buttons.
* 28.11.2020: Battery's voltage can now be visualized by Neopixel by short-press of rotary encoder's burtton.
* 28.11.2020: Added directive `PLAY_LAST_RFID_AFTER_REBOOT`: Tonuino will recall the last RFID played after reboot.
* 05.12.2020: Added filebrowser to webgui (thanks @mariolukas for contribution!)
* 05.12.2020: Moved all user-relevant settings to src/settings.h
* 06.12.2020: Added PCB for Wemos Lolin32
* 08.12.2020: Reworked MQTT-timeout-Management
* 09.12.2020: mDNS-feature added. If tonuino's name is "tonuino", you can use `tonuino.local` instead it's of IP-address.
* 11.12.2020: Revised GUI-design (thanks @mariolukas for contribution!) + (untested) PCB added for Wemos Lolin D32 + gerberfiles for headphone-PCB
* 18.12.2020: Added SD-MMC 1 Bit-mode (`SD_MMC_1BIT_MODE`). This mode needs one GPIO less and provides almost doubled speed (compared to SPI) for FTP-transfers (thanks @tueddy for contribution!)
* 18.12.2020: Added support for RFID-reader PN5180 (`RFID_READER_TYPE_PN5180`). PN5180 has better RFID-range/sensitivity and can read ISO-15693 / iCode SLIX2-tags aka 'Tonies' (thanks @tueddy for contribution!)
* 20.12.2020: Due to memory-issues with webstreams, FTP needs to be activated by pressing pause+next-button now
<br />More to come...
## Known bugs
* Some webstreams don't run. Guess it's a combination of saturated connection-pool and lack of heap-memory. Works probably better if ESP32-WROVER is used, as this chip has PSRAM. Advice: Don't enable modules (e.g. MQTT) if you don't need them as this could save memory.
* English translation for webgui is currently outdated. This will be fixed soon when i18n-support will be integrated.
## Disclaimer
This is a **fork** of the popular [Tonuino-project](https://github.com/xfjx/TonUINO) which means, that it only shares the basic concept of controlling a music-player by RFID-tags and buttons. **Said this I want to rule out, that the code-basis is completely different and developed by myself**. So there might be features, that are supported by my fork whereas others are missing or implemented differently. For sure both share that it's non-profit, DIY and developed on [Arduino](https://www.arduino.cc/).
@ -10,56 +35,107 @@ This is a **fork** of the popular [Tonuino-project](https://github.com/xfjx/TonU
## What's different (basically)?
The original project makes use of microcontrollers (uC) like Arduino nano (which is the [Microchip AVR-platform](https://de.wikipedia.org/wiki/Microchip_AVR) behind the scenes). Music-decoding is done in hardware using [DFPlayer mini](https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299) which offers a uSD-card-slot and an integrated amp as well. Control of this unit is done by a serial-interconnect with a uC using the API provided.
The core of my implementation is based on the popular [ESP32 by Espressif](https://www.espressif.com/en/products/hardware/esp32/overview). Having WiFi-support out-of-the-box makes it possible to provide further features like an integrated FTP-server (to feed the player with music), smarthome-integration via MQTT and webradio. However, my primary focus was to port the project to a modular base. Said this mp3-decoding is done in software with a dedicated uSD-card-slot and music-output is done via I2S-protocol. I did all my tests on [Adafruit's MAX98357A](https://learn.adafruit.com/adafruit-max98357-i2s-class-d-mono-amp/pinouts). Hopefully, not only in theory, other DACs that support I2S can be used as well.
The core of my implementation is based on the popular [ESP32 by Espressif](https://www.espressif.com/en/products/hardware/esp32/overview). Having WiFi-support out-of-the-box makes it possible to provide further features like an integrated FTP-server (to feed the player with music), smarthome-integration via MQTT, webradio and administration via webgui. However, my primary focus was to port the project to a modular base. Said this mp3-decoding is done in software with a dedicated uSD-card-slot and music-output is done via I2S-protocol. I did all my tests on [Adafruit's MAX98357A](https://learn.adafruit.com/adafruit-max98357-i2s-class-d-mono-amp/pinouts), [UDA1334](https://www.adafruit.com/product/3678) and [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308). Hopefully, not only in theory, other DACs that support I2S can be used as well.
## Basic concept/handling
The basic idea of Tonuino (and my fork, respectively) is to provide a way, to use the Arduino-platform for a music-control-concept that supports locally stored music-files instead of being fully cloud-dependend. This basically means that RFID-tags are used to direct a music-player. Even for kids this concept is simple: place an RFID-object (card, character) on top of a box and the music starts to play. Place another RFID-object on it and anything else is played. Simple as that.
## Basic concept/handling of Tonuino
The basic idea of Tonuino is to provide a way, to use the Arduino-platform for a music-control-concept that supports locally stored music-files without DRM-restrictions. This basically means that RFID-tags are used to direct a music-player. Even for kids this concept is simple: place an RFID-object (card, character) on top of a box and the music starts to play. Place another RFID-object on it and anything else is played. Simple as that.
## Hardware-setup
The heart of my project is an ESP32 on a [Wemos Lolin32 development-board](https://www.ebay.de/itm/4MB-Flash-WEMOS-Lolin32-V1-0-0-WIFI-Bluetooth-Card-Based-ESP-32-ESP-WROOM-32/162716855489). If ordered in China (Aliexpress, eBay e.g.) it's pretty cheap (around 4€) but even in Europe it's only around 8€. Make sure to install the drivers for the USB/Serial-chip (CP2102 e.g.). If unsure have a look at eBay or Aliexpress for "Lolin 32".
* [MAX98357A (like Adafruit's)](https://www.ebay.de/itm/MAX98357-Amplifier-Breakout-Interface-I2S-Class-D-Module-For-ESP32-Raspberry-Pi/174319322988)
* [uSD-card-reader 3.3V + 5V](https://www.amazon.de/AZDelivery-Reader-Speicher-Memory-Arduino/dp/B077MB17JB)
* [uSD-card-reader 3.3V only](https://www.ebay.de/itm/Micro-SPI-Kartenleser-Card-Reader-2GB-SD-8GB-SDHC-Card-3-3V-ESP8266-Arduino-NEU/333796577968)
The heart of my project is an ESP32 on a [Wemos Lolin32 development-board](https://www.ebay.de/itm/4MB-Flash-WEMOS-Lolin32-V1-0-0-WIFI-Bluetooth-Card-Based-ESP-32-ESP-WROOM-32/162716855489). If ordered in China (Aliexpress, eBay e.g.) it's pretty cheap (around 4€) but even in Europe it's only around 8€. Make sure to install the drivers for the USB/Serial-chip (CP2102 e.g.).
* [MAX98357A (like Adafruit's)](https://de.aliexpress.com/item/32999952454.html)
* [uSD-card-reader; 3.3V only; supports SPI + SD-MMC](https://www.ebay.de/itm/Micro-SPI-Kartenleser-Card-Reader-2GB-SD-8GB-SDHC-Card-3-3V-ESP8266-Arduino-NEU/333796577968)
* [RFID-reader](https://www.amazon.de/AZDelivery-Reader-Arduino-Raspberry-gratis/dp/B074S8MRQ7)
* [RFID-tags](https://www.amazon.de/AZDelivery-Keycard-56MHz-Schlüsselkarte-Karte/dp/B07TVJPTM7)
* [Neopixel-ring](https://www.ebay.de/itm/LED-Ring-24-x-5050-RGB-LEDs-WS2812-integrierter-Treiber-NeoPixel-kompatibel/282280571841)
* [Rotary Encoder](https://www.amazon.de/gp/product/B07T3672VK)
* [Neopixel-ring](https://de.aliexpress.com/item/32673883645.html)
* [Rotary Encoder](https://de.aliexpress.com/item/33041814942.html)
* [Buttons](https://de.aliexpress.com/item/32896285438.html)
* [Speaker](https://www.visaton.de/de/produkte/chassiszubehoer/breitband-systeme/fr-7-4-ohm)
* uSD-card: doesn't have to be a super-fast one; uC is limiting the throughput. Tested 32GB without any problems.
* uSD in SD-MMC (1 Bit) mode: Several devkits with onboard SD slot have support for this mode, e.g.: (https://de.aliexpress.com/item/4001229463219.html)
* optional replace the RFID-reader with a the better one: PN5180 comes with better RFID range, less power consumption and support for ISO-15693 / iCode SLIX2 tags (https://www.bing.com/shop?q=pn5180&FORM=SHOPTB)
Most of them can be ordered cheaper directly in China. It's just a give an short impression of the hardware; feel free to order where ever you want to. These are not affiliate-links.
## Getting Started
I recommend Microsoft's [Visual Studio Code](https://code.visualstudio.com/) alongside with [Platformio Plugin](https://platformio.org/install/ide?install=vscode). Since my project on Github contains [platformio.ini](platformio.ini), libraries used should be fetched automatically. Please note: if you use another ESP32-develboard (Lolin32 e.g.) you might have to change "env:" in platformio.ini to the corresponding value. Documentation can be found [here](https://docs.platformio.org/en/latest/projectconf.html). After that it might be necessary to adjust the names of the GPIO-pins in the upper #define-section of my code.
In the upper section of main.cpp you can specify the modules that should be compiled into the code.
Please note: if MQTT is enabled it's still possible to deactivate it via webgui.
## Wiring (2 SPI-instances)
A lot of wiring is necessary to get ESP32-Tonuino working. After my first experiments I soldered the stuff on a board in order to avoid wild-west-cabling. Especially for the interconnect between uC and uSD-card-reader make sure to use short wires (like 10cm or so)! Important: you can easily connect another I2S-DACs by just connecting them in parallel to the I2S-pins (DIN, BCLK, LRC). This is true for example if you plan to integrate a [line/headphone-pcb](https://www.adafruit.com/product/3678). In general, this runs fine. But unfortunately especially this board lacks of a headphone jack, that takes note if a plug is inserted or not. Best way is to use a [headphone jack](https://www.conrad.de/de/p/cliff-fcr1295-klinken-steckverbinder-3-5-mm-buchse-einbau-horizontal-polzahl-3-stereo-schwarz-1-st-705830.html) that has a pin that is pulled to GND, if there's no plug and vice versa. Using for example a MOSFET-circuit, this GND-signal can be inverted in a way, that MAX98357.SD is pulled down to GND if there's a plug. Doing that will turn off the speaker immediately if there's a plug and vice versa. Have a look at the PCB-folder in order to view the detailed solution. Here's an example for such a [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TMD1308) that makes use of GND.
## Getting Started
* Arduino-IDE can be used but you need to satisfy dependencies for all the libraries listed in `platformio.ini` manually.
* Instead I recommend to install Microsoft's [Visual Studio Code](https://code.visualstudio.com/). This is a popular and powerful IDE that gives you the ability to install tons of (well-supported) plugins.
* Install [Platformio Plugin](https://platformio.org/install/ide?install=vscode) into [Visual Studio Code](https://code.visualstudio.com/) and make sure to have a look at the [documentation](https://docs.platformio.org/en/latest/integration/ide/pioide.html).
* Install [Git](https://git-scm.com/downloads) and make a copy ("clone") my repository to your local computer using `git clone https://github.com/biologist79/Tonuino-ESP32-I2S.git`. Using git you can keep your local repository easily up to date without doing copy'n'paste.
* (Optional) Install [Gitlens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) as plugin (to have advanced Git-support).
* Now, that the git-repository is saved locally, import this folder into Platformio as a project.
* There's a file called `platformio.ini`, that contains the configuration for different develboards (e.g. env:lolin32). Platformio supports hundrets of boards out of the box. So probably you need to change/extend that configuration. Guess Lolin32 is described in platformio.ini but you need Lolin D32, then lookup Platformio's [documentation](https://docs.platformio.org/en/latest/boards/espressif32/lolin_d32.html) to know what to change.
* Depending on your operating system (Windows, Mac OS, Linux), you probably need to change `upload_port`and `monitor_port` as well.
* Edit `src/settings.h` according your needs. Especially GPIO-configuration is really important.
* Connect your develboard via USB, click the alien-head to the left and run `Upload and Monitor`. All libraries necessary should be fetched in background now followed by code-compilation. After that, your ESP32 is flashed with the firmware.
* Now have a look at the serial-output at the bottom of Visual Studio Code's windows.
* If everything ran fine, at the first run, Tonuino should open an access-point with the name "Tonuino". Connect to this WiFi with your computer (or mobile) and enter `192.168.4.1` to your webbrowser. Enter WiFi-credentials and the hostname. After saving the configuraton, restart Tonuino.
* Now Tonuino tries to join your WiFi. If that was successful, an IP is shown in the serial-console of Visual Studio Code. You can call Tonuino's GUI via this IP. If mDNS-feature is active in `src/settings.h`, you can use the hostname configured extended by .local. So if you configured `tonuino` as hostname, you can use `tonuino.local` for webgui and FTP.
* Via FTP you can upload data (don't expect it to be super fast). It's round about 185 kb/s if SD is in SPI-mode and 300 kB/s if SD is in MMC-mode.
* Via webbrowser you can configure various settings and pair RFID-tags with actions.
## Prerequisites / tipps
* Open settings.h
* choose if optional modules (e.g. MQTT, FTP, Neopixel) should be compiled/enabled
* For debugging-purposes serialDebug can be set to ERROR, NOTICE, INFO or DEBUG.
* If MQTT=yes, set the IP or hostname of the MQTT-server accordingly and check the MQTT-topics (states and commands)
* If Neopixel enabled: set NUM_LEDS to the LED-number of your Neopixel-ring and define the Neopixel-type using `#define CHIPSET`
* If you're using Arduino-IDE please make sure to change ESP32's partition-layout to `No OTA (2MB APP/2MB Spiffs)` as otherwise the sketch won't fit into the flash-memory.
* If you want to monitor battery's voltage, make sure to enable `MEASURE_BATTERY_VOLTAGE`. Use a voltage-divider as voltage of a LiPo is way too high for ESP32 (only 3.3V supported!). For my tests I connected VBat with a serial connection of 130k + 390k resistors (VBat(+)--130k--X--390k--VBat(-)). X is the measure-point where to connect the GPIO to. If using Lolin D32 or Lolin D32 pro, make sure to adjust both values to 100k each and change GPIO to 35 as this is already integrated and wired fixed.
Please note: via GUI upper and lower voltage cut-offs for visualisation of battery-voltage (Neopixel) is available. Additional GUI-configurable values are interval (in minutes) for checking battery voltage and the cut off-voltage below whose a warning is shown via Neopixel.
* If you're using a headphone-pcb with a [headphone jack](https://www.conrad.de/de/p/cliff-fcr1295-klinken-steckverbinder-3-5-mm-buchse-einbau-horizontal-polzahl-3-stereo-schwarz-1-st-705830.html) that has a pin to indicate if there's a plug, you can use this signal along with the feature `HEADPHONE_ADJUST_ENABLE` to limit the maximum headphone-voltage automatically. As per default you have to invert this signal (with a P-channel MOSFET) and connect it to GPIO22.
* Enabling `SHUTDOWN_IF_SD_BOOT_FAILS` is really recommended if you run your Tonuino in battery-mode without having a restart-button exposed to the outside of Tonuino's enclosure. Because otherwise there's no way to restart your Tonuino and the error-state will remain until battery is empty (or you open the enclosure, hehe).
* Enabling `PLAY_LAST_RFID_AFTER_REBOOT` will tell Tonuino to remember the last RFID-tag played after next reboot. So rebooting Tonuino will end up in autoplay.
* If `MDNS_ENABLE` is enabled, your Tonuino is reachable via hostname.local. So if your Tonuino's hostname is 'tonuino', the address is `tonuino.local`.
## SD-card: SPI or SD-MMC (1 bit)-mode?
Having SD working is mandatory. So there are two modes available to access SD-cards: SPI and SD-MMC (1 bit).<br />
Advantages SD-MMC (1 bit) over SPI:
* Needs only three GPIOs (instead of four)
* It is almost twice as fast in FTP-transfer
So why using SPI if SD-MMC is better? The primary problem of SD-MMC is: you cannot choose different GPIOs. That doesn't sound bad but this can (depends on the ESP-develboard) be a problem because GPIO2 is pulled HIGH in SD-MMC-mode. And if that's the case, you'll probably not able to flash the firmware as the ESP32 doesn't enter flash-mode. Tested it with Lolin32 and had to remove SD's wire to GPIO2 until flash-mode was entered. As soon as flash-mode was entered, it's safe to re-connect it. So be advised: SD-MMC is nice and fast, but if you want to update the firmware and ESP32 is deeply burried in Tonuino's enclosure, this can be problem.
## RFID: RC522 or PN5180?
RC522 is so to say the Tonuino-standard. It's cheap and works, but RFID-tag has to be placed near the reader. PN5180 instead has better RFID range/sensitivity and can read ISO-15693 / iCode SLIX2-tags aka 'Tonies'. Disadvantages: is a bit more expensive and needs more GPIOs (6 instead of 4). Refer PN5180's wire-section below for further informations.
## 3.3 or 5V?
* MAX98357a: provides more power at 5 V but also runs at 3.3 V
* Neopixel: specification says it needs 5 V but also runs at 3.3 V
* RC522: needs 3.3 V (don't power with 5 V!)
* SD: needs 3.3 V but if voltage-regulator is onboard, it can be connected to 5 V as well
* Rotary encoder: 3.3 V (don't power with 5 V! Encoder doens't care if connected to 3.3 or 5 V, but GPIOs of ESP32 do!)
So why choosing 3.3 V over 5 V?
* It's not necessary to mix up 3.3 and 5 V. Natively ESP32 operates at 3.3 V.
* If you plan to use battery-mode with a LiPo, there's no 5 V available (unless USB is connected).
That's why my design's focus is 3.3 V. If you want to use 5 V - do so. The Mosfet-circuit for saving power in deepsleep (see [Lolin32-schematics](https://github.com/biologist79/Tonuino-ESP32-I2S/blob/master/PCBs/Wemos%20Lolin32/Pictures/Tonuino%20V2-Schematics.pdf) as reference) works as well for 5 V.
## Wiring (general)
Depending on the develboard you're using and the needs you have, there are different options available.
A lot of wiring is necessary to get ESP32-Tonuino working. After my first experiments on a breadboard I soldered all the stuff onto a board in order to avoid wild-west-cabling. Especially for the interconnect between uC and uSD-card-reader make sure to use short wires (like 10cm or so)! As of my experience with a breadbord, male/male-connectors are better than female/female-connectors. Important: you can easily connect another I2S-DACs by just connecting them in parallel to the I2S-pins (DIN, BCLK, LRC). This is true for example if you plan to integrate a [line/headphone-pcb](https://www.adafruit.com/product/3678). In general, this runs fine. But unfortunately especially this board lacks of a headphone jack, that takes note if a plug is inserted or not. Best way is to use a [headphone jack](https://www.conrad.de/de/p/cliff-fcr1295-klinken-steckverbinder-3-5-mm-buchse-einbau-horizontal-polzahl-3-stereo-schwarz-1-st-705830.html) that has a pin that is pulled to GND, if there's no plug and vice versa. Using for example a MOSFET-circuit, this GND-signal can be inverted in a way, that MAX98357.SD is pulled down to GND if there's a plug. Doing that will turn off the speaker immediately if there's a plug and vice versa. Have a look at the PCB-folder in order to view the detailed solution. Here's an example for such a [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TDA1308) that makes use of GND.<br />
Have a look at my PCB in the subfolder Hardware-Platforms/Wemos Lolin32. Probably this makes things easier for you.
## Wiring (2 SPI-instances: RC522 + SD)
Uses two SPI-instances. The first one for the RFID-reader and the second for SD-card-reader. This is also the [setup, I personally use](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Wemos%20Lolin32).<br />
| ESP32 (GPIO) | Hardware | Pin | Comment |
| ------------- | --------------------- | ------ | ------------------------------------------------------------ |
| 5 V | SD-reader | VCC | Connect to p-channel MOSFET for power-saving when uC is off |
| 3.3 (5) V | SD-reader | VCC | Connect to p-channel MOSFET for power-saving when uC is off |
| GND | SD-reader | GND | |
| 15 | SD-reader | CS | |
| 13 | SD-reader | MOSI | |
| 16 | SD-reader | MISO | |
| 14 | SD-reader | SCK | |
| 17 | RFID-reader | 3.3V | Connect directly to GPIO 17 for power-saving when uC is off |
| 3.3 V | RFID-reader | 3.3V | Connect directly to GPIO 17 for power-saving when uC is off |
| GND | RFID-reader | GND | |
| 21 | RFID-reader | CS/SDA | |
| 23 | RFID-reader | MOSI | |
| 19 | RFID-reader | MISO | |
| 18 | RFID-reader | SCK | |
| 5 V | MAX98357 | VIN | Connect to p-channel MOSFET for power-saving when uC is off |
| 5 / 3.3 V | MAX98357 | VIN | Connect to p-channel MOSFET for power-saving when uC is off |
| GND | MAX98357 | GND | |
| 25 | MAX98357 | DIN | |
| 27 | MAX98357 | BCLK | |
| 26 | MAX98357 | LRC | |
| --- | MAX98357 | SD | Info: if pulled down to GND amp will turn off |
| 34 | Rotary encoder | CLR | Invert CLR with DT if you want to change the direction of RE |
| 35 | Rotary encoder | DT | Invert CLR with DT if you want to change the direction of RE |
| 34 | Rotary encoder | CLR | Change CLR with DT if you want to change the direction of RE |
| 35 | Rotary encoder | DT | Change CLR with DT if you want to change the direction of RE |
| 32 | Rotary encoder | BUTTON | |
| 3.3 V | Rotary encoder | + | |
| GND | Rotary encoder | GND | |
@ -69,40 +145,51 @@ A lot of wiring is necessary to get ESP32-Tonuino working. After my first experi
| GND | Button (previous) | | |
| 5 | Button (pause/play) | | |
| GND | Button (pause/play) | | |
| 5 V | Neopixel | 5 V | Connect to p-channel MOSFET for power-saving when uC is off |
| 3.3 V | Neopixel | 5 V | Connect to p-channel MOSFET for power-saving when uC is off |
| GND | Neopixel | GND | |
| 12 | Neopixel | DI | Might be necessary to use a logic-converter 3.3 => 5V |
| 17 | (e.g.) BC337 (via R5) | Base | Don't forget R5! |
| 33 | Voltage-divider / BAT | | Optional: Voltage-divider to monitor battery-voltage |
| 12 | Neopixel | DI | |
| 17 | N-channel Mosfet | Gate | |
| 33 | Voltage-divider / BAT | | Optional: voltage-divider to monitor battery-voltage |
| 22 | Headphone jack | | Optional: if pulled to ground, headphone-volume is set |
Optionally, GPIO 17 can be used to drive a Mosfet-circuit in order to switch off peripherals (SD, Neopixel, RFID and MAX98357a) if ESP32 is in deepsleep. lease refer the schematics for my [Lolin32-PCB](https://github.com/biologist79/Tonuino-ESP32-I2S/blob/master/PCBs/Wemos%20Lolin32/Pictures/Tonuino-Lolin32-Schematics.pdf) for further informations. If you need further informations on transistor-circuits visit this [website](https://dl6gl.de/schalten-mit-transistoren.html). <br />
In general I recommend using a [uSD-card-reader](https://www.ebay.de/itm/Micro-SPI-Kartenleser-Card-Reader-2GB-SD-8GB-SDHC-Card-3-3V-ESP8266-Arduino-NEU/333796577968) that can be run solely with 3.3V (doesn't have a voltage-regulator). This is because if 3.3V go through the voltage regulator a small voltage-drop will be introduced, which may lead to SD-malfunction as the resulting voltage is a bit too low. Vice versa if you want to connect your reader solely to 5V, make sure to have one WITH a voltage regulator :-). And by the way: when LiPo-battery is connected, there's no 5V. That's why I designed my [Lolin32-PCB](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Wemos%20Lolin32) with 3.3V only.
## Wiring (SD-card in 1 Bit SD-MMC mode) different to above
| ESP32 (GPIO) | Hardware | Pin | Comment |
| ------------- | --------------------- | ------ | ------------------------------------------------------------ |
| -- | SD-reader | CS | no CS required |
| 15 | SD-reader | MOSI | |
| 2 | SD-reader | MISO | 10K hardware pullup may be required |
| 14 | SD-reader | SCK | |
Optionally, GPIO 17 can be used to drive a NPN-transistor (BC337-40) that pulls a p-channel MOSFET (IRF9520) to GND in order to switch on/off 5V-current. Transistor-circuit is described [here](https://dl6gl.de/schalten-mit-transistoren.html): Just have a look at Abb. 4. Values of the resistors I used: R1: 10k, R2: omitted(!), R4: 10k, R5: 4,7k. <br />
Also tested this successfully for a 3.3V-setup with IRF530NPBF (N-channel MOSFET) and NDP6020P (P-channel MOSFET). Resistor-values: R1: 100k, R2: omitted(!), R4: 100k, R5: 4,7k. A 3.3V-setup is helpful if you want to battery-power your Tonuino and 5V is not available in battery-mode. For example this is the case when using Wemos Lolin32 with only having LiPo connected. <br />
Advice: When powering a SD-card-reader solely with 3.3V, make sure to use one WITHOUT a voltage regulator. Or at least one with a pin dedicated for 3.3V (bypassing voltage regulator). This is because if 3.3V go through the voltage regulator a small voltage-drop will be introduced, which may lead to SD-malfunction as the resulting voltage is a bit too low. Vice versa if you want to connect your reader solely to 5V, make sure to have one WITH a voltage regulator :-).
Make sure to enable `SD_MMC_1BIT_MODE` if you want to use this feature. SD-MMC-mode requires these fixed PINs listed above. You can find a good comparison of different SD-card-modes here: (https://www.instructables.com/Select-SD-Interface-for-ESP32/).
Advice: Double check that above PINs are not used elsewhere (e.g. GPIO2 is used as PREVIOUS_BUTTON as per default in settings.h).
## Wiring (1 SPI-instance) [EXPERIMENTAL!]
## Wiring (1 SPI-instance: RC522 + SD) [EXPERIMENTAL, maybe not working!]
Basically the same as using 2 SPI-instances but...
In this case RFID-reader + SD-reader share SPI's SCK, MISO and MOSI. But make sure to use different CS-pins.
In this case RFID-reader + SD-reader share SPI's SCK, MISO and MOSI. But make sure to use different CS-pins. Have to admit I had some problems to get this running. Seems to be connected properly, but nothing happens when an RFID-tag is applied. Maybe anybody else wants to point out :-)
| ESP32 (GPIO) | Hardware | Pin | Comment |
| ------------- | --------------------- | ------ | ------------------------------------------------------------ |
| 5 V | SD-reader | VCC | Connect to p-channel MOSFET for power-saving when uC is off |
| 3.3 (5) V | SD-reader | VCC | Connect to p-channel MOSFET for power-saving when uC is off |
| GND | SD-reader | GND | |
| 15 | SD-reader | CS | Don't share with RFID! |
| 17 | RFID-reader | 3.3V | Connect directly to GPIO 17 for power-saving when uC is off |
| 3.3 V | RFID-reader | 3.3V | Connect directly to GPIO 17 for power-saving when uC is off |
| GND | RFID-reader | GND | |
| 21 | RFID-reader | CS/SDA | Don't share with SD! |
| 23 | RFID+SD-reader | MOSI | |
| 19 | RFID+SD-reader | MISO | |
| 18 | RFID+SD-reader | SCK | |
| 5 V | MAX98357 | VIN | Connect to p-channel MOSFET for power-saving when uC is off |
| 5 / 3.3 V | MAX98357 | VIN | Connect to p-channel MOSFET for power-saving when uC is off |
| GND | MAX98357 | GND | |
| 25 | MAX98357 | DIN | |
| 27 | MAX98357 | BCLK | |
| 26 | MAX98357 | LRC | |
| --- | MAX98357 | SD | Info: if pulled down to GND amp will turn off |
| 34 | Rotary encoder | CLR | Invert CLR with DT if you want to change the direction of RE |
| 35 | Rotary encoder | DT | Invert CLR with DT if you want to change the direction of RE |
| 34 | Rotary encoder | CLR | Change CLR with DT if you want to change the direction of RE |
| 35 | Rotary encoder | DT | Change CLR with DT if you want to change the direction of RE |
| 32 | Rotary encoder | BUTTON | |
| 3.3 V | Rotary encoder | + | |
| GND | Rotary encoder | GND | |
@ -112,39 +199,40 @@ In this case RFID-reader + SD-reader share SPI's SCK, MISO and MOSI. But make su
| GND | Button (previous) | | |
| 5 | Button (pause/play) | | |
| GND | Button (pause/play) | | |
| 5 V | Neopixel | 5 V | Connect to p-channel MOSFET for power-saving when uC is off |
| 5 / 3.3 V | Neopixel | 5 V | Connect to p-channel MOSFET for power-saving when uC is off |
| GND | Neopixel | GND | |
| 12 | Neopixel | DI | Might be necessary to use a logic-converter 3.3 => 5V |
| 17 | (e.g.) BC337 (via R5) | Base | Don't forget R5! |
| 33 | Voltage-divider / BAT | | Optional: Voltage-divider to monitor battery-voltage |
| 12 | Neopixel | DI | |
| 17 | N-channel Mosfet | Gate | |
| 33 | Voltage-divider / BAT | | Optional: voltage-divider to monitor battery-voltage |
| 22 | Headphone jack | | Optional: if pulled to ground, headphone-volume is set |
## Wiring (custom) / different pinout
When using a develboard with for example SD-card-reader already integrated (Lolin D32 Pro), the pinouts described above my not fit; feel free to change them according your needs. Additionaly some boards may use one or some of the GPIOs I used for their internal purposes and that reason for are maybe not exposed via pin-headers. However, having them exposed doesn't mean they can be used without limits. This is because some GPIOs have to be logical LOW or HIGH at start for example and this is probably not the case when connecting stuff to it. Feel free to adjust the GPIOs proposed by me (but be adviced it could take a while to get it running). If you encounter problems please refer the board's manual first. <br />
Keep in mind the RFID-lib I used is intended for default-SPI-pins only (SCK, MISO, MOSI). [Here](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/Hardware-Plaforms/ESP32-A1S-Audiokit) I described a solution for a board with many GPIOs used internally and a very limited number of GPIOs exposed. That's why I had to use different SPI-GPIOs for RFID as well. Please note I used a slightly modified [RFID-lib](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/Hardware-Plaforms/ESP32-A1S-Audiokit/lib/MFRC522) there.
## Prerequisites / tipps
* choose if optional modules (MQTT, FTP, Neopixel) should be compiled/enabled
* for debugging-purposes serialDebug can be set to ERROR, NOTICE, INFO or DEBUG.
* if MQTT=yes, set the IP of the MQTT-server accordingly and check the MQTT-topics (states and commands)
* if Neopixel enabled: set NUM_LEDS to the LED-number of your Neopixel-ring and define the Neopixel-type using `#define CHIPSET`
* If you're using Arduino-IDE please make sure to change ESP32's partition-layout to `No OTA (2MB APP/2MB Spiffs)` as otherwise the sketch won't fit into the flash-memory.
* Please keep in mind that working SD is mandatory. Unless `SD_NOT_MANDATORY_ENABLE` is not set, Tonuino will never fully start up if SD is not working. Only use `SD_NOT_MANDATORY_ENABLE` for debugging as for normal operational mode, not having SD working doesn't make sense.
* If you want to monitor the battery-voltage, make sure to enable `MEASURE_BATTERY_VOLTAGE`. Use a voltage-divider as voltage of a LiPo is way too high for ESP32 (only 3.3V supported!). For my tests I connected VBat with a serial connection of 130k + 390k resistors (VBat--130k--X--390k--GND). X is the measure-point where to connect the GPIO to.
* If you're using a headphone-pcb with a [headphone jack](https://www.conrad.de/de/p/cliff-fcr1295-klinken-steckverbinder-3-5-mm-buchse-einbau-horizontal-polzahl-3-stereo-schwarz-1-st-705830.html) that has a pin to indicate if there's a plug, you can use this signal along with the feature `HEADPHONE_ADJUST_ENABLE` to limit the maximum headphone-voltage automatically. As per default you have to invert this signal and connect it to GPIO22.
* Enabling `SHUTDOWN_IF_SD_BOOT_FAILS` is really recommended if you run your Tonuino in battery-mode without having a restart-button exposed to the outside of the Tonuino's enclosure. Because otherwise there's no way to restart your Tonuino and the error-state will remain until battery is empty (or you open the enclosure, hehe).
* compile and upload the sketch
## Wiring (PN5180 instead of MFRC522) different to above
PN5180 reader needs two more PIN's, RESET and BUSY. Double check PIN conflicts! `RFID_READER_TYPE_PN5180` needs to be enabled to use this feature. Make sure to disable `RFID_READER_TYPE_MFRC522`!
| ESP32 (GPIO) | Hardware | Pin | Comment |
| ------------- | --------------------- | ------ | ------------------------------------------------------------ |
| 3.3 V | PN5180 RFID-reader | 3.3V | Connect directly to GPIO 17 for power-saving when uC is off |
| 5 / 3.3 V | PN5180 RFID-reader | 5V | Don't forget to connect this pin the same way as 3.3V |
| GND | PN5180 RFID-reader | GND | |
| 21 | PN5180 RFID-reader | CS/SDA | Same as MFRC522. Don't share with SD! |
| 23 | PN5180 RFID-reader | MOSI | Same as MFRC522 |
| 19 | PN5180 RFID-reader | MISO | Same as MFRC522 |
| 18 | PN5180 RFID-reader | SCK | Same as MFRC522 |
| 16 | PN5180 RFID-reader | BUSY | be aware of SD MISO if running in SPI mode |
| 22 | PN5180 RFID-reader | RST | be aware of Headphone jack PIN |
## Starting Tonuino-ESP32 first time
After plugging in it takes a few seconds until neopixel indicates that Tonuino is ready (by four (slow) rotating LEDs; white if Wifi enabled and blue if disabled). If uC was not able to connect to WiFi, an access-point (named Tonuino) is opened and after connecting this WiFi, a [configuration-Interface](http://192.168.4.1) is available via webbrowser. Enter WiFI-credentials + the hostname (Tonuio's name), save them and restart the uC. Then reconnect to your "regular" WiFi. Now you're ready to go: start learning RFID-tags via GUI. To reach the GUI enter the IP stated in the serial console or use the hostname. For example if you're using a Fritzbox as router and entered tonuino as hostname in the previous configuration-step, in your webbrowser tonuino.fritz.box should work. After doing rfid-learnings, place your RFID-tag next to the RFID-reader and the music (or whatever else you choosed) should start to play. While the playlist is generated/processed, fast-rotating LEDs are shown to indicate that Tonuino is busy. The more tracks a playlist/directory contains the longer this step will take. <br >
Please note: hostname can be used to call webgui or FTP-server. I tested it with a Fritzbox 7490 and worked fine. Make sure you don't use a name that already exists in you local network (LAN).
## Wiring (custom) / different pinout
When using a develboard with SD-card-reader already integrated (Lolin D32 Pro, several TTGO-boards), the pinouts described above my not fit. Feel free to change them according your needs. Additionaly some boards may use one or some of the GPIOs I used for their internal purposes and that reason for are maybe not exposed via pin-headers. However, having them exposed doesn't mean they can be used without limits. This is because some GPIOs have to be logical LOW or HIGH at start/boot for example and this is probably not the case when connecting stuff to it. Feel free to adjust the GPIOs proposed by me (but be adviced it could take a while to get it running). If you encounter problems please refer the board's manual first. <br />
[Here](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/Hardware-Plaforms/ESP32-A1S-Audiokit) I described a solution for a board with many GPIOs used internally and a very limited number of GPIOs exposed. That's why I had to use different SPI-GPIOs for RFID as well. Please note I used a slightly modified [RFID-lib](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/Hardware-Plaforms/ESP32-A1S-Audiokit/lib/MFRC522) there. Please note: the code-basis is outdated. Will need to re-integrate it into my master-branch...
## WiFi
WiFi is mandatory for webgui, FTP and MQTT. However, WiFi can be temporarily or permanently disabled. There are two ways to do that:
* Use a special modification-card that can be configured via webgui
* Press previous-key (and keep pressed!) + press next-button in parallel shortly. Now release both.
This toggles the current WiFi-status which means: if it's currently enabled, it will be disabled instantly and vice versa. Please note: This WiFi-status will remain until you change it again. Having Wifi enabled is indicated in idle-mode (no playlist active) with four white slow rotating LEDs whereas disabled WiFi is represented by those ones colored blue.
* Press previous-key (and keep it pressed) + press next-button in parallel shortly. Now release both.
This toggles the current WiFi-status: if it's currently enabled, it will be disabled instantly and vice versa. Please note: this WiFi-status will remain until you change it again, which means, that Tonuino will remember this state after the next reboot. Having Wifi enabled is indicated in idle-mode (no playlist active) with four *white* slow rotating LEDs whereas disabled WiFi is represented by those ones coloured *blue*.
## After Tonuino-ESP32 is connected to your WiFi
After getting Tonuino part of your LAN/WiFi, the 'regular' webgui is available at the IP assigned by your router (or the configured hostname). Using this GUI, you can configure:
After bringing Tonuino part of your LAN/WiFi, the 'regular' webgui is available at the IP assigned by your router (or the configured hostname). Using this GUI, you can configure:
* WiFi
* Binding between RFID-tag, file/directory/URL and playMode
* Binding between RFID-tag and a modification-type
@ -167,20 +255,27 @@ Webgui #4:
Webgui #5:
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/pictures/Mgmt-GUI5.jpg" width="300">
Webgui #6:
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/pictures/Mgmt-GUI6.jpg" width="300">
Webgui #7:
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/pictures/Mgmt-GUI7.jpg" width="300">
Webgui: websocket broken:
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/pictures/Mgmt-GUI_connection_broken.jpg" width="300">
Webgui: action ok:
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/pictures/Mgmt_GUI_action_ok.jpg" width="300">
Please note: as you apply a RFID-tag to the RFID-reader, the corresponding ID is pushed to the GUI (and flashes a few times; so you can't miss it). So there's no need to enter such IDs manually (unless you want to).
Please note: as you apply a RFID-tag to the RFID-reader, the corresponding ID is pushed to the GUI. So there's no need to enter such IDs manually (unless you want to). Filepath can be filled out by selecting a file/directory in the tree.
IMPORTANT: Every time you add, delete or rename stuff on the SD-card, it's necessary to re-index the json-indexfile. Simply click on the refresh-button below the filetree and wait until it's done. This is because all operations of the filebrowser rely on this file in order to provide a fast way to access all the files and directories.
## Interacting with Tonuino
### Playmodes
It's not just simply playing music; different playmodes are supported:
* single track
* single track (loop)
* audiobook (single file or playlist; last play-position (file and playlist) is saved and re-used next time)
* audiobook (single file or playlist/folder; last play-position (file and playlist) is saved and re-used next time)
* audiobook (loop)
* folder/playlist (alph. sorted)
* folder/playlist (random order)
@ -189,7 +284,7 @@ It's not just simply playing music; different playmodes are supported:
* webradio (always only one "track")
### Modification RFID-tags
There are special RFID-tags, that don't start music by themself but can modify things. If applied a second time, it's previous action will be reversed. Please note: all sleep-modes do dimming automatically because it's supposed to be used in the evening when going to bed. Well, at least that's my children's indication :-) So first make sure to start the music then use a modification-card in order to apply your desired modification:
There are special RFID-tags, that don't start music by themself but can modify things. If applied a second time, it's previous action/modification will be reversed. Please note: all sleep-modes do dimming (Neopixel) automatically because it's supposed to be used in the evening when going to bed. Well, at least that's my children's indication :-) So first make sure to start the music then use a modification-card in order to apply your desired modification:
* lock/unlock all buttons
* sleep after 5/30/60/120 minutes
* sleep after end of current track
@ -204,19 +299,21 @@ There are special RFID-tags, that don't start music by themself but can modify t
### Neopixel-ring (optional)
Indicates different things. Don't forget configuration of number of LEDs via #define NUM_LEDS
* While booting: 1/2 LEDs rotating orange
* Unable to mount SD: LEDs flashing red (will remain forever unless SD-card is available)
* Unable to mount SD: LEDs flashing red (will remain forever unless SD-card is available or `SHUTDOWN_IF_SD_BOOT_FAILS` is active)
* IDLE: four LEDs slow rotating (white if WiFi enabled; blue if WiFi disabled)
* ERROR: all LEDs flashing red (1x)
* OK: all LEDs flashing green (1x)
* BUSY: violet; four fast rotating LEDs
* ERROR: all LEDs flashing red (1x) if an action was not accepted
* OK: all LEDs flashing green (1x) if an action was accepted
* BUSY: violet; four fast rotating LEDs when generating a playlist
* track-progress: rainbow; number of LEDs relative to play-progress
* playlist-progress: blue; appears only shortly in playlist-mode with every new track; number of LEDs relative to progress
* volume: green => red-gradient; number of LEDs relative from actual to max volume
* webstream: two slow rotating LEDs that change their colours as the stream proceeds
* volume: green => red-gradient; number of LEDs relative from current to max volume
* switching off: red; circle that grows until long-press-time is reached
* buttons locked: track-progress-LEDs coloured red
* paused: track-progress-LEDs coloured orange
* rewind: if single-track-loop is activated a LED-rewind is performed when restarting the given track
* (Optional) Undervoltage: flashes three times red if battery-voltage is too low
* (Optional) Undervoltage: flashes three times red if battery-voltage is too low. This voltage-level can be configured via GUI.
* (Optional) Short press of rotary encoder's button provides battery-voltage visualisation via Neopixel. Upper und lower voltage cut-offs can be adjusted via GUI.
Please note: some Neopixels use a reversed addressing which leads to the 'problem', that all effects are shown
counter clockwise. If you want to change that behaviour, just enable `NEOPIXEL_REVERSE_ROTATION`.
@ -230,11 +327,13 @@ Some buttons have different actions if pressed long or short. Minimum duration f
* pause/play (short/long): pause/play
* rotary encoder (turning): vol +/-
* rotary encoder (button long): switch off (only when on)
* rotary encoder (button short): switch on (only when off)
* previous (long; keep pressed) + next (short): toggle WiFi enabled/disabled
* rotary encoder (button short): switch on (when switched off)
* rotary encoder (button short): show battery-voltage via Neopixel (when switched on and `MEASURE_BATTERY_VOLTAGE` is active)
* previous (long; keep pressed) + next (short) + release (both): toggle WiFi enabled/disabled
### Music-play
* music starts to play right after valid RFID-tag was applied
* if `PLAY_LAST_RFID_AFTER_REBOOT` is active, Tonuino will remember the last RFID applied => music-autoplay
* if a folder should be played that contains many mp3s, the playlist generation can take a few seconds. Please note that a file's name including path cannot exceed 255 characters.
* while playlist is generated Neopixel indicates BUSY-mode
* after last track was played, Neopixel indicates IDLE-mode
@ -257,10 +356,16 @@ After having Tonuino running on your ESP32 in your local WiFi, the webinterface-
* General-configuration (volume (speaker + headphone), neopixel-brightness (night-mode + initial), sleep after inactivity)
### FTP (optional)
In order to avoid exposing uSD-card or disassembling the Tonuino all the time for adding new music, it's possible to transfer music onto the uSD-card using FTP. Please make sure to set the max. number of parallel connections to ONE in your FTP-client. My recommendation is [Filezilla](https://filezilla-project.org/). But don't expect fast data-transfer. Initially it was around 145 kB/s but after modifying ftp-server-lib (changing from 4 kB static-buffer to 16 kB heap-buffer) I saw rates improving a bit. Please note: if music is played in parallel, this rate decrases dramatically! So better stop playback then doing a FTP-transfer. However, playback sounds normal if a FTP-upload is performed in parallel. Default-user and password are set via `ftpUser` and `ftpPassword` but can be changed later via GUI.
* In order to avoid exposing uSD-card or disassembling Tonuino all the time for adding new music, it's possible to transfer music to the uSD-card using FTP.
* Default-user and password are set to `esp32` / `esp32` but can be changed later via GUI.
* FTP needs to be activated after boot by pressing `PAUSE` + `NEXT`-buttons (in parallel) first! Neopixel flashes green (1x) if enabling was successful.
* Make sure to set the max. number of parallel connections to ONE in your FTP-client.
* Software: my recommendation is [Filezilla](https://filezilla-project.org/).
* Don't expect a super fast data-transfer; it's around 180 kB/s (SPI-mode) and >=300 kB/s (MMC-mode).
* Please note: if music is played in parallel, this rate decrases dramatically! So better stop playback when doing a FTP-transfer.
### Files / ID3-tags (IMPORTANT!)
Make sure to not use filenames that contain German 'Umlaute'. I've been told this is also true for mp3's ID3-tags.
Make sure to not use filenames that contain German 'Umlaute'. I've been told this is also true for mp3's ID3-tags. Also better remove coverarts from the files.
### Energy saving
As already described in the modify-section, there are different sleepmodes available. Additionaly uC will be put into deepsleep after 10 minutes of inactivity (configurable my maxInactivityTime) unless Tonuino doesn't play music, has a FTP-client connected and any input via buttons. Every button-interaction resets the counter to the initial value.
@ -286,7 +391,8 @@ Feel free to use your own smarthome-environments (instead of openHAB). The MQTT-
| topicSleepCmnd | 0 or OFF | Power off Tonuino immediately |
| topicSleepState | ON or OFF | Sends Tonuino's current/last state |
| topicTrackCmnd | 12 digits | Set number of RFID-tag which 'emulates' an RFID-tag (e.g. `123789456089`) |
| topicTrackState | 12 digits | Sends number of last RFID-tag applied |
| topicTrackState | String | Sends current track number, total number of tracks and full path of current |
| | | track. E.g. "(2/10) /mp3/kinderlieder/Ri ra rutsch.mp3"
| topicTrackControlCmnd | 1 -> 7 | `1`=stop; `2`=unused!; `3`=play/pause; `4`=next; `5`=prev; `6`=first; `7`=last |
| topicLoudnessCmnd | 0 -> 21 | Set loudness (depends on minVolume / maxVolume) |
| topicLoudnessState | 0 -> 21 | Sends loudness (depends on minVolume / maxVolume |

64
html/accesspoint.html

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<title>WLAN-Einrichtung</title>
<style>
input {
width: 90%;
height: 44px;
border-radius: 4px;
margin: 10px auto;
font-size: 15px;
background: #f1f1f1;
border: 0;
padding: 0 15px
}
input {
}
body {
background: #007bff;
font-family: sans-serif;
font-size: 14px;
color: #777
}
.box {
background: #fff;
max-width: 258px;
margin: 75px auto;
padding: 30px;
border-radius: 5px;
text-align: center
}
.btn {
background: #3498db;
color: #fff;
cursor: pointer;
width: 90%;
height: 44px;
border-radius: 4px;
margin: 10px auto;
font-size: 15px;
}
.rebootmsg {
display: none;
}
</style>
</head>
<body>
<form id="settings" action="/init" class="box" method="POST">
<h1>WLAN-Einrichtung</h1>
<label for="ssid">SSID:</label><br>
<input type="text" id="ssid" name="ssid" placeholder="SSID" required><br>
<label for="pwd">Passwort:</label><br>
<input type="password" id="pwd" name="pwd" autocomplete="off" required><br>
<label for="hostname">Tonuino-Name (Hostname):</label><br>
<input type="text" id="hostname" name="hostname" placeholder="Tonuino" required><br><br>
<input class="btn" type="submit" id="save-button" value="Save">
</form>
<form action="/restart" class="box">
<h1>Fertig?</h1>
<input class="btn" type="submit" id="restart-button" value="Reboot">
</form>
</body>
</html>

64
html/accesspoint_EN.html

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<title>WiFi-configuration</title>
<style>
input {
width: 90%;
height: 44px;
border-radius: 4px;
margin: 10px auto;
font-size: 15px;
background: #f1f1f1;
border: 0;
padding: 0 15px
}
input {
}
body {
background: #007bff;
font-family: sans-serif;
font-size: 14px;
color: #777
}
.box {
background: #fff;
max-width: 258px;
margin: 75px auto;
padding: 30px;
border-radius: 5px;
text-align: center
}
.btn {
background: #3498db;
color: #fff;
cursor: pointer;
width: 90%;
height: 44px;
border-radius: 4px;
margin: 10px auto;
font-size: 15px;
}
.rebootmsg {
display: none;
}
</style>
</head>
<body>
<form id="settings" action="/init" class="box" method="POST">
<h1>WiFi-configuration</h1>
<label for="ssid">SSID:</label><br>
<input type="text" id="ssid" name="ssid" placeholder="SSID" required><br>
<label for="pwd">Password:</label><br>
<input type="password" id="pwd" name="pwd" autocomplete="off" required><br>
<label for="hostname">Tonuino's name (hostname):</label><br>
<input type="text" id="hostname" name="hostname" placeholder="Tonuino" required><br><br>
<input class="btn" type="submit" id="save-button" value="Save">
</form>
<form action="/restart" class="box">
<h1>Ready to go?</h1>
<input class="btn" type="submit" id="restart-button" value="Reboot">
</form>
</body>
</html>

727
html/management.html

@ -0,0 +1,727 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title>ESPuino-Konfiguration</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://ts-cs.de/tonuino/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/11.0.2/css/bootstrap-slider.min.css" />
<script src="https://ts-cs.de/tonuino/js/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<script src="https://ts-cs.de/tonuino/js/popper.min.js"></script>
<script src="https://ts-cs.de/tonuino/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/11.0.2/bootstrap-slider.min.js"></script>
<style type="text/css">
.filetree {
border: 1px solid black;
height: 200px;
margin: 0em 0em 1em 0em;
overflow-y: scroll;
}
.slider.slider-horizontal {
width: 60%;
margin-left: 1em;
margin-right: 1em;
}
.slider-handle{
height: 30px;
width: 30px;
top: -5px;
}
legend.scheduler-border {
width:inherit; /* Or auto */
padding:0 10px; /* To give a bit of padding on the left and right */
border-bottom:none;
}
.icon-pos{
top: 0.3em;
position: relative;
}
.reboot{
color:white;
}
.reboot:hover{
color: orange;
}
.fa-sync:hover {
color: #666666;
}
.clickForRefresh {
text-align: center;
color: gray;
cursor: pointer;
margin-top: 5em;
}
.clickForRefresh:hover {
color: darkgray;
}
.filetree-container {
position: relative;
}
.indexing-progress {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
opacity: 0.7;
display: none;
}
.refreshAction{
text-align: right;
font-size: 0.8em;
}
.refreshAction:hover{
cursor: pointer;
color: darkgray;
}
.overlay {
z-index: 9;
opacity: 0.8;
background: #1a1919;
height: 200px;
display: none;
width: 100%;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
<div class="col-md-12">
<a class="float-left navbar-brand">
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/html/tonuino_logo.png"
width="30" height="30" class="d-inline-block align-top" alt=""/>
Tonuino
</a>
<a class="reboot float-right nav-link" href="/restart"><i class="fas fa-power-off"></i> Neustart</a>
</div>
</nav>
<br/>
<nav>
<div class="container nav nav-tabs" id="nav-tab" role="tablist">
<a class="nav-item nav-link active" id="nav-rfid-tab" data-toggle="tab" href="#nav-rfid" role="tab" aria-controls="nav-rfid" aria-selected="true"><i class="fas fa-dot-circle"></i> RFID</a>
<a class="nav-item nav-link" id="nav-wifi-tab" data-toggle="tab" href="#nav-wifi" role="tab" aria-controls="nav-wifi" aria-selected="false"><i class="fas fa-wifi"></i><span class=".d-sm-none .d-md-block"> WLAN</span></a>
<a class="nav-item nav-link" id="nav-mqtt-tab" data-toggle="tab" href="#nav-mqtt" role="tab" aria-controls="nav-mqtt" aria-selected="false"><i class="fas fa-network-wired"></i> MQTT</a>
<a class="nav-item nav-link" id="nav-ftp-tab" data-toggle="tab" href="#nav-ftp" role="tab" aria-controls="nav-ftp" aria-selected="false"><i class="fas fa-folder"></i> FTP</a>
<a class="nav-item nav-link" id="nav-general-tab" data-toggle="tab" href="#nav-general" role="tab" aria-controls="nav-general" aria-selected="false"><i class="fas fa-sliders-h"></i> Allgemein</a>
<a class="nav-item nav-link" id="nav-tools-tab" data-toggle="tab" href="#nav-tools" role="tab" aria-controls="nav-tools" aria-selected="false"><i class="fas fa-wrench"></i> Tools</a>
</div>
</nav>
<br>
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade" id="nav-wifi" role="tabpanel" aria-labelledby="nav-wifi-tab">
<div class="container" id="wifiConfig">
<form action="#wifiConfig" method="POST" onsubmit="wifiConfig('wifiConfig'); return false">
<div class="form-group col-md-12">
<label for="ssid">WLAN-Name (SSID):</label>
<input type="text" class="form-control" id="ssid" placeholder="SSID" name="ssid" required>
<div class="invalid-feedback">
Bitte SSID des WLANs eintragen.
</div>
<label for="pwd">Passwort:</label>
<input type="password" class="form-control" id="pwd" placeholder="Passwort" name="pwd" required>
<label for="hostname">Tonuino-Name (Hostname):</label>
<input type="text" class="form-control" id="hostname" placeholder="tonuino" name="hostname"
value="%HOSTNAME%" pattern="^[^-\.]{2,32}" required>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</div>
</form>
</div>
</div>
<div class="tab-pane fade show active" id="nav-rfid" role="tabpanel" aria-labelledby="nav-rfid-tab">
<div class="container" id="rfidMusicTags">
<fieldset>
<legend>RFID-Zuweisungen</legend>
<form action="#rfidMusicTags" method="POST" onsubmit="rfidAssign('rfidMusicTags'); return false">
<div class="form-group col-md-12">
<label for="rfidIdMusic">RFID-Chip-Nummer (12-stellig)</label>
<input type="text" class="form-control" id="rfidIdMusic" maxlength="12" pattern="[0-9]{12}"
placeholder="%RFID_TAG_ID%" name="rfidIdMusic" required>
<label for="fileOrUrl">Datei, Verzeichnis oder URL (^ und # als Zeichen nicht erlaubt)</label>
<input type="text" class="form-control" id="fileOrUrl" maxlength="255" placeholder="z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3" pattern="^[^\^#]+$" name="fileOrUrl" required>
<div class="filetree-container">
<div id="filebrowser">
<div class="filetree demo" id="filetree"></div>
<div class="refreshAction">
<span id="refreshAction" data-toggle="tooltip" data-placement="top" title="Datei Liste aktualisieren."><i class="fas fa-sync fa-1x"></i> Dateiliste aktualisieren</span>
</div>
</div>
<div id="indexing-progress" class="indexing-progress overlay">
<div style="text-align: center; color:white; margin-top:2em;">
<div><i class="fas fa-sync fa-spin fa-2x"></i> <br><br> Der Prozess kann mehrere Minuten dauern...</div>
<div id="currentProcessedFile"></div>
</div>
</div>
</div>
<label for="playMode">Abspielmodus</label>
<select class="form-control" id="playMode" name="playMode">
<option class="option-file" value="1">Einzelner Titel</option>
<option class="option-file" value="2">Einzelner Titel (Endlosschleife)</option>
<option class="option-file-and-folder" value="3">Hörbuch</option>
<option class="option-file-and-folder" value="4">Hörbuch (Endlosschleife)</option>
<option class="option-folder" value="5">Alle Titel eines Verzeichnis (sortiert)</option>
<option class="option-folder" value="6">Alle Titel eines Verzeichnis (zufällig)</option>
<option class="option-folder" value="7">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option>
<option class="option-folder" value="9">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>
<option class="option-stream" value="8">Webradio</option>
</select>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</div>
</form>
</fieldset>
</div>
<br>
<br>
<div class="container" id="rfidModTags">
<fieldset>
<legend>RFID-Modifkationen</legend>
<form class="needs-validation" action="#rfidModTags" method="POST" onsubmit="rfidMods('rfidModTags'); return false">
<div class="form-group col-md-12">
<label for="rfidIdMod">RFID-Chip-Nummer (12-stellig)</label>
<input type="text" class="form-control" id="rfidIdMod" maxlength="12" pattern="[0-9]{12}"
placeholder="%RFID_TAG_ID%" name="rfidIdMod" required>
<div class="invalid-feedback">
Bitte eine 12-stellige Zahl eingeben.
</div>
<label for="modId">Konfiguraiton</label>
<select class="form-control" id="modId" name="modId">
<option value="100">Tastensperre</option>
<option value="101">Schlafen nach 15 Minuten</option>
<option value="102">Schlafen nach 30 Minuten</option>
<option value="103">Schlafen nach 1 Stunde</option>
<option value="104">Schlafen nach 2 Stunden</option>
<option value="105">Schlafen nach Ende des Titels</option>
<option value="106">Schlafen nach Ende der Playlist</option>
<option value="107">Schlafen nach fünf Titeln</option>
<option value="110">Wiederhole Playlist (endlos)</option>
<option value="111">Wiederhole Titel (endlos)</option>
<option value="112">Dimme LEDs (Nachtmodus)</option>
<option value="130">Aktiviere/deaktive WLAN</option>
</select>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</div>
</form>
</fieldset>
</div>
</div>
<div class="tab-pane fade" id="nav-mqtt" role="tabpanel" aria-labelledby="nav-mqtt-tab">
<div class="container" id="mqttConfig">
<form class="needs-validation" action="#mqttConfig" method="POST"
onsubmit="mqttSettings('mqttConfig'); return false">
<div class="form-check col-md-12">
<input class="form-check-input" type="checkbox" value="1" id="mqttEnable" name="mqttEnable" %MQTT_ENABLE%>
<label class="form-check-label" for="mqttEnable">
MQTT aktivieren
</label>
</div>
<div class="form-group my-2 col-md-12">
<label for="mqttServer">MQTT-Server</label>
<input type="text" class="form-control" id="mqttServer" minlength="7" maxlength="%MQTT_SERVER_LENGTH%"
placeholder="z.B. 192.168.2.89" name="mqttServer" value="%MQTT_SERVER%">
<label for="mqttUser">MQTT-Benutzername (optional):</label>
<input type="text" class="form-control" id="mqttUser" maxlength="%MQTT_USER_LENGTH%"
placeholder="Benutzername" name="mqttUser" value="%MQTT_USER%">
<label for="mqttPwd">MQTT-Passwort (optional):</label>
<input type="password" class="form-control" id="mqttPwd" maxlength="%MQTT_PWD_LENGTH%"
placeholder="Passwort" name="mqttPwd" value="%MQTT_PWD%">
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="nav-ftp" role="tabpanel" aria-labelledby="nav-ftp-tab">
<div class="container" id="ftpConfig">
<form action="#ftpConfig" method="POST" onsubmit="ftpSettings('ftpConfig'); return false">
<div class="form-group col-md-12">
<label for="ftpUser">FTP-Benutzername:</label>
<input type="text" class="form-control" id="ftpUser" maxlength="%FTP_USER_LENGTH%"
placeholder="Benutzername" name="ftpUser" value="%FTP_USER%" required>
<label for="pwd">FTP-Passwort:</label>
<input type="password" class="form-control" id="ftpPwd" maxlength="%FTP_PWD_LENGTH%" placeholder="Passwort"
name="ftpPwd" value="%FTP_PWD%" required>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="nav-general" role="tabpanel" aria-labelledby="nav-general-tab">
<div class="container" id="generalConfig">
<form action="#generalConfig" method="POST" onsubmit="genSettings('generalConfig'); return false">
<div class="form-group col-md-12">
<fieldset>
<legend class="w-auto">Lautstärke</legend>
<label for="initialVolume">Nach dem Einschalten</label>
<div class="text-center">
<i class="fas fa-volume-down fa-2x .icon-pos"></i> <input data-provide="slider" type="number" data-slider-min="1" data-slider-max="21" min="1" max="21" class="form-control" id="initialVolume" name="initialVolume"
data-slider-value="%INIT_VOLUME%" value="%INIT_VOLUME%" required> <i class="fas fa-volume-up fa-2x .icon-pos"></i></div>
<br>
<label for="maxVolumeSpeaker">Maximal (Lautsprecher)</label>
<div class="text-center">
<i class="fas fa-volume-down fa-2x .icon-pos"></i> <input data-provide="slider" type="number" data-slider-min="1" data-slider-max="21" min="1" max="21" class="form-control" id="maxVolumeSpeaker" name="maxVolumeSpeaker"
data-slider-value="%MAX_VOLUME_SPEAKER%" value="%MAX_VOLUME_SPEAKER%" required> <i class="fas fa-volume-up fa-2x .icon-pos"></i>
</div>
<br>
<label for="maxVolumeHeadphone">Maximal (Kopfhörer)</label>
<div class="text-center">
<i class="fas fa-volume-down fa-2x .icon-pos"></i> <input data-provide="slider" type="number" data-slider-min="1" data-slider-max="21" min="1" max="21" class="form-control" id="maxVolumeHeadphone" name="maxVolumeHeadphone"
data-slider-value="%MAX_VOLUME_HEADPHONE%" value="%MAX_VOLUME_HEADPHONE%" required> <i class="fas fa-volume-up fa-2x .icon-pos"></i>
</div>
</fieldset>
</div>
<br>
<div class="form-group col-md-12">
<fieldset >
<legend class="w-auto">Neopixel (Helligkeit)</legend>
<label for="initBrightness">Nach dem Einschalten:</label>
<div class="text-center">
<i class="far fa-sun fa-2x .icon-pos"></i>
<input data-provide="slider" type="number" data-slider-min="0" data-slider-max="255" min="0" max="255" class="form-control" id="initBrightness" name="initBrightness"
data-slider-value="%INIT_LED_BRIGHTNESS%" value="%INIT_LED_BRIGHTNESS%" required><i class="fas fa-sun fa-2x .icon-pos"></i>
</div>
<label for="nightBrightness">Im Nachtmodus</label>
<div class="text-center">
<i class="far fa-sun fa-2x .icon-pos"></i><input data-provide="slider" type="number" data-slider-min="0" data-slider-max="255" min="0" max="255" class="form-control" id="nightBrightness" name="nightBrightness" data-slider-value="%NIGHT_LED_BRIGHTNESS%" value="%NIGHT_LED_BRIGHTNESS%" required><i class="fas fa-sun fa-2x .icon-pos"></i>
</div>
</fieldset>
</div>
<br>
<div class="form-group col-md-12">
<fieldset>
<legend>Deep Sleep</legend>
<label for="inactivityTime">Inaktivität nach (in Minuten)</label>
<div class="text-center"><i class="fas fa-hourglass-start fa-2x .icon-pos"></i> <input type="number" data-provide="slider" data-slider-min="0" data-slider-max="30" min="1" max="120" class="form-control" id="inactivityTime" name="inactivityTime"
data-slider-value="%MAX_INACTIVITY%" value="%MAX_INACTIVITY%" required><i class="fas fa-hourglass-end fa-2x .icon-pos"></i></div>
</fieldset>
</div>
<br>
<div class="form-group col-md-12">
<fieldset>
<legend>Batterie</legend>
<div>Status über Neopixel anzeigen</div>
<br>
<label for="warningLowVoltage">Unter dieser Spannung wird eine Warnung angezeit.
</label>
<div class="text-center">
<i class="fas fa-battery-quarter fa-2x .icon-pos"></i> <input data-provide="slider" data-slider-step="0.1" data-slider-min="3.0" data-slider-max="5.0" min="3.0" max="5.0" type="text" class="form-control" id="warningLowVoltage" name="warningLowVoltage"
data-slider-value="%WARNING_LOW_VOLTAGE%" value="%WARNING_LOW_VOLTAGE%" pattern="^\d{1,2}(\.\d{1,3})?" required> <i class="fas fa-battery-three-quarters fa-2x .icon-pos" fa-2x .icon-pos></i>
</div>
<br>
<label for="voltageIndicatorLow">Eine LED leuchtet bei dieser Spannung
</label>
<div class="text-center">
<i class="fas fa-battery-quarter fa-2x .icon-pos"></i> <input data-provide="slider" min="2.0" data-slider-step="0.1" data-slider-min="2.0" data-slider-max="5.0" max="5.0"type="text" class="form-control" id="voltageIndicatorLow" name="voltageIndicatorLow"
data-slider-value="%VOLTAGE_INDICATOR_LOW%" value="%VOLTAGE_INDICATOR_LOW%" pattern="^\d{1,2}(\.\d{1,3})?" required> <i class="fas fa-battery-three-quarters fa-2x .icon-pos" fa-2x .icon-pos></i>
</div>
<br>
<label for="voltageIndicatorHigh">Alle LEDs leuchten bei dieser Spannung</label>
<div class="text-center">
<i class="fas fa-battery-quarter fa-2x .icon-pos"></i><input data-provide="slider" data-slider-step="0.1" data-slider-min="2.0" data-slider-max="5.0" min="2.0" max="5.0" type="text" class="form-control" id="voltageIndicatorHigh" name="voltageIndicatorHigh"
data-slider-value="%VOLTAGE_INDICATOR_HIGH%" value="%VOLTAGE_INDICATOR_HIGH%" pattern="^\d{1,2}(\.\d{1,3})?" required> <i class="fas fa-battery-three-quarters fa-2x .icon-pos" fa-2x .icon-pos></i>
</div>
<br>
<label for="voltageCheckInterval">Zeitabstand der Messung (in Minuten)</label>
<div class="text-center"><i class="fas fa-hourglass-start fa-2x .icon-pos"></i>
<input data-provide="slider" data-slider-min="1" data-slider-max="60" type="number" min="1" max="60" class="form-control" id="voltageCheckInterval"
data-slider-value="%VOLTAGE_CHECK_INTERVAL%" name="voltageCheckInterval" value="%VOLTAGE_CHECK_INTERVAL%" required><i class="fas fa-hourglass-end fa-2x .icon-pos"></i>
</div>
</fieldset>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="nav-tools" role="tabpanel" aria-labelledby="nav-tools-tab">
<div class="container my-5" id="importNvs">
<h2>NVS-Importer</h2>
<form action="/upload" enctype="multipart/form-data" method="POST">
<div class="form-group">
<label for="nvsUpload">Hier kann eine Backup-Datei importiert werden.</label>
<input type="file" class="form-control-file" id="nvsUpload" name="nvsUpload" accept=".txt">
</div>
<br>
<div class="text-center">
<button type="submit" class="btn btn-primary">Absenden</button>
</div>
</form>
</div>
</div>
</div>
<script type="text/javascript">
var DEBUG = false;
var lastIdclicked = '';
var host = $(location).attr('hostname');
if (DEBUG) {
host = "192.168.178.114";
}
toastr.options = {
"closeButton": false,
"debug": false,
"newestOnTop": false,
"progressBar": false,
"positionClass": "toast-top-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
function postRendering(event, data) {
Object.keys(data.instance._model.data).forEach(function (key, index) {
var cur = data.instance.get_node(data.instance._model.data[key]);
var lastFolder = cur['id'].split('/').filter(function (el) {
return el.trim().length > 0;
}).pop();
if ((/\.(mp3|MP3|ogg|wav|WAV|OGG|wma|WMA|acc|ACC|flac|FLAC)$/i).test(lastFolder)) {
data.instance.set_type(data.instance._model.data[key], 'audio');
} else {
if (data.instance._model.data[key]['type'] == "file") {
data.instance.disable_node(data.instance._model.data[key]);
}
}
data.instance.rename_node(data.instance._model.data[key], lastFolder);
});
}
function renderFileTree() {
var filesURI = "/files";
if (DEBUG) {
filesURI = "http://" + host + "/files";
}
$('#filetree').jstree({
'core': {
'check_callback': true,
'data': {
url: filesURI,
error: function (XMLHttpRequest, textStatus, errorThrown) {
$('#j1_loading').hide();
$("#refreshAction").hide();
$('#filetree').html("<div class='clickForRefresh' ><i class='fas fa-sync fa-1x'><span id='#clickForRefresh' > Dateien suchen.</span></div>");
$('#filetree').on("click", function () {
refreshFileList();
});
toastr.error("Die Verzeichnis-Liste konnte nicht geladen werden.");
}
},
},
'types': {
'folder': {
'icon': "fa fa-folder"
},
'file': {
'icon': "fa fa-file"
},
'audio': {
'icon': "fa fa-file-audio"
},
'default': {
'icon': "fa fa-folder"
}
},
'plugins': ["themes", "types"]
}).bind('loaded.jstree', function (event, data) {
postRendering(event, data);
if ((data.instance._model.data['#']['children'].length == 0)) {
toastr.info("Der SD-Karten-Index muss erzeugt werden.");
}
}).bind('refresh.jstree', function (event, data) {
postRendering(event, data);
});
}
$('#filetree').on('select_node.jstree', function (e, data) {
$('input[name=fileOrUrl]').val(data.node.id);
if (data.node.type == "folder") {
$('.option-folder').show();
$('.option-file').hide();
$('#playMode option').removeAttr('selected').filter('[value=3]').attr('selected', true);
}
if (data.node.type == "audio") {
$('.option-file').show();
$('.option-folder').hide();
$('#playMode option').removeAttr('selected').filter('[value=1]').attr('selected', true);
}
});
$('#refreshAction').on("click", function () {
refreshFileList();
$("#indexing-progress").show();
$("#refreshAction").hide();
});
$('#playMode').on("change", function () {
if (this.value == 8) {
$('#filebrowser').slideUp();
} else {
$('#filebrowser').slideDown();
}
});
function showFileIndexingState() {
$("#indexing-progress").show();
$("#refreshAction").hide();
}
function hideFileIndexingState() {
$("#indexing-progress").hide();
$("#refreshAction").show();
}
var socket = undefined;
var tm;
function connect() {
socket = new WebSocket("ws://" + host + "/ws");
socket.onopen = function () {
setInterval(ping, 15000);
};
socket.onclose = function (e) {
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
socket = null;
setTimeout(function () {
connect();
}, 5000);
};
socket.onerror = function (err) {
console.error('Socket encountered error: ', err.message, 'Closing socket');
};
socket.onmessage = function(event) {
console.log(event.data);
var socketMsg = JSON.parse(event.data);
if (socketMsg.rfidId != null) {
document.getElementById('rfidIdMod').value = socketMsg.rfidId;
document.getElementById('rfidIdMusic').value = socketMsg.rfidId;
toastr.info("RFID Tag mit "+ socketMsg.rfidId + " erkannt." );
$("#rfidIdMusic").effect("highlight", {color:"#abf5af"}, 3000);
$("#rfidIdMod").effect("highlight", {color:"#abf5af"}, 3000);
} if ("status" in socketMsg) {
if (socketMsg.status == "ok") {
toastr.success("Aktion erfolgreich ausgeführt." );
}
} if ("pong" in socketMsg) {
if (socketMsg.pong == 'pong') {
pong();
}
} if ("refreshFileList" in socketMsg) {
hideFileIndexingState();
toastr.info("Die Dateiliste wurde neu erzeugt!");
$('#filetree').jstree(true).refresh();
}
if ("indexingState" in socketMsg) {
if(socketMsg.indexingState != null) {
$("#currentProcessedFile").text(socketMsg.indexingState);
console.log(socketMsg.indexingState);
}
}
};
}
function ping() {
var myObj = {
"ping": {
ping: 'ping'
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
tm = setTimeout(function () {
toastr.warning('Die Verbindung zum Tonuino ist unterbrochen! Bitte Seite neu laden.');
}, 5000);
}
function pong() {
clearTimeout(tm);
}
function refreshFileList(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"refreshFileList": true
};
var myJSON = JSON.stringify(myObj);
$("#refreshAction").hide();
socket.send(myJSON);
showFileIndexingState();
};
function genSettings(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"general": {
iVol: document.getElementById('initialVolume').value,
mVolSpeaker: document.getElementById('maxVolumeSpeaker').value,
mVolHeadphone: document.getElementById('maxVolumeHeadphone').value,
iBright: document.getElementById('initBrightness').value,
nBright: document.getElementById('nightBrightness').value,
iTime: document.getElementById('inactivityTime').value,
vWarning: document.getElementById('warningLowVoltage').value,
vIndLow: document.getElementById('voltageIndicatorLow').value,
vIndHi: document.getElementById('voltageIndicatorHigh').value,
vInt: document.getElementById('voltageCheckInterval').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function ftpSettings(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"ftp": {
ftpUser: document.getElementById('ftpUser').value,
ftpPwd: document.getElementById('ftpPwd').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function mqttSettings(clickedId) {
lastIdclicked = clickedId;
var val;
if (document.getElementById('mqttEnable').checked) {
val = document.getElementById('mqttEnable').value;
} else {
val = 0;
}
var myObj = {
"mqtt": {
mqttEnable: val,
mqttServer: document.getElementById('mqttServer').value,
mqttUser: document.getElementById('mqttUser').value,
mqttPwd: document.getElementById('mqttPwd').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function rfidMods(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"rfidMod": {
rfidIdMod: document.getElementById('rfidIdMod').value,
modId: document.getElementById('modId').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function removeTrSlash(str) {
if (str.substr(-1) === '/') {
return str.substr(0, str.length - 1);
}
return str;
}
function rfidAssign(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"rfidAssign": {
rfidIdMusic: document.getElementById('rfidIdMusic').value,
fileOrUrl: removeTrSlash(document.getElementById('fileOrUrl').value),
playMode: document.getElementById('playMode').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function wifiConfig(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"wifiConfig": {
ssid: document.getElementById('ssid').value,
pwd: document.getElementById('pwd').value,
hostname: document.getElementById('hostname').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
$(document).ready(function () {
connect();
renderFileTree();
console.log(parseInt(document.getElementById('warningLowVoltage').value));
$(function () {
$('[data-toggle="tooltip"]').tooltip();
});
});
</script>
</body>
</html>

727
html/management_EN.html

@ -0,0 +1,727 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title>ESPuino-configuration</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://ts-cs.de/tonuino/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/11.0.2/css/bootstrap-slider.min.css" />
<script src="https://ts-cs.de/tonuino/js/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<script src="https://ts-cs.de/tonuino/js/popper.min.js"></script>
<script src="https://ts-cs.de/tonuino/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/11.0.2/bootstrap-slider.min.js"></script>
<style type="text/css">
.filetree {
border: 1px solid black;
height: 200px;
margin: 0em 0em 1em 0em;
overflow-y: scroll;
}
.slider.slider-horizontal {
width: 60%;
margin-left: 1em;
margin-right: 1em;
}
.slider-handle{
height: 30px;
width: 30px;
top: -5px;
}
legend.scheduler-border {
width:inherit; /* Or auto */
padding:0 10px; /* To give a bit of padding on the left and right */
border-bottom:none;
}
.icon-pos{
top: 0.3em;
position: relative;
}
.reboot{
color:white;
}
.reboot:hover{
color: orange;
}
.fa-sync:hover {
color: #666666;
}
.clickForRefresh {
text-align: center;
color: gray;
cursor: pointer;
margin-top: 5em;
}
.clickForRefresh:hover {
color: darkgray;
}
.filetree-container {
position: relative;
}
.indexing-progress {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
opacity: 0.7;
display: none;
}
.refreshAction{
text-align: right;
font-size: 0.8em;
}
.refreshAction:hover{
cursor: pointer;
color: darkgray;
}
.overlay {
z-index: 9;
opacity: 0.8;
background: #1a1919;
height: 200px;
display: none;
width: 100%;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
<div class="col-md-12">
<a class="float-left navbar-brand">
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/html/tonuino_logo.png"
width="30" height="30" class="d-inline-block align-top" alt=""/>
Tonuino
</a>
<a class="reboot float-right nav-link" href="/restart"><i class="fas fa-power-off"></i> Neustart</a>
</div>
</nav>
<br/>
<nav>
<div class="container nav nav-tabs" id="nav-tab" role="tablist">
<a class="nav-item nav-link active" id="nav-rfid-tab" data-toggle="tab" href="#nav-rfid" role="tab" aria-controls="nav-rfid" aria-selected="true"><i class="fas fa-dot-circle"></i> RFID</a>
<a class="nav-item nav-link" id="nav-wifi-tab" data-toggle="tab" href="#nav-wifi" role="tab" aria-controls="nav-wifi" aria-selected="false"><i class="fas fa-wifi"></i><span class=".d-sm-none .d-md-block"> WiFi</span></a>
<a class="nav-item nav-link" id="nav-mqtt-tab" data-toggle="tab" href="#nav-mqtt" role="tab" aria-controls="nav-mqtt" aria-selected="false"><i class="fas fa-network-wired"></i> MQTT</a>
<a class="nav-item nav-link" id="nav-ftp-tab" data-toggle="tab" href="#nav-ftp" role="tab" aria-controls="nav-ftp" aria-selected="false"><i class="fas fa-folder"></i> FTP</a>
<a class="nav-item nav-link" id="nav-general-tab" data-toggle="tab" href="#nav-general" role="tab" aria-controls="nav-general" aria-selected="false"><i class="fas fa-sliders-h"></i> General</a>
<a class="nav-item nav-link" id="nav-tools-tab" data-toggle="tab" href="#nav-tools" role="tab" aria-controls="nav-tools" aria-selected="false"><i class="fas fa-wrench"></i> Tools</a>
</div>
</nav>
<br>
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade" id="nav-wifi" role="tabpanel" aria-labelledby="nav-wifi-tab">
<div class="container" id="wifiConfig">
<form action="#wifiConfig" method="POST" onsubmit="wifiConfig('wifiConfig'); return false">
<div class="form-group col-md-12">
<label for="ssid">Wifi (SSID):</label>
<input type="text" class="form-control" id="ssid" placeholder="SSID" name="ssid" required>
<div class="invalid-feedback">
Please enter WiFi's SSID.
</div>
<label for="pwd">Password:</label>
<input type="password" class="form-control" id="pwd" placeholder="Passwort" name="pwd" required>
<label for="hostname">tonuino-name (Hostname):</label>
<input type="text" class="form-control" id="hostname" placeholder="tonuino" name="hostname"
value="%HOSTNAME%" pattern="^[^-\.]{2,32}" required>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
<div class="tab-pane fade show active" id="nav-rfid" role="tabpanel" aria-labelledby="nav-rfid-tab">
<div class="container" id="rfidMusicTags">
<fieldset>
<legend>RFID-assignments</legend>
<form action="#rfidMusicTags" method="POST" onsubmit="rfidAssign('rfidMusicTags'); return false">
<div class="form-group col-md-12">
<label for="rfidIdMusic">RFID-tag-ID (12-digits)</label>
<input type="text" class="form-control" id="rfidIdMusic" maxlength="12" pattern="[0-9]{12}"
placeholder="%RFID_TAG_ID%" name="rfidIdMusic" required>
<label for="fileOrUrl">File, directory or URL (^ and # not allowed as characters)</label>
<input type="text" class="form-control" id="fileOrUrl" maxlength="255" placeholder="z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3" pattern="^[^\^#]+$" name="fileOrUrl" required>
<div class="filetree-container">
<div id="filebrowser">
<div class="filetree demo" id="filetree"></div>
<div class="refreshAction">
<span id="refreshAction" data-toggle="tooltip" data-placement="top" title="Refresh indexfile."><i class="fas fa-sync fa-1x"></i> Refresh indexfile</span>
</div>
</div>
<div id="indexing-progress" class="indexing-progress overlay">
<div style="text-align: center; color:white; margin-top:2em;">
<div><i class="fas fa-sync fa-spin fa-2x"></i> <br><br> This could take while...</div>
<div id="currentProcessedFile"></div>
</div>
</div>
</div>
<label for="playMode">Playmode</label>
<select class="form-control" id="playMode" name="playMode">
<option class="option-file" value="1">Single track</option>
<option class="option-file" value="2">Single track (loop)</option>
<option class="option-file-and-folder" value="3">Audiobook</option>
<option class="option-file-and-folder" value="4">Audiobook (loop)</option>
<option class="option-folder" value="5">All tracks of directory (alph. sorted)</option>
<option class="option-folder" value="6">All tracks of directory (random, loop)</option>
<option class="option-folder" value="7">All tracks of directory (alph. sorted, loop)</option>
<option class="option-folder" value="9">All tracks of directory (random)</option>
<option class="option-stream" value="8">Webstream</option>
</select>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</fieldset>
</div>
<br>
<br>
<div class="container" id="rfidModTags">
<fieldset>
<legend>RFID-modifications</legend>
<form class="needs-validation" action="#rfidModTags" method="POST" onsubmit="rfidMods('rfidModTags'); return false">
<div class="form-group col-md-12">
<label for="rfidIdMod">RFID-tag-ID (12-digits)</label>
<input type="text" class="form-control" id="rfidIdMod" maxlength="12" pattern="[0-9]{12}"
placeholder="%RFID_TAG_ID%" name="rfidIdMod" required>
<div class="invalid-feedback">
Please enter 12-digits-number.
</div>
<label for="modId">Configuration</label>
<select class="form-control" id="modId" name="modId">
<option value="100">Keylock</option>
<option value="101">Auto-sleep after 15 minutes</option>
<option value="102">Auto-sleep after 30 minutes</option>
<option value="103">Auto-sleep after 1 hour</option>
<option value="104">Auto-sleep after 2 hours</option>
<option value="105">Auto-sleep after after end of current track</option>
<option value="106">Auto-sleep after after end of current playlist</option>
<option value="107">Auto-sleep after after five tracks</option>
<option value="110">Loop current playlist</option>
<option value="111">Loop current track</option>
<option value="112">Dimm LEDs (nightmode)</option>
<option value="130">Enable/disable (toggle) WiFi</option>
</select>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</fieldset>
</div>
</div>
<div class="tab-pane fade" id="nav-mqtt" role="tabpanel" aria-labelledby="nav-mqtt-tab">
<div class="container" id="mqttConfig">
<form class="needs-validation" action="#mqttConfig" method="POST"
onsubmit="mqttSettings('mqttConfig'); return false">
<div class="form-check col-md-12">
<input class="form-check-input" type="checkbox" value="1" id="mqttEnable" name="mqttEnable" %MQTT_ENABLE%>
<label class="form-check-label" for="mqttEnable">
Enable MQTT
</label>
</div>
<div class="form-group my-2 col-md-12">
<label for="mqttServer">MQTT-server</label>
<input type="text" class="form-control" id="mqttServer" minlength="7" maxlength="%MQTT_SERVER_LENGTH%"
placeholder="z.B. 192.168.2.89" name="mqttServer" value="%MQTT_SERVER%">
<label for="mqttUser">MQTT-username (optional):</label>
<input type="text" class="form-control" id="mqttUser" maxlength="%MQTT_USER_LENGTH%"
placeholder="Benutzername" name="mqttUser" value="%MQTT_USER%">
<label for="mqttPwd">MQTT-password (optional):</label>
<input type="password" class="form-control" id="mqttPwd" maxlength="%MQTT_PWD_LENGTH%"
placeholder="Passwort" name="mqttPwd" value="%MQTT_PWD%">
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="nav-ftp" role="tabpanel" aria-labelledby="nav-ftp-tab">
<div class="container" id="ftpConfig">
<form action="#ftpConfig" method="POST" onsubmit="ftpSettings('ftpConfig'); return false">
<div class="form-group col-md-12">
<label for="ftpUser">FTP-username:</label>
<input type="text" class="form-control" id="ftpUser" maxlength="%FTP_USER_LENGTH%"
placeholder="Benutzername" name="ftpUser" value="%FTP_USER%" required>
<label for="pwd">FTP-password:</label>
<input type="password" class="form-control" id="ftpPwd" maxlength="%FTP_PWD_LENGTH%" placeholder="Passwort"
name="ftpPwd" value="%FTP_PWD%" required>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="nav-general" role="tabpanel" aria-labelledby="nav-general-tab">
<div class="container" id="generalConfig">
<form action="#generalConfig" method="POST" onsubmit="genSettings('generalConfig'); return false">
<div class="form-group col-md-12">
<fieldset>
<legend class="w-auto">Volume</legend>
<label for="initialVolume">After power on</label>
<div class="text-center">
<i class="fas fa-volume-down fa-2x .icon-pos"></i> <input data-provide="slider" type="number" data-slider-min="1" data-slider-max="21" min="1" max="21" class="form-control" id="initialVolume" name="initialVolume"
data-slider-value="%INIT_VOLUME%" value="%INIT_VOLUME%" required> <i class="fas fa-volume-up fa-2x .icon-pos"></i></div>
<br>
<label for="maxVolumeSpeaker">Maximum (speaker)</label>
<div class="text-center">
<i class="fas fa-volume-down fa-2x .icon-pos"></i> <input data-provide="slider" type="number" data-slider-min="1" data-slider-max="21" min="1" max="21" class="form-control" id="maxVolumeSpeaker" name="maxVolumeSpeaker"
data-slider-value="%MAX_VOLUME_SPEAKER%" value="%MAX_VOLUME_SPEAKER%" required> <i class="fas fa-volume-up fa-2x .icon-pos"></i>
</div>
<br>
<label for="maxVolumeHeadphone">Maximum (headphones)</label>
<div class="text-center">
<i class="fas fa-volume-down fa-2x .icon-pos"></i> <input data-provide="slider" type="number" data-slider-min="1" data-slider-max="21" min="1" max="21" class="form-control" id="maxVolumeHeadphone" name="maxVolumeHeadphone"
data-slider-value="%MAX_VOLUME_HEADPHONE%" value="%MAX_VOLUME_HEADPHONE%" required> <i class="fas fa-volume-up fa-2x .icon-pos"></i>
</div>
</fieldset>
</div>
<br>
<div class="form-group col-md-12">
<fieldset >
<legend class="w-auto">Neopixel (brightness)</legend>
<label for="initBrightness">After power on:</label>
<div class="text-center">
<i class="far fa-sun fa-2x .icon-pos"></i>
<input data-provide="slider" type="number" data-slider-min="0" data-slider-max="255" min="0" max="255" class="form-control" id="initBrightness" name="initBrightness"
data-slider-value="%INIT_LED_BRIGHTNESS%" value="%INIT_LED_BRIGHTNESS%" required><i class="fas fa-sun fa-2x .icon-pos"></i>
</div>
<label for="nightBrightness">In nightmode</label>
<div class="text-center">
<i class="far fa-sun fa-2x .icon-pos"></i><input data-provide="slider" type="number" data-slider-min="0" data-slider-max="255" min="0" max="255" class="form-control" id="nightBrightness" name="nightBrightness" data-slider-value="%NIGHT_LED_BRIGHTNESS%" value="%NIGHT_LED_BRIGHTNESS%" required><i class="fas fa-sun fa-2x .icon-pos"></i>
</div>
</fieldset>
</div>
<br>
<div class="form-group col-md-12">
<fieldset>
<legend>Deep Sleep</legend>
<label for="inactivityTime">Inactivity (minutes)</label>
<div class="text-center"><i class="fas fa-hourglass-start fa-2x .icon-pos"></i> <input type="number" data-provide="slider" data-slider-min="0" data-slider-max="30" min="1" max="120" class="form-control" id="inactivityTime" name="inactivityTime"
data-slider-value="%MAX_INACTIVITY%" value="%MAX_INACTIVITY%" required><i class="fas fa-hourglass-end fa-2x .icon-pos"></i></div>
</fieldset>
</div>
<br>
<div class="form-group col-md-12">
<fieldset>
<legend>Battery</legend>
<div>Status via Neopixel</div>
<br>
<label for="warningLowVoltage">LED-warning will appear below this voltage.
</label>
<div class="text-center">
<i class="fas fa-battery-quarter fa-2x .icon-pos"></i> <input data-provide="slider" data-slider-step="0.1" data-slider-min="3.0" data-slider-max="5.0" min="3.0" max="5.0" type="text" class="form-control" id="warningLowVoltage" name="warningLowVoltage"
data-slider-value="%WARNING_LOW_VOLTAGE%" value="%WARNING_LOW_VOLTAGE%" pattern="^\d{1,2}(\.\d{1,3})?" required> <i class="fas fa-battery-three-quarters fa-2x .icon-pos" fa-2x .icon-pos></i>
</div>
<br>
<label for="voltageIndicatorLow">One LED lights at this voltage
</label>
<div class="text-center">
<i class="fas fa-battery-quarter fa-2x .icon-pos"></i> <input data-provide="slider" min="2.0" data-slider-step="0.1" data-slider-min="2.0" data-slider-max="5.0" max="5.0"type="text" class="form-control" id="voltageIndicatorLow" name="voltageIndicatorLow"
data-slider-value="%VOLTAGE_INDICATOR_LOW%" value="%VOLTAGE_INDICATOR_LOW%" pattern="^\d{1,2}(\.\d{1,3})?" required> <i class="fas fa-battery-three-quarters fa-2x .icon-pos" fa-2x .icon-pos></i>
</div>
<br>
<label for="voltageIndicatorHigh">All LEDs light at this voltage</label>
<div class="text-center">
<i class="fas fa-battery-quarter fa-2x .icon-pos"></i><input data-provide="slider" data-slider-step="0.1" data-slider-min="2.0" data-slider-max="5.0" min="2.0" max="5.0" type="text" class="form-control" id="voltageIndicatorHigh" name="voltageIndicatorHigh"
data-slider-value="%VOLTAGE_INDICATOR_HIGH%" value="%VOLTAGE_INDICATOR_HIGH%" pattern="^\d{1,2}(\.\d{1,3})?" required> <i class="fas fa-battery-three-quarters fa-2x .icon-pos" fa-2x .icon-pos></i>
</div>
<br>
<label for="voltageCheckInterval">Interval of measurement (minutes)</label>
<div class="text-center"><i class="fas fa-hourglass-start fa-2x .icon-pos"></i>
<input data-provide="slider" data-slider-min="1" data-slider-max="60" type="number" min="1" max="60" class="form-control" id="voltageCheckInterval"
data-slider-value="%VOLTAGE_CHECK_INTERVAL%" name="voltageCheckInterval" value="%VOLTAGE_CHECK_INTERVAL%" required><i class="fas fa-hourglass-end fa-2x .icon-pos"></i>
</div>
</fieldset>
</div>
<br>
<div class="text-center">
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="nav-tools" role="tabpanel" aria-labelledby="nav-tools-tab">
<div class="container my-5" id="importNvs">
<h2>NVS-Importer</h2>
<form action="/upload" enctype="multipart/form-data" method="POST">
<div class="form-group">
<label for="nvsUpload">Select backup-file for import.</label>
<input type="file" class="form-control-file" id="nvsUpload" name="nvsUpload" accept=".txt">
</div>
<br>
<div class="text-center">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
<script type="text/javascript">
var DEBUG = false;
var lastIdclicked = '';
var host = $(location).attr('hostname');
if (DEBUG) {
host = "192.168.178.114";
}
toastr.options = {
"closeButton": false,
"debug": false,
"newestOnTop": false,
"progressBar": false,
"positionClass": "toast-top-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
function postRendering(event, data) {
Object.keys(data.instance._model.data).forEach(function (key, index) {
var cur = data.instance.get_node(data.instance._model.data[key]);
var lastFolder = cur['id'].split('/').filter(function (el) {
return el.trim().length > 0;
}).pop();
if ((/\.(mp3|MP3|ogg|wav|WAV|OGG|wma|WMA|acc|ACC|flac|FLAC)$/i).test(lastFolder)) {
data.instance.set_type(data.instance._model.data[key], 'audio');
} else {
if (data.instance._model.data[key]['type'] == "file") {
data.instance.disable_node(data.instance._model.data[key]);
}
}
data.instance.rename_node(data.instance._model.data[key], lastFolder);
});
}
function renderFileTree() {
var filesURI = "/files";
if (DEBUG) {
filesURI = "http://" + host + "/files";
}
$('#filetree').jstree({
'core': {
'check_callback': true,
'data': {
url: filesURI,
error: function (XMLHttpRequest, textStatus, errorThrown) {
$('#j1_loading').hide();
$("#refreshAction").hide();
$('#filetree').html("<div class='clickForRefresh' ><i class='fas fa-sync fa-1x'><span id='#clickForRefresh' > Refresh fileindex.</span></div>");
$('#filetree').on("click", function () {
refreshFileList();
});
toastr.error("Unable to fetch directory-list.");
}
},
},
'types': {
'folder': {
'icon': "fa fa-folder"
},
'file': {
'icon': "fa fa-file"
},
'audio': {
'icon': "fa fa-file-audio"
},
'default': {
'icon': "fa fa-folder"
}
},
'plugins': ["themes", "types"]
}).bind('loaded.jstree', function (event, data) {
postRendering(event, data);
if ((data.instance._model.data['#']['children'].length == 0)) {
toastr.info("SD-card-index needs to be refreshed.");
}
}).bind('refresh.jstree', function (event, data) {
postRendering(event, data);
});
}
$('#filetree').on('select_node.jstree', function (e, data) {
$('input[name=fileOrUrl]').val(data.node.id);
if (data.node.type == "folder") {
$('.option-folder').show();
$('.option-file').hide();
$('#playMode option').removeAttr('selected').filter('[value=3]').attr('selected', true);
}
if (data.node.type == "audio") {
$('.option-file').show();
$('.option-folder').hide();
$('#playMode option').removeAttr('selected').filter('[value=1]').attr('selected', true);
}
});
$('#refreshAction').on("click", function () {
refreshFileList();
$("#indexing-progress").show();
$("#refreshAction").hide();
});
$('#playMode').on("change", function () {
if (this.value == 8) {
$('#filebrowser').slideUp();
} else {
$('#filebrowser').slideDown();
}
});
function showFileIndexingState() {
$("#indexing-progress").show();
$("#refreshAction").hide();
}
function hideFileIndexingState() {
$("#indexing-progress").hide();
$("#refreshAction").show();
}
var socket = undefined;
var tm;
function connect() {
socket = new WebSocket("ws://" + host + "/ws");
socket.onopen = function () {
setInterval(ping, 15000);
};
socket.onclose = function (e) {
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
socket = null;
setTimeout(function () {
connect();
}, 5000);
};
socket.onerror = function (err) {
console.error('Socket encountered error: ', err.message, 'Closing socket');
};
socket.onmessage = function(event) {
console.log(event.data);
var socketMsg = JSON.parse(event.data);
if (socketMsg.rfidId != null) {
document.getElementById('rfidIdMod').value = socketMsg.rfidId;
document.getElementById('rfidIdMusic').value = socketMsg.rfidId;
toastr.info("RFID Tag mit "+ socketMsg.rfidId + " erkannt." );
$("#rfidIdMusic").effect("highlight", {color:"#abf5af"}, 3000);
$("#rfidIdMod").effect("highlight", {color:"#abf5af"}, 3000);
} if ("status" in socketMsg) {
if (socketMsg.status == "ok") {
toastr.success("Action completed successfully." );
}
} if ("pong" in socketMsg) {
if (socketMsg.pong == 'pong') {
pong();
}
} if ("refreshFileList" in socketMsg) {
hideFileIndexingState();
toastr.info("SD-card-index has been refreshed!");
$('#filetree').jstree(true).refresh();
}
if ("indexingState" in socketMsg) {
if(socketMsg.indexingState != null) {
$("#currentProcessedFile").text(socketMsg.indexingState);
console.log(socketMsg.indexingState);
}
}
};
}
function ping() {
var myObj = {
"ping": {
ping: 'ping'
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
tm = setTimeout(function () {
toastr.warning('Connection to Tonuino is broken. Please refresh.');
}, 5000);
}
function pong() {
clearTimeout(tm);
}
function refreshFileList(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"refreshFileList": true
};
var myJSON = JSON.stringify(myObj);
$("#refreshAction").hide();
socket.send(myJSON);
showFileIndexingState();
};
function genSettings(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"general": {
iVol: document.getElementById('initialVolume').value,
mVolSpeaker: document.getElementById('maxVolumeSpeaker').value,
mVolHeadphone: document.getElementById('maxVolumeHeadphone').value,
iBright: document.getElementById('initBrightness').value,
nBright: document.getElementById('nightBrightness').value,
iTime: document.getElementById('inactivityTime').value,
vWarning: document.getElementById('warningLowVoltage').value,
vIndLow: document.getElementById('voltageIndicatorLow').value,
vIndHi: document.getElementById('voltageIndicatorHigh').value,
vInt: document.getElementById('voltageCheckInterval').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function ftpSettings(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"ftp": {
ftpUser: document.getElementById('ftpUser').value,
ftpPwd: document.getElementById('ftpPwd').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function mqttSettings(clickedId) {
lastIdclicked = clickedId;
var val;
if (document.getElementById('mqttEnable').checked) {
val = document.getElementById('mqttEnable').value;
} else {
val = 0;
}
var myObj = {
"mqtt": {
mqttEnable: val,
mqttServer: document.getElementById('mqttServer').value,
mqttUser: document.getElementById('mqttUser').value,
mqttPwd: document.getElementById('mqttPwd').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function rfidMods(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"rfidMod": {
rfidIdMod: document.getElementById('rfidIdMod').value,
modId: document.getElementById('modId').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function removeTrSlash(str) {
if (str.substr(-1) === '/') {
return str.substr(0, str.length - 1);
}
return str;
}
function rfidAssign(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"rfidAssign": {
rfidIdMusic: document.getElementById('rfidIdMusic').value,
fileOrUrl: removeTrSlash(document.getElementById('fileOrUrl').value),
playMode: document.getElementById('playMode').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function wifiConfig(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"wifiConfig": {
ssid: document.getElementById('ssid').value,
pwd: document.getElementById('pwd').value,
hostname: document.getElementById('hostname').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
$(document).ready(function () {
connect();
renderFileTree();
console.log(parseInt(document.getElementById('warningLowVoltage').value));
$(function () {
$('[data-toggle="tooltip"]').tooltip();
});
});
</script>
</body>
</html>

359
html/website.html

@ -1,359 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title>ESPuino-Konfiguration</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://ts-cs.de/tonuino/css/bootstrap.min.css">
<script src="https://ts-cs.de/tonuino/js/jquery.min.js"></script>
<script src="https://ts-cs.de/tonuino/js/popper.min.js"></script>
<script src="https://ts-cs.de/tonuino/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand">
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/html/tonuino_logo.png" width="30" height="30" class="d-inline-block align-top" alt="" />
Tonuino
</a>
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="#wifiConfig">WLAN</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#rfidMusicTags">RFID-Zuweisungen</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#rfidModTags">RFID-Modifikationen</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#mqttConfig">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#ftpConfig">FTP</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#generalConfig">Allgemein</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#importNvs">NVS-Importer</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/restart" style="color: orange">Neustart Tonuino</a>
</li>
</ul>
</div>
</nav>
<br />
<div class="container" id="wifiConfig">
<h2>WLAN-Konfiguration</h2>
<form action="#wifiConfig" method="POST" onsubmit="wifiConfig('wifiConfig'); return false">
<div class="form-group col-md-6">
<label for="ssid">WLAN-Name (SSID):</label>
<input type="text" class="form-control" id="ssid" placeholder="SSID" name="ssid" required>
<div class="invalid-feedback">
Bitte SSID des WLANs eintragen.
</div>
<label for="pwd">Passwort:</label>
<input type="password" class="form-control" id="pwd" placeholder="Passwort" name="pwd" required>
<label for="hostname">Tonuino-Name (Hostname):</label>
<input type="text" class="form-control" id="hostname" placeholder="tonuino" name="hostname" value="%HOSTNAME%" pattern="^[^-\.]{2,32}" required>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="rfidMusicTags">
<h2>RFID-Zuweisungen</h2>
<form action="#rfidMusicTags" method="POST" onsubmit="rfidAssign('rfidMusicTags'); return false">
<div class="form-group col-md-6">
<label for="rfidIdMusic">RFID-Chip-Nummer (12-stellig)</label>
<input type="text" class="form-control" id="rfidIdMusic" maxlength="12" pattern="[0-9]{12}" placeholder="%RFID_TAG_ID%" name="rfidIdMusic" required>
<label for="fileOrUrl">Datei, Verzeichnis oder URL (^ und # als Zeichen nicht erlaubt)</label>
<input type="text" class="form-control" id="fileOrUrl" maxlength="255" placeholder="z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3" pattern="^[^\^#]+$" name="fileOrUrl" required>
<label for="playMode">Abspielmodus</label>
<select class="form-control" id="playMode" name="playMode">
<option value="1">Einzelner Titel</option>
<option value="2">Einzelner Titel (Endlosschleife)</option>
<option value="3">Hörbuch</option>
<option value="4">Hörbuch (Endlosschleife)</option>
<option value="5">Alle Titel eines Verzeichnis (sortiert)</option>
<option value="6">Alle Titel eines Verzeichnis (zufällig)</option>
<option value="7">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option>
<option value="9">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>
<option value="8">Webradio</option>
</select>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="rfidModTags">
<h2>RFID-Modifkationen</h2>
<form class="needs-validation" action="#rfidModTags" method="POST" onsubmit="rfidMods('rfidModTags'); return false">
<div class="form-group col-md-6">
<label for="rfidIdMod">RFID-Chip-Nummer (12-stellig)</label>
<input type="text" class="form-control" id="rfidIdMod" maxlength="12" pattern="[0-9]{12}" placeholder="%RFID_TAG_ID%" name="rfidIdMod" required>
<div class="invalid-feedback">
Bitte eine 12-stellige Zahl eingeben.
</div>
<label for="modId">Abspielmodus</label>
<select class="form-control" id="modId" name="modId">
<option value="100">Tastensperre</option>
<option value="101">Schlafen nach 15 Minuten</option>
<option value="102">Schlafen nach 30 Minuten</option>
<option value="103">Schlafen nach 1 Stunde</option>
<option value="104">Schlafen nach 2 Stunden</option>
<option value="105">Schlafen nach Ende des Titels</option>
<option value="106">Schlafen nach Ende der Playlist</option>
<option value="107">Schlafen nach fünf Titeln</option>
<option value="110">Wiederhole Playlist (endlos)</option>
<option value="111">Wiederhole Titel (endlos)</option>
<option value="112">Dimme LEDs (Nachtmodus)</option>
<option value="130">Aktiviere/deaktive WLAN</option>
</select>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="mqttConfig">
<h2>MQTT-Konfiguration</h2>
<form class="needs-validation" action="#mqttConfig" method="POST" onsubmit="mqttSettings('mqttConfig'); return false">
<div class="form-check col-md-6">
<input class="form-check-input" type="checkbox" value="1" id="mqttEnable" name="mqttEnable" %MQTT_ENABLE%>
<label class="form-check-label" for="mqttEnable">
MQTT aktivieren
</label>
</div>
<div class="form-group my-2 col-md-6">
<label for="mqttServer">MQTT-Server</label>
<input type="text" class="form-control" id="mqttServer" minlength="7" maxlength="%MQTT_SERVER_LENGTH%" placeholder="z.B. 192.168.2.89" name="mqttServer" value="%MQTT_SERVER%">
<label for="mqttUser">MQTT-Benutzername (optional):</label>
<input type="text" class="form-control" id="mqttUser" maxlength="%MQTT_USER_LENGTH%" placeholder="Benutzername" name="mqttUser" value="%MQTT_USER%">
<label for="mqttPwd">Passwort (optional):</label>
<input type="password" class="form-control" id="mqttPwd" maxlength="%MQTT_PWD_LENGTH%" placeholder="Passwort" name="mqttPwd" value="%MQTT_PWD%">
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container" id="ftpConfig">
<h2>FTP-Konfiguration</h2>
<form action="#ftpConfig" method="POST" onsubmit="ftpSettings('ftpConfig'); return false">
<div class="form-group col-md-6">
<label for="ftpUser">FTP-Benutzername:</label>
<input type="text" class="form-control" id="ftpUser" maxlength="%FTP_USER_LENGTH%" placeholder="Benutzername" name="ftpUser" value="%FTP_USER%" required>
<label for="pwd">Passwort:</label>
<input type="password" class="form-control" id="ftpPwd" maxlength="%FTP_PWD_LENGTH%" placeholder="Passwort" name="ftpPwd" value="%FTP_PWD%" required>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="generalConfig">
<h2>Allgemeine Konfiguration</h2>
<form action="#generalConfig" method="POST" onsubmit="genSettings('generalConfig'); return false">
<div class="form-group col-md-6">
<label for="initialVolume">Lautstärke nach dem Einschalten</label>
<input type="number" min="1" max="21" class="form-control" id="initialVolume" name="initialVolume" value="%INIT_VOLUME%" required>
<label for="maxVolumeSpeaker">Maximale Lautstärke (Lautsprecher)</label>
<input type="number" min="1" max="21" class="form-control" id="maxVolumeSpeaker" name="maxVolumeSpeaker" value="%MAX_VOLUME_SPEAKER%" required>
<label for="maxVolumeHeadphone">Maximale Lautstärke (Kopfhörer)</label>
<input type="number" min="1" max="21" class="form-control" id="maxVolumeHeadphone" name="maxVolumeHeadphone" value="%MAX_VOLUME_HEADPHONE%" required>
</div>
<div class="form-group col-md-6">
<label for="initBrightness">Neopixel-Helligkeit nach dem Einschalten</label>
<input type="number" min="0" max="255" class="form-control" id="initBrightness" name="initBrightness" value="%INIT_LED_BRIGHTNESS%" required>
<label for="nightBrightness">Neopixel-Helligkeit im Nachtmodus</label>
<input type="number" min="0" max="255" class="form-control" id="nightBrightness" name="nightBrightness" value="%NIGHT_LED_BRIGHTNESS%" required>
</div>
<div class="form-group col-md-6">
<label for="inactivityTime">Deep-Sleep nach Inaktivität (Minuten)</label>
<input type="number" min="1" max="1440" class="form-control" id="inactivityTime" name="inactivityTime" value="%MAX_INACTIVITY%" required>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="importNvs">
<h2>NVS-Importer</h2>
<form action="/upload" enctype="multipart/form-data" method="POST">
<div class="form-group">
<label for="nvsUpload">Hier kann eine Backup-Datei importiert werden.</label>
<input type="file" class="form-control-file" id="nvsUpload" name="nvsUpload" accept=".txt">
</div>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<script>
var lastIdclicked = '';
var errorBox = '<div class="alert alert-danger alert-dismissible fade show" role="alert">Es ist ein Fehler aufgetreten!<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>';
var okBox = '<div class="alert alert-success alert-dismissible fade show" role="alert">Aktion erfolgreich ausgeführt.<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>';
var socket = new WebSocket("ws://%IPv4%/ws");
function connect() {
socket = new WebSocket("ws://%IPv4%/ws");
}
function ping() {
var myObj = {
"ping": {
ping: 'ping'
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
tm = setTimeout(function () {
alert("Die Verbindung zum Tonuino ist unterbrochen!\\nBitte Seite neu laden.");
}, 5000);
}
function pong() {
clearTimeout(tm);
}
socket.onopen = function () {
setInterval(ping, 15000);
};
socket.onclose = function(e) {
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
setTimeout(function() {
connect();
}, 5000);
};
socket.onerror = function(err) {
console.error('Socket encountered error: ', err.message, 'Closing socket');
socket.close();
};
socket.onmessage = function(event) {
console.log(event.data);
var socketMsg = JSON.parse(event.data);
if (socketMsg.rfidId != null) {
document.getElementById('rfidIdMod').value = socketMsg.rfidId;
document.getElementById('rfidIdMusic').value = socketMsg.rfidId;
$("#rfidIdMusic").fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500);
$("#rfidIdMod").fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500);
} if (socketMsg.status != null) {
if (socketMsg.status == 'ok') {
$("#" + lastIdclicked).find('.messages').html(okBox);
} else {
$("#" + lastIdclicked).find('.messages').html(errorBox);
}
} if (socketMsg.pong != null) {
if (socketMsg.pong == 'pong') {
pong();
}
}
};
function genSettings(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"general": {
iVol: document.getElementById('initialVolume').value,
mVolSpeaker: document.getElementById('maxVolumeSpeaker').value,
mVolHeadphone: document.getElementById('maxVolumeHeadphone').value,
iBright: document.getElementById('initBrightness').value,
nBright: document.getElementById('nightBrightness').value,
iTime: document.getElementById('inactivityTime').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function ftpSettings(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"ftp": {
ftpUser: document.getElementById('ftpUser').value,
ftpPwd: document.getElementById('ftpPwd').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function mqttSettings(clickedId) {
lastIdclicked = clickedId;
var val;
if (document.getElementById('mqttEnable').checked) {
val = document.getElementById('mqttEnable').value;
} else {
val = 0;
}
var myObj = {
"mqtt": {
mqttEnable: val,
mqttServer: document.getElementById('mqttServer').value,
mqttUser: document.getElementById('mqttUser').value,
mqttPwd: document.getElementById('mqttPwd').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function rfidMods(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"rfidMod": {
rfidIdMod: document.getElementById('rfidIdMod').value,
modId: document.getElementById('modId').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function removeTrSlash(str) {
if(str.substr(-1) === '/') {
return str.substr(0, str.length - 1);
}
return str;
}
function rfidAssign(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"rfidAssign": {
rfidIdMusic: document.getElementById('rfidIdMusic').value,
fileOrUrl: removeTrSlash(document.getElementById('fileOrUrl').value),
playMode: document.getElementById('playMode').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function wifiConfig(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"wifiConfig": {
ssid: document.getElementById('ssid').value,
pwd: document.getElementById('pwd').value,
hostname: document.getElementById('hostname').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
</script>
</body>
</html>

23
html/websiteBasic.html

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>WLAN-Einrichtung</title>
</head>
<body>
<form action="/init" method="POST">
<fieldset>
<legend>Initiale WLAN-Einrichtung</legend>
<label for="ssid">SSID:</label><br>
<input type="text" id="ssid" name="ssid" placeholder="SSID" required><br>
<label for="pwd">Passwort:</label><br>
<input type="password" id="pwd" name="pwd" autocomplete="off" required><br>
<label for="hostname">Tonuino-Name (Hostname):</label><br>
<input type="text" id="hostname" name="hostname" required><br><br>
<input type="submit" value="Absenden">
</fieldset>
</form>
<form action="/restart">
<button type="submit">Neustart</button>
</form>
</body>
</html>

23
html/websiteBasic_EN.html

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Wifi-configuration</title>
</head>
<body>
<form action="/init" method="POST">
<fieldset>
<legend>Basic wifi-setup</legend>
<label for="ssid">SSID:</label><br>
<input type="text" id="ssid" name="ssid" placeholder="SSID" required><br>
<label for="pwd">Password:</label><br>
<input type="password" id="pwd" name="pwd" autocomplete="off" required><br>
<label for="hostname">Tonuino-name (hostname):</label><br>
<input type="text" id="hostname" name="hostname" required><br><br>
<input type="submit" value="Submit">
</fieldset>
</form>
<form action="/restart">
<button type="submit">Reboot</button>
</form>
</body>
</html>

263
html/websiteMgmt.h

@ -1,263 +0,0 @@
static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<html lang=\"de\">\
<head>\
<title>ESPuino-Konfiguration</title>\
<meta charset=\"utf-8\">\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\
<link rel=\"stylesheet\" href=\"https://ts-cs.de/css/bootstrap.min.css\">\
<script src=\"https://ts-cs.de/js/jquery.min.js\"></script>\
<script src=\"https://ts-cs.de/js/popper.min.js\"></script>\
<script src=\"https://ts-cs.de/js/bootstrap.min.js\"></script>\
</head>\
<body>\
<nav class=\"navbar navbar-expand-sm bg-primary navbar-dark\">\
<button class=\"navbar-toggler\" type=\"button\" data-toggle=\"collapse\" data-target=\"#navbarSupportedContent\" aria-controls=\"navbarSupportedContent\" aria-expanded=\"false\" aria-label=\"Toggle navigation\">\
<span class=\"navbar-toggler-icon\"></span>\
</button>\
<a class=\"navbar-brand\">\
<img src=\"./tonuino_logo.png\" width=\"30\" height=\"30\" class=\"d-inline-block align-top\" alt=\"\" />\
Tonuino\
</a>\
<div class=\"collapse navbar-collapse\" id=\"collapsibleNavbar\">\
<ul class=\"navbar-nav mr-auto\">\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#wifiConfig\">WLAN</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#rfidMusicTags\">RFID-Zuweisungen</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#rfidModTags\">RFID-Modifikationen</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#mqttConfig\">MQTT</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#ftpConfig\">FTP</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#generalConfig\">Allgemein</a>\
</li>\
</ul>\
</div>\
</nav>\
<br />\
<div class=\"container\" id=\"wifiConfig\">\
<h2>WLAN-Konfiguration</h2>\
<form action=\"#wifiConfig\" method=\"GET\">\
<div class=\"form-group col-md-6\">\
<label for=\"SSID\">WLAN-Name (SSID):</label>\
<input type=\"text\" class=\"form-control\" id=\"SSID\" placeholder=\"SSID\" name=\"SSID\" required>\
<div class=\"invalid-feedback\">\
Bitte SSID des WLANs eintragen.\
</div>\
<label for=\"pwd\">Passwort:</label>\
<input type=\"password\" class=\"form-control\" id=\"pwd\" placeholder=\"Passwort\" name=\"pwd\" required>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\" onclick=\"wifiConfig()\">Absenden</button>\
</form>\
</div>\
<div class=\"container my-5\" id=\"rfidMusicTags\">\
<h2>RFID-Zuweisungen</h2>\
<form action=\"#rfidMusicTags\" method=\"GET\">\
<div class=\"form-group col-md-6\">\
<label for=\"rfidIdMusic\">RFID-Chip-Nummer</label>\
<input type=\"text\" class=\"form-control\" id=\"rfidIdMusic\" maxlength=\"12\" pattern=\"[0-9]{12}\" placeholder=\"012345678912\" name=\"rfidIdMusic\" required>\
<label for=\"fileOrUrl\">Datei, Verzeichnis oder URL (URL nur für Webradio)</label>\
<input type=\"text\" class=\"form-control\" id=\"fileOrUrl\" maxlength=\"255\" placeholder=\"z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3\" name=\"fileOrUrl\" required>\
<label for=\"playMode\">Abspielmodus</label>\
<select class=\"form-control\" id=\"playMode\" name=\"playMode\">\
<option value=\"1\">Einzelner Titel</option>\
<option value=\"2\">Einzelner Titel (Endlosschleife)</option>\
<option value=\"3\">Hörbuch</option>\
<option value=\"4\">Hörbuch (Endlosschleife)</option>\
<option value=\"5\">Alle Titel eines Verzeichnis (sortiert)</option>\
<option value=\"6\">Alle Titel eines Verzeichnis (zufällig)</option>\
<option value=\"7\">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option>\
<option value=\"8\">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>\
<option value=\"9\">Webradio</option>\
</select>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\" onclick=\"rfidAssign()\">Absenden</button>\
</form>\
</div>\
<div class=\"container my-5\" id=\"rfidModTags\">\
<h2>RFID-Modifkationen</h2>\
<form class=\"needs-validation\" action=\"#rfidModTags\" method=\"GET\" novalidate>\
<div class=\"form-group col-md-6\">\
<label for=\"rfidIdMod\">RFID-Chip-Nummer</label>\
<input type=\"text\" class=\"form-control\" id=\"rfidIdMod\" maxlength=\"12\" pattern=\"[0-9]{12}\" placeholder=\"012345678912\" name=\"rfidIdMod\" required>\
<div class=\"invalid-feedback\">\
Bitte eine 12-stellige Zahl eingeben.\
</div>\
<label for=\"modId\">Abspielmodus</label>\
<select class=\"form-control\" id=\"modId\" name=\"modId\">\
<option value=\"100\">Tastensperre</option>\
<option value=\"101\">Schlafen nach 15 Minuten</option>\
<option value=\"102\">Schlafen nach 30 Minuten</option>\
<option value=\"103\">Schlafen nach 1 Stunde</option>\
<option value=\"104\">Schlafen nach 2 Stunden</option>\
<option value=\"105\">Schlafen nach Ende des Titels</option>\
<option value=\"106\">Schlafen nach Ende der Playlist</option>\
<option value=\"110\">Wiederhole Playlist (endlos)</option>\
<option value=\"111\">Wiederhole Titel (endlos)</option>\
<option value=\"112\">Dimme LEDs (Nachtmodus)</option>\
</select>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\" onclick=\"rfidMods()\">Absenden</button>\
</form>\
</div>\
<div class=\"container my-5\" id=\"mqttConfig\">\
<h2>MQTT-Konfiguration</h2>\
<form class=\"needs-validation\" action=\"#mqttConfig\" method=\"GET\" novalidate>\
<div class=\"form-check col-md-6\">\
<input class=\"form-check-input\" type=\"checkbox\" value=\"1\" id=\"mqttEnable\" name=\"mqttEnable\" %MQTT_ENABLE%>\
<label class=\"form-check-label\" for=\"mqttEnable\">\
MQTT aktivieren\
</label>\
</div>\
<div class=\"form-group my-2 col-md-6\">\
<label for=\"mqttServer\">MQTT-Server (IP-Adresse)</label>\
<input type=\"text\" class=\"form-control\" id=\"mqttServer\" pattern=\"^((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$\" minlength=\"7\" maxlength=\"15\" placeholder=\"z.B. 192.168.2.89\" name=\"mqttServer\" value=\"%MQTT_SERVER%\">\
<div class=\"invalid-feedback\">\
Bitte eine gültige IPv4-Adresse eingeben, z.B. 192.168.2.89.\
</div>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\" onclick=\"mqttSettings()\">Absenden</button>\
</form>\
</div>\
<div class=\"container\" id=\"ftpConfig\">\
<h2>FTP-Konfiguration</h2>\
<form action=\"#ftpConfig\" method=\"GET\">\
<div class=\"form-group col-md-6\">\
<label for=\"ftpUser\">FTP-Benutzername:</label>\
<input type=\"text\" class=\"form-control\" id=\"ftpUser\" maxlength=\"32\" placeholder=\"Benutzername\" name=\"ftpUser\" value=\"%FTP_USER%\" required>\
<label for=\"pwd\">Passwort:</label>\
<input type=\"password\" class=\"form-control\" id=\"ftpPwd\" maxlength=\"32\" placeholder=\"Passwort\" name=\"ftpPwd\" value=\"%FTP_PWD%\" required>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\" onClick=\"ftpSettings()\">Absenden</button>\
</form>\
</div>\
<div class=\"container my-5\" id=\"generalConfig\">\
<h2>Allgemeine Konfiguration</h2>\
<form action=\"#generalConfig\" method=\"GET\">\
<div class=\"form-group col-md-6\">\
<label for=\"initialVolume\">Lautstärke nach dem Einschalten</label>\
<input type=\"number\" min=\"1\" max=\"21\" class=\"form-control\" id=\"initialVolume\" name=\"initialVolume\" value=\"%INIT_VOLUME%\" required>\
<label for=\"maxVolume\">Maximale Lautstärke</label>\
<input type=\"number\" min=\"1\" max=\"21\" class=\"form-control\" id=\"maxVolume\" name=\"maxVolume\" value=\"%MAX_VOLUME%\" required>\
</div>\
<div class=\"form-group col-md-6\">\
<label for=\"initBrightness\">Neopixel-Helligkeit nach dem Einschalten</label>\
<input type=\"number\" min=\"0\" max=\"255\" class=\"form-control\" id=\"initBrightness\" name=\"initBrightness\" value=\"%INIT_LED_BRIGHTBESS%\" required>\
<label for=\"nightBrightness\">Neopixel-Helligkeit im Nachtmodus</label>\
<input type=\"number\" min=\"0\" max=\"255\" class=\"form-control\" id=\"nightBrightness\" name=\"nightBrightness\" value=\"%NIGHT_LED_BRIGHTBESS%\" required>\
</div>\
<div class=\"form-group col-md-6\">\
<label for=\"inactivityTime\">Deep-Sleep nach Inaktivität (Minuten)</label>\
<input type=\"number\" min=\"1\" max=\"1440\" class=\"form-control\" id=\"inactivityTime\" name=\"inactivityTime\" value=\"%MAX_INACTIVITY%\" required>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\" onClick=\"genSettings()\">Absenden</button>\
</form>\
<script>\
(function() {\
'use strict';\
window.addEventListener('load', function() {\
// Fetch all the forms we want to apply custom Bootstrap validation styles to\
var forms = document.getElementsByClassName('needs-validation');\
// Loop over them and prevent submission\
var validation = Array.prototype.filter.call(forms, function(form) {\
form.addEventListener('submit', function(event) {\
if (form.checkValidity() === false) {\
event.preventDefault();\
event.stopPropagation();\
}\
form.classList.add('was-validated');\
}, false);\
});\
}, false);\
});\
\
let socket = new WebSocket(\"ws://%IPv4%:81/ws\");\
\
function genSettings() {\
\
var myObj = {\
\"general\": {\
iVol: document.getElementById('initialVolume').value,\
mVol: document.getElementById('maxVolume').value,\
iBright: document.getElementById('initBrightness').value,\
nBright: document.getElementById('nightBrightness').value,\
iTime: document.getElementById('inactivityTime').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function ftpSettings() {\
var myObj = {\
\"ftp\": {\
ftpUser: document.getElementById('ftpUser').value,\
ftpPwd: document.getElementById('ftpPwd').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function mqttSettings() {\
var myObj = {\
\"mqtt\": {\
enable: document.getElementById('mqttEnable').value,\
server: document.getElementById('mqttServer').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function rfidMods() {\
var myObj = {\
\"rfidMod\": {\
rfidId: document.getElementById('rfidIdMod').value,\
modId: document.getElementById('modId').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function rfidAssign() {\
var myObj = {\
\"rfidAssign\": {\
rfidId: document.getElementById('rfidIdMusic').value,\
fileOrUrl: document.getElementById('fileOrUrl').value,\
playmode: document.getElementById('playMode').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function wifiConfig() {\
var myObj = {\
\"wifiConfig\": {\
ssid: document.getElementById('SSID').value,\
pwd: document.getElementById('pwd').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
</script>\
</div>\
</body>\
</html>\
";

359
html/website_EN.html

@ -1,359 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title>ESPuino-configuration</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://ts-cs.de/tonuino/css/bootstrap.min.css">
<script src="https://ts-cs.de/tonuino/js/jquery.min.js"></script>
<script src="https://ts-cs.de/tonuino/js/popper.min.js"></script>
<script src="https://ts-cs.de/tonuino/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand">
<img src="https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/html/tonuino_logo.png" width="30" height="30" class="d-inline-block align-top" alt="" />
Tonuino
</a>
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="#wifiConfig">Wifi</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#rfidMusicTags">RFID-assignments</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#rfidModTags">RFID-modifications</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#mqttConfig">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#ftpConfig">FTP</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#generalConfig">General</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#importNvs">NVS-importer</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/restart" style="color: orange">Reboot Tonuino</a>
</li>
</ul>
</div>
</nav>
<br />
<div class="container" id="wifiConfig">
<h2>Wifi-configuration</h2>
<form action="#wifiConfig" method="POST" onsubmit="wifiConfig('wifiConfig'); return false">
<div class="form-group col-md-6">
<label for="ssid">Wifi-name (SSID):</label>
<input type="text" class="form-control" id="ssid" placeholder="SSID" name="ssid" required>
<div class="invalid-feedback">
Please enter name of wifi (SSID).
</div>
<label for="pwd">Password:</label>
<input type="password" class="form-control" id="pwd" placeholder="Passwort" name="pwd" required>
<label for="hostname">Tonuino-name (hostname):</label>
<input type="text" class="form-control" id="hostname" placeholder="tonuino" name="hostname" value="%HOSTNAME%" pattern="^[^-\.]{2,32}" required>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="rfidMusicTags">
<h2>RFID-assignments</h2>
<form action="#rfidMusicTags" method="POST" onsubmit="rfidAssign('rfidMusicTags'); return false">
<div class="form-group col-md-6">
<label for="rfidIdMusic">RFID-chip-ID (12 digits)</label>
<input type="text" class="form-control" id="rfidIdMusic" maxlength="12" pattern="[0-9]{12}" placeholder="%RFID_TAG_ID%" name="rfidIdMusic" required>
<label for="fileOrUrl">File, directory or URL (^ und # are not allowed)</label>
<input type="text" class="form-control" id="fileOrUrl" maxlength="255" placeholder="z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3" pattern="^[^\^#]+$" name="fileOrUrl" required>
<label for="playMode">Playmode</label>
<select class="form-control" id="playMode" name="playMode">
<option value="1">Single track</option>
<option value="2">Single track (infinite loop)</option>
<option value="3">Audiobook</option>
<option value="4">Audiobook (infinite loop)</option>
<option value="5">All tracks of directory (sorted)</option>
<option value="6">All tracks of directory (random)</option>
<option value="7">All tracks of directory (sorted, inf. loop)</option>
<option value="9">All tracks of directory (random, inf. loop)</option>
<option value="8">Webradio</option>
</select>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="rfidModTags">
<h2>RFID-modifications</h2>
<form class="needs-validation" action="#rfidModTags" method="POST" onsubmit="rfidMods('rfidModTags'); return false">
<div class="form-group col-md-6">
<label for="rfidIdMod">RFID-chip-ID (12 digits)</label>
<input type="text" class="form-control" id="rfidIdMod" maxlength="12" pattern="[0-9]{12}" placeholder="%RFID_TAG_ID%" name="rfidIdMod" required>
<div class="invalid-feedback">
Please enter a number with 12 digits.
</div>
<label for="modId">Abspielmodus</label>
<select class="form-control" id="modId" name="modId">
<option value="100">Lock keys</option>
<option value="101">Sleep after 15 minutes</option>
<option value="102">Sleep after 30 minutes</option>
<option value="103">Sleep after 1 hour</option>
<option value="104">Sleep after 1 hours</option>
<option value="105">Sleep after end of track</option>
<option value="106">Sleep after end of playlist</option>
<option value="107">Sleep after five tracks</option>
<option value="110">Repeat playlist (inf. loop)</option>
<option value="111">Repeat track (inf. loop)</option>
<option value="112">Dimm LEDs (nightmode)</option>
<option value="130">Toggle WiFi-status</option>
</select>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="mqttConfig">
<h2>MQTT-configuration</h2>
<form class="needs-validation" action="#mqttConfig" method="POST" onsubmit="mqttSettings('mqttConfig'); return false">
<div class="form-check col-md-6">
<input class="form-check-input" type="checkbox" value="1" id="mqttEnable" name="mqttEnable" %MQTT_ENABLE%>
<label class="form-check-label" for="mqttEnable">
Enable MQTT
</label>
</div>
<div class="form-group my-2 col-md-6">
<label for="mqttServer">MQTT-server</label>
<input type="text" class="form-control" id="mqttServer" minlength="7" maxlength="%MQTT_SERVER_LENGTH%" placeholder="z.B. 192.168.2.89" name="mqttServer" value="%MQTT_SERVER%">
<label for="mqttUser">MQTT-username (optional):</label>
<input type="text" class="form-control" id="mqttUser" maxlength="%MQTT_USER_LENGTH%" placeholder="Benutzername" name="mqttUser" value="%MQTT_USER%">
<label for="mqttPwd">Password (optional):</label>
<input type="password" class="form-control" id="mqttPwd" maxlength="%MQTT_PWD_LENGTH%" placeholder="Passwort" name="mqttPwd" value="%MQTT_PWD%">
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container" id="ftpConfig">
<h2>FTP-configuration</h2>
<form action="#ftpConfig" method="POST" onsubmit="ftpSettings('ftpConfig'); return false">
<div class="form-group col-md-6">
<label for="ftpUser">FTP-username:</label>
<input type="text" class="form-control" id="ftpUser" maxlength="%FTP_USER_LENGTH%" placeholder="Benutzername" name="ftpUser" value="%FTP_USER%" required>
<label for="pwd">password:</label>
<input type="password" class="form-control" id="ftpPwd" maxlength="%FTP_PWD_LENGTH%" placeholder="Passwort" name="ftpPwd" value="%FTP_PWD%" required>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="generalConfig">
<h2>General configuration</h2>
<form action="#generalConfig" method="POST" onsubmit="genSettings('generalConfig'); return false">
<div class="form-group col-md-6">
<label for="initialVolume">Volume after start</label>
<input type="number" min="1" max="21" class="form-control" id="initialVolume" name="initialVolume" value="%INIT_VOLUME%" required>
<label for="maxVolumeSpeaker">Maximum volume (speaker)</label>
<input type="number" min="1" max="21" class="form-control" id="maxVolumeSpeaker" name="maxVolumeSpeaker" value="%MAX_VOLUME_SPEAKER%" required>
<label for="maxVolumeHeadphone">Maximum volume (headphone)</label>
<input type="number" min="1" max="21" class="form-control" id="maxVolumeHeadphone" name="maxVolumeHeadphone" value="%MAX_VOLUME_HEADPHONE%" required>
</div>
<div class="form-group col-md-6">
<label for="initBrightness">Neopixel-brightness after start</label>
<input type="number" min="0" max="255" class="form-control" id="initBrightness" name="initBrightness" value="%INIT_LED_BRIGHTNESS%" required>
<label for="nightBrightness">Neopixel-brightness in nightmode</label>
<input type="number" min="0" max="255" class="form-control" id="nightBrightness" name="nightBrightness" value="%NIGHT_LED_BRIGHTNESS%" required>
</div>
<div class="form-group col-md-6">
<label for="inactivityTime">Deepsleep after inactivity (minutes)</label>
<input type="number" min="1" max="1440" class="form-control" id="inactivityTime" name="inactivityTime" value="%MAX_INACTIVITY%" required>
</div>
<button type="reset" class="btn btn-secondary">Reset</button>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<div class="container my-5" id="importNvs">
<h2>NVS-importer</h2>
<form action="/upload" enctype="multipart/form-data" method="POST">
<div class="form-group">
<label for="nvsUpload">Backup-files can be imported here.</label>
<input type="file" class="form-control-file" id="nvsUpload" name="nvsUpload" accept=".txt">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="messages col-md-6 my-2"></div>
</div>
<script>
var lastIdclicked = '';
var errorBox = '<div class="alert alert-danger alert-dismissible fade show" role="alert">Error occured!<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>';
var okBox = '<div class="alert alert-success alert-dismissible fade show" role="alert">Action successful.<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>';
var socket = new WebSocket("ws://%IPv4%/ws");
function connect() {
socket = new WebSocket("ws://%IPv4%/ws");
}
function ping() {
var myObj = {
"ping": {
ping: 'ping'
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
tm = setTimeout(function () {
alert("Connection to tonuino is broken!\\nPlease refresh this website.");
}, 5000);
}
function pong() {
clearTimeout(tm);
}
socket.onopen = function () {
setInterval(ping, 15000);
};
socket.onclose = function(e) {
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
setTimeout(function() {
connect();
}, 5000);
};
socket.onerror = function(err) {
console.error('Socket encountered error: ', err.message, 'Closing socket');
socket.close();
};
socket.onmessage = function(event) {
console.log(event.data);
var socketMsg = JSON.parse(event.data);
if (socketMsg.rfidId != null) {
document.getElementById('rfidIdMod').value = socketMsg.rfidId;
document.getElementById('rfidIdMusic').value = socketMsg.rfidId;
$("#rfidIdMusic").fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500);
$("#rfidIdMod").fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500);
} if (socketMsg.status != null) {
if (socketMsg.status == 'ok') {
$("#" + lastIdclicked).find('.messages').html(okBox);
} else {
$("#" + lastIdclicked).find('.messages').html(errorBox);
}
} if (socketMsg.pong != null) {
if (socketMsg.pong == 'pong') {
pong();
}
}
};
function genSettings(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"general": {
iVol: document.getElementById('initialVolume').value,
mVolSpeaker: document.getElementById('maxVolumeSpeaker').value,
mVolHeadphone: document.getElementById('maxVolumeHeadphone').value,
iBright: document.getElementById('initBrightness').value,
nBright: document.getElementById('nightBrightness').value,
iTime: document.getElementById('inactivityTime').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function ftpSettings(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"ftp": {
ftpUser: document.getElementById('ftpUser').value,
ftpPwd: document.getElementById('ftpPwd').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function mqttSettings(clickedId) {
lastIdclicked = clickedId;
var val;
if (document.getElementById('mqttEnable').checked) {
val = document.getElementById('mqttEnable').value;
} else {
val = 0;
}
var myObj = {
"mqtt": {
mqttEnable: val,
mqttServer: document.getElementById('mqttServer').value,
mqttUser: document.getElementById('mqttUser').value,
mqttPwd: document.getElementById('mqttPwd').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function rfidMods(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"rfidMod": {
rfidIdMod: document.getElementById('rfidIdMod').value,
modId: document.getElementById('modId').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function removeTrSlash(str) {
if(str.substr(-1) === '/') {
return str.substr(0, str.length - 1);
}
return str;
}
function rfidAssign(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"rfidAssign": {
rfidIdMusic: document.getElementById('rfidIdMusic').value,
fileOrUrl: removeTrSlash(document.getElementById('fileOrUrl').value),
playMode: document.getElementById('playMode').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
function wifiConfig(clickedId) {
lastIdclicked = clickedId;
var myObj = {
"wifiConfig": {
ssid: document.getElementById('ssid').value,
pwd: document.getElementById('pwd').value,
hostname: document.getElementById('hostname').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}
</script>
</body>
</html>

BIN
pictures/Mgmt-GUI1.jpg

Before

Width: 1080  |  Height: 1856  |  Size: 336 KiB

After

Width: 1080  |  Height: 1847  |  Size: 310 KiB

BIN
pictures/Mgmt-GUI2.jpg

Before

Width: 1080  |  Height: 1223  |  Size: 188 KiB

After

Width: 1080  |  Height: 1844  |  Size: 222 KiB

BIN
pictures/Mgmt-GUI3.jpg

Before

Width: 1080  |  Height: 1644  |  Size: 240 KiB

After

Width: 1080  |  Height: 1847  |  Size: 252 KiB

BIN
pictures/Mgmt-GUI4.jpg

Before

Width: 1080  |  Height: 1471  |  Size: 245 KiB

After

Width: 1080  |  Height: 1847  |  Size: 197 KiB

BIN
pictures/Mgmt-GUI5.jpg

Before

Width: 1080  |  Height: 602  |  Size: 81 KiB

After

Width: 1080  |  Height: 1847  |  Size: 287 KiB

BIN
pictures/Mgmt-GUI6.jpg

After

Width: 1080  |  Height: 1752  |  Size: 230 KiB

BIN
pictures/Mgmt-GUI7.jpg

After

Width: 1080  |  Height: 1849  |  Size: 217 KiB

BIN
pictures/Mgmt-GUI_connection_broken.jpg

Before

Width: 1080  |  Height: 1853  |  Size: 242 KiB

After

Width: 1080  |  Height: 1851  |  Size: 364 KiB

BIN
pictures/Mgmt_GUI_action_ok.jpg

Before

Width: 1080  |  Height: 1843  |  Size: 268 KiB

After

Width: 1032  |  Height: 1280  |  Size: 137 KiB

55
platformio.ini

@ -21,7 +21,6 @@ lib_deps_external =
ESP Async WebServer
https://github.com/me-no-dev/AsyncTCP
https://github.com/bblanchon/ArduinoJson.git
olikraus/U8g2 @ ^2.28.8
; https://github.com/pschatzmann/ESP32-A2DP.git
[env:esp32-a12]
@ -33,9 +32,9 @@ board_build.partitions = no_ota.csv
build_flags = -DHAL=2
lib_deps =
https://github.com/kkloesener/MFRC522_I2C_Library.git
https://github.com/kkloesener/AC101.git
${common.lib_deps_builtin}
${common.lib_deps_external}
yveaux/AC101@^0.0.1
[env:lolin32]
platform = espressif32
@ -47,8 +46,54 @@ build_flags = -DHAL=1
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
https://github.com/biologist79/rfid.git
https://github.com/tueddy/PN5180-Library.git
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
[env:lolin_d32]
platform = espressif32
board = lolin_d32
framework = arduino
monitor_speed = 115200
board_build.partitions = no_ota.csv
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
[env:lolin_d32_pro]
platform = espressif32
board = lolin_d32_pro
framework = arduino
monitor_speed = 115200
board_build.partitions = no_ota.csv
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
[env:nodemcu-32s]
platform = espressif32
board = nodemcu-32s
framework = arduino
monitor_speed = 115200
board_build.partitions = no_ota.csv
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
board_build.partitions = no_ota.csv
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
; Don't forget to run this script if you changed the html-files provided in any way
;extra_scripts =
; pre:processHtml.py
;;; Change upload/monitor-port of your board regarding your operating-system!
;MAC: /dev/cu.SLAB_USBtoUART
;WINDOWS: COM3
;LINUX: /dev/ttyUSB0 (if it's the first USB-device attached, otherwise increase number)
; Don't forget to run this script if you changed the html-files provided in any way
extra_scripts =
pre:processHtml.py

101
processHtml.py

@ -1,57 +1,74 @@
# -*- coding: utf-8 -*-
#!/usr/bin/python
###
# Use this script for creating PROGMEM header files from html files.
# needs pip install requests
##
# html file base names
import requests
import argparse
content = ''
content2 = ''
contentEN = ''
content2EN = ''
def str2bool(v):
if isinstance(v, bool):
return v
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')
with open('html/website.html', 'r') as r:
data = r.read().replace('\n', '\\\n')
data = data.replace('\"', '\\"')
data = data.replace('\\d', '\\\d')
data = data.replace('\\.', '\\\.')
data = data.replace('\\^', '\\\\^')
content += data
HTML_FILES = ["management","management_EN", "accesspoint", "accesspoint_EN"]
with open('src/websiteMgmt.h', 'w') as w:
w.write("static const char mgtWebsite[] PROGMEM = \"")
w.write(content)
w.write("\";")
class htmlHeaderProcessor(object):
with open('html/website_EN.html', 'r') as ren:
data = ren.read().replace('\n', '\\\n')
"""
Returns a minified HTML string, uses html-minifier.com api.
"""
def minifyHTML(self, filename):
with open('html/' + filename + '.html', 'r') as r:
data = r.read()
return requests.post('https://html-minifier.com/raw', data=dict(input=data)).text.encode('utf8')
def escape_html(self, data):
data = data.replace('\n', '\\\n')
data = data.replace('\"', '\\"')
data = data.replace('\\d', '\\\d')
data = data.replace('\\.', '\\\.')
data = data.replace('\\^', '\\\\^')
contentEN += data
data = data.replace('%;', '%%;')
return data
with open('src/websiteMgmt_EN.h', 'w') as wen:
wen.write("static const char mgtWebsite[] PROGMEM = \"")
wen.write(contentEN)
wen.write("\";")
def html_to_c_header(self, filename):
content = ""
with open('html/' + filename + '.html', 'r') as r:
data = r.read()
content += self.escape_html(data)
return content
with open('html/websiteBasic.html', 'r') as r2:
data = r2.read().replace('\n', '\\\n')
data = data.replace('\"', '\\"')
content2 += data
with open('src/websiteBasic.h', 'w') as w2:
w2.write("static const char basicWebsite[] PROGMEM = \"")
w2.write(content2)
w2.write("\";")
def write_header_file(self, filename, content):
with open('src/HTML' + filename + '.h', 'w') as w:
varname = filename.split('_')[0]
w.write("static const char " + varname + "_HTML[] PROGMEM = \"")
w.write(content)
w.write("\";")
def main(self):
parser = argparse.ArgumentParser(description='Create c code PROGMEM header files from HTML files.')
parser.add_argument("--minify", type=str2bool, nargs='?',
const=True, default=False,
help="Minify HTML Code")
args = parser.parse_args()
with open('html/websiteBasic_EN.html', 'r') as r2en:
data = r2en.read().replace('\n', '\\\n')
data = data.replace('\"', '\\"')
content2EN += data
for file in HTML_FILES:
if args.minify:
with open('src/websiteBasic_EN.h', 'w') as w2en:
w2en.write("static const char basicWebsite[] PROGMEM = \"")
w2en.write(content2EN)
w2en.write("\";")
self.header_file_content = self.minifyHTML(file)
self.header_file_content = self.escape_html(self.header_file_content)
else:
self.header_file_content = self.html_to_c_header(file)
self.write_header_file(file, self.header_file_content)
r.close()
w.close()
r2.close()
w2.close()
if __name__ == '__main__':
htmlHeaderProcessor().main()

65
src/HTMLaccesspoint.h

@ -0,0 +1,65 @@
static const char accesspoint_HTML[] PROGMEM = "<!DOCTYPE html>\
<html>\
<head>\
<title>WLAN-Einrichtung</title>\
<style>\
input {\
width: 90%%;\
height: 44px;\
border-radius: 4px;\
margin: 10px auto;\
font-size: 15px;\
background: #f1f1f1;\
border: 0;\
padding: 0 15px\
}\
input {\
\
}\
body {\
background: #007bff;\
font-family: sans-serif;\
font-size: 14px;\
color: #777\
}\
.box {\
background: #fff;\
max-width: 258px;\
margin: 75px auto;\
padding: 30px;\
border-radius: 5px;\
text-align: center\
}\
.btn {\
background: #3498db;\
color: #fff;\
cursor: pointer;\
width: 90%%;\
height: 44px;\
border-radius: 4px;\
margin: 10px auto;\
font-size: 15px;\
}\
.rebootmsg {\
display: none;\
}\
</style>\
</head>\
<body>\
<form id=\"settings\" action=\"/init\" class=\"box\" method=\"POST\">\
<h1>WiFi Configuration</h1>\
<label for=\"ssid\">SSID:</label><br>\
<input type=\"text\" id=\"ssid\" name=\"ssid\" placeholder=\"SSID\" required><br>\
<label for=\"pwd\">Passwort:</label><br>\
<input type=\"password\" id=\"pwd\" name=\"pwd\" autocomplete=\"off\" required><br>\
<label for=\"hostname\">Tonuino-Name (Hostname):</label><br>\
<input type=\"text\" id=\"hostname\" name=\"hostname\" placeholder=\"Tonuino\" required><br><br>\
<input class=\"btn\" type=\"submit\" id=\"save-button\" value=\"Save\">\
</form>\
<form action=\"/restart\" class=\"box\">\
<h1>Ready to go?</h1>\
<input class=\"btn\" type=\"submit\" id=\"restart-button\" value=\"Reboot\">\
</form>\
\
</body>\
</html>";

2
src/websiteBasic_EN.h → src/HTMLaccesspoint_EN.h

@ -1,4 +1,4 @@
static const char basicWebsite[] PROGMEM = "<!DOCTYPE html>\
static const char accesspoint_HTML[] PROGMEM = "<!DOCTYPE html>\
<html>\
<head>\
<title>Wifi-configuration</title>\

728
src/HTMLmanagement.h

@ -0,0 +1,728 @@
static const char management_HTML[] PROGMEM = "<!DOCTYPE html>\
<html lang=\"de\">\
<head>\
<title>ESPuino-Konfiguration</title>\
<meta charset=\"utf-8\">\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\
<link rel=\"stylesheet\" href=\"https://ts-cs.de/tonuino/css/bootstrap.min.css\">\
<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css\"/>\
<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css\"/>\
<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css\"/>\
<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/11.0.2/css/bootstrap-slider.min.css\" />\
<script src=\"https://ts-cs.de/tonuino/js/jquery.min.js\"></script>\
<script src=\"https://code.jquery.com/ui/1.12.0/jquery-ui.min.js\"></script>\
<script src=\"https://ts-cs.de/tonuino/js/popper.min.js\"></script>\
<script src=\"https://ts-cs.de/tonuino/js/bootstrap.min.js\"></script>\
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js\"></script>\
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js\"></script>\
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/11.0.2/bootstrap-slider.min.js\"></script>\
<style type=\"text/css\">\
.filetree {\
border: 1px solid black;\
height: 200px;\
margin: 0em 0em 1em 0em;\
overflow-y: scroll;\
}\
.slider.slider-horizontal {\
width: 60%%;\
margin-left: 1em;\
margin-right: 1em;\
\
}\
.slider-handle{\
height: 30px;\
width: 30px;\
top: -5px;\
}\
\
legend.scheduler-border {\
width:inherit; /* Or auto */\
padding:0 10px; /* To give a bit of padding on the left and right */\
border-bottom:none;\
}\
\
.icon-pos{\
top: 0.3em;\
position: relative;\
}\
.reboot{\
color:white;\
}\
.reboot:hover{\
color: orange;\
}\
\
.fa-sync:hover {\
color: #666666;\
}\
\
.clickForRefresh {\
text-align: center;\
color: gray;\
cursor: pointer;\
margin-top: 5em;\
}\
\
.clickForRefresh:hover {\
color: darkgray;\
}\
\
.filetree-container {\
position: relative;\
}\
\
.indexing-progress {\
width: 100%%;\
height: 100%%;\
position: absolute;\
top: 0;\
left: 0;\
opacity: 0.7;\
display: none;\
}\
\
.refreshAction{\
text-align: right;\
font-size: 0.8em;\
}\
\
.refreshAction:hover{\
cursor: pointer;\
color: darkgray;\
}\
\
.overlay {\
z-index: 9;\
opacity: 0.8;\
background: #1a1919;\
height: 200px;\
display: none;\
width: 100%%;\
}\
</style>\
</head>\
<body>\
<nav class=\"navbar navbar-expand-sm bg-primary navbar-dark\">\
<div class=\"col-md-12\">\
<a class=\"float-left navbar-brand\">\
<img src=\"https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/html/tonuino_logo.png\"\
width=\"30\" height=\"30\" class=\"d-inline-block align-top\" alt=\"\"/>\
Tonuino\
</a>\
\
<a class=\"reboot float-right nav-link\" href=\"/restart\"><i class=\"fas fa-power-off\"></i> Neustart</a>\
</div>\
</nav>\
<br/>\
<nav>\
<div class=\"container nav nav-tabs\" id=\"nav-tab\" role=\"tablist\">\
<a class=\"nav-item nav-link active\" id=\"nav-rfid-tab\" data-toggle=\"tab\" href=\"#nav-rfid\" role=\"tab\" aria-controls=\"nav-rfid\" aria-selected=\"true\"><i class=\"fas fa-dot-circle\"></i> RFID</a>\
<a class=\"nav-item nav-link\" id=\"nav-wifi-tab\" data-toggle=\"tab\" href=\"#nav-wifi\" role=\"tab\" aria-controls=\"nav-wifi\" aria-selected=\"false\"><i class=\"fas fa-wifi\"></i><span class=\".d-sm-none .d-md-block\"> WLAN</span></a>\
<a class=\"nav-item nav-link\" id=\"nav-mqtt-tab\" data-toggle=\"tab\" href=\"#nav-mqtt\" role=\"tab\" aria-controls=\"nav-mqtt\" aria-selected=\"false\"><i class=\"fas fa-network-wired\"></i> MQTT</a>\
<a class=\"nav-item nav-link\" id=\"nav-ftp-tab\" data-toggle=\"tab\" href=\"#nav-ftp\" role=\"tab\" aria-controls=\"nav-ftp\" aria-selected=\"false\"><i class=\"fas fa-folder\"></i> FTP</a>\
<a class=\"nav-item nav-link\" id=\"nav-general-tab\" data-toggle=\"tab\" href=\"#nav-general\" role=\"tab\" aria-controls=\"nav-general\" aria-selected=\"false\"><i class=\"fas fa-sliders-h\"></i> Allgemein</a>\
<a class=\"nav-item nav-link\" id=\"nav-tools-tab\" data-toggle=\"tab\" href=\"#nav-tools\" role=\"tab\" aria-controls=\"nav-tools\" aria-selected=\"false\"><i class=\"fas fa-wrench\"></i> Tools</a>\
</div>\
</nav>\
<br>\
<div class=\"tab-content\" id=\"nav-tabContent\">\
<div class=\"tab-pane fade\" id=\"nav-wifi\" role=\"tabpanel\" aria-labelledby=\"nav-wifi-tab\">\
<div class=\"container\" id=\"wifiConfig\">\
<form action=\"#wifiConfig\" method=\"POST\" onsubmit=\"wifiConfig('wifiConfig'); return false\">\
<div class=\"form-group col-md-12\">\
<label for=\"ssid\">WLAN-Name (SSID):</label>\
<input type=\"text\" class=\"form-control\" id=\"ssid\" placeholder=\"SSID\" name=\"ssid\" required>\
<div class=\"invalid-feedback\">\
Bitte SSID des WLANs eintragen.\
</div>\
<label for=\"pwd\">Passwort:</label>\
<input type=\"password\" class=\"form-control\" id=\"pwd\" placeholder=\"Passwort\" name=\"pwd\" required>\
<label for=\"hostname\">Tonuino-Name (Hostname):</label>\
<input type=\"text\" class=\"form-control\" id=\"hostname\" placeholder=\"tonuino\" name=\"hostname\"\
value=\"%HOSTNAME%\" pattern=\"^[^-\\.]{2,32}\" required>\
</div>\
<br>\
<div class=\"text-center\">\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</div>\
</form>\
</div>\
</div>\
<div class=\"tab-pane fade show active\" id=\"nav-rfid\" role=\"tabpanel\" aria-labelledby=\"nav-rfid-tab\">\
<div class=\"container\" id=\"rfidMusicTags\">\
<fieldset>\
<legend>RFID-Zuweisungen</legend>\
<form action=\"#rfidMusicTags\" method=\"POST\" onsubmit=\"rfidAssign('rfidMusicTags'); return false\">\
<div class=\"form-group col-md-12\">\
<label for=\"rfidIdMusic\">RFID-Chip-Nummer (12-stellig)</label>\
<input type=\"text\" class=\"form-control\" id=\"rfidIdMusic\" maxlength=\"12\" pattern=\"[0-9]{12}\"\
placeholder=\"%RFID_TAG_ID%\" name=\"rfidIdMusic\" required>\
<label for=\"fileOrUrl\">Datei, Verzeichnis oder URL (^ und # als Zeichen nicht erlaubt)</label>\
<input type=\"text\" class=\"form-control\" id=\"fileOrUrl\" maxlength=\"255\" placeholder=\"z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3\" pattern=\"^[^\\^#]+$\" name=\"fileOrUrl\" required>\
<div class=\"filetree-container\">\
<div id=\"filebrowser\">\
<div class=\"filetree demo\" id=\"filetree\"></div>\
<div class=\"refreshAction\">\
<span id=\"refreshAction\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"Datei Liste aktualisieren.\"><i class=\"fas fa-sync fa-1x\"></i> Dateiliste aktualisieren</span>\
</div>\
</div>\
<div id=\"indexing-progress\" class=\"indexing-progress overlay\">\
<div style=\"text-align: center; color:white; margin-top:2em;\">\
<div><i class=\"fas fa-sync fa-spin fa-2x\"></i> <br><br> Der Prozess kann mehrere Minuten dauern...</div>\
<div id=\"currentProcessedFile\"></div>\
</div>\
</div>\
</div>\
<label for=\"playMode\">Abspielmodus</label>\
<select class=\"form-control\" id=\"playMode\" name=\"playMode\">\
<option class=\"option-file\" value=\"1\">Einzelner Titel</option>\
<option class=\"option-file\" value=\"2\">Einzelner Titel (Endlosschleife)</option>\
<option class=\"option-file-and-folder\" value=\"3\">Hörbuch</option>\
<option class=\"option-file-and-folder\" value=\"4\">Hörbuch (Endlosschleife)</option>\
<option class=\"option-folder\" value=\"5\">Alle Titel eines Verzeichnis (sortiert)</option>\
<option class=\"option-folder\" value=\"6\">Alle Titel eines Verzeichnis (zufällig)</option>\
<option class=\"option-folder\" value=\"7\">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option>\
<option class=\"option-folder\" value=\"9\">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>\
<option class=\"option-stream\" value=\"8\">Webradio</option>\
</select>\
</div>\
<br>\
<div class=\"text-center\">\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</div>\
</form>\
</fieldset>\
</div>\
\
<br>\
<br>\
<div class=\"container\" id=\"rfidModTags\">\
<fieldset>\
<legend>RFID-Modifkationen</legend>\
<form class=\"needs-validation\" action=\"#rfidModTags\" method=\"POST\" onsubmit=\"rfidMods('rfidModTags'); return false\">\
<div class=\"form-group col-md-12\">\
<label for=\"rfidIdMod\">RFID-Chip-Nummer (12-stellig)</label>\
<input type=\"text\" class=\"form-control\" id=\"rfidIdMod\" maxlength=\"12\" pattern=\"[0-9]{12}\"\
placeholder=\"%RFID_TAG_ID%\" name=\"rfidIdMod\" required>\
<div class=\"invalid-feedback\">\
Bitte eine 12-stellige Zahl eingeben.\
</div>\
<label for=\"modId\">Konfiguraiton</label>\
<select class=\"form-control\" id=\"modId\" name=\"modId\">\
<option value=\"100\">Tastensperre</option>\
<option value=\"101\">Schlafen nach 15 Minuten</option>\
<option value=\"102\">Schlafen nach 30 Minuten</option>\
<option value=\"103\">Schlafen nach 1 Stunde</option>\
<option value=\"104\">Schlafen nach 2 Stunden</option>\
<option value=\"105\">Schlafen nach Ende des Titels</option>\
<option value=\"106\">Schlafen nach Ende der Playlist</option>\
<option value=\"107\">Schlafen nach fünf Titeln</option>\
<option value=\"110\">Wiederhole Playlist (endlos)</option>\
<option value=\"111\">Wiederhole Titel (endlos)</option>\
<option value=\"112\">Dimme LEDs (Nachtmodus)</option>\
<option value=\"130\">Aktiviere/deaktive WLAN</option>\
</select>\
</div>\
<br>\
<div class=\"text-center\">\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</div>\
</form>\
</fieldset>\
</div>\
</div>\
<div class=\"tab-pane fade\" id=\"nav-mqtt\" role=\"tabpanel\" aria-labelledby=\"nav-mqtt-tab\">\
<div class=\"container\" id=\"mqttConfig\">\
\
<form class=\"needs-validation\" action=\"#mqttConfig\" method=\"POST\"\
onsubmit=\"mqttSettings('mqttConfig'); return false\">\
<div class=\"form-check col-md-12\">\
<input class=\"form-check-input\" type=\"checkbox\" value=\"1\" id=\"mqttEnable\" name=\"mqttEnable\" %MQTT_ENABLE%>\
<label class=\"form-check-label\" for=\"mqttEnable\">\
MQTT aktivieren\
</label>\
</div>\
<div class=\"form-group my-2 col-md-12\">\
<label for=\"mqttServer\">MQTT-Server</label>\
<input type=\"text\" class=\"form-control\" id=\"mqttServer\" minlength=\"7\" maxlength=\"%MQTT_SERVER_LENGTH%\"\
placeholder=\"z.B. 192.168.2.89\" name=\"mqttServer\" value=\"%MQTT_SERVER%\">\
<label for=\"mqttUser\">MQTT-Benutzername (optional):</label>\
<input type=\"text\" class=\"form-control\" id=\"mqttUser\" maxlength=\"%MQTT_USER_LENGTH%\"\
placeholder=\"Benutzername\" name=\"mqttUser\" value=\"%MQTT_USER%\">\
<label for=\"mqttPwd\">Passwort (optional):</label>\
<input type=\"password\" class=\"form-control\" id=\"mqttPwd\" maxlength=\"%MQTT_PWD_LENGTH%\"\
placeholder=\"Passwort\" name=\"mqttPwd\" value=\"%MQTT_PWD%\">\
</div>\
<br>\
<div class=\"text-center\">\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</div>\
</form>\
</div>\
</div>\
<div class=\"tab-pane fade\" id=\"nav-ftp\" role=\"tabpanel\" aria-labelledby=\"nav-ftp-tab\">\
<div class=\"container\" id=\"ftpConfig\">\
\
<form action=\"#ftpConfig\" method=\"POST\" onsubmit=\"ftpSettings('ftpConfig'); return false\">\
<div class=\"form-group col-md-12\">\
<label for=\"ftpUser\">FTP-Benutzername:</label>\
<input type=\"text\" class=\"form-control\" id=\"ftpUser\" maxlength=\"%FTP_USER_LENGTH%\"\
placeholder=\"Benutzername\" name=\"ftpUser\" value=\"%FTP_USER%\" required>\
<label for=\"pwd\">Passwort:</label>\
<input type=\"password\" class=\"form-control\" id=\"ftpPwd\" maxlength=\"%FTP_PWD_LENGTH%\" placeholder=\"Passwort\"\
name=\"ftpPwd\" value=\"%FTP_PWD%\" required>\
</div>\
<br>\
<div class=\"text-center\">\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</div>\
</form>\
</div>\
</div>\
\
<div class=\"tab-pane fade\" id=\"nav-general\" role=\"tabpanel\" aria-labelledby=\"nav-general-tab\">\
<div class=\"container\" id=\"generalConfig\">\
\
<form action=\"#generalConfig\" method=\"POST\" onsubmit=\"genSettings('generalConfig'); return false\">\
<div class=\"form-group col-md-12\">\
<fieldset>\
<legend class=\"w-auto\">Lautstärke</legend>\
<label for=\"initialVolume\">Nach dem Einschalten</label>\
<div class=\"text-center\">\
<i class=\"fas fa-volume-down fa-2x .icon-pos\"></i> <input data-provide=\"slider\" type=\"number\" data-slider-min=\"1\" data-slider-max=\"21\" min=\"1\" max=\"21\" class=\"form-control\" id=\"initialVolume\" name=\"initialVolume\"\
data-slider-value=\"%INIT_VOLUME%\" value=\"%INIT_VOLUME%\" required> <i class=\"fas fa-volume-up fa-2x .icon-pos\"></i></div>\
<br>\
<label for=\"maxVolumeSpeaker\">Maximal Lautsprecher</label>\
<div class=\"text-center\">\
<i class=\"fas fa-volume-down fa-2x .icon-pos\"></i> <input data-provide=\"slider\" type=\"number\" data-slider-min=\"1\" data-slider-max=\"21\" min=\"1\" max=\"21\" class=\"form-control\" id=\"maxVolumeSpeaker\" name=\"maxVolumeSpeaker\"\
data-slider-value=\"%MAX_VOLUME_SPEAKER%\" value=\"%MAX_VOLUME_SPEAKER%\" required> <i class=\"fas fa-volume-up fa-2x .icon-pos\"></i>\
</div>\
<br>\
<label for=\"maxVolumeHeadphone\">Maximal Kopfhörer</label>\
<div class=\"text-center\">\
<i class=\"fas fa-volume-down fa-2x .icon-pos\"></i> <input data-provide=\"slider\" type=\"number\" data-slider-min=\"1\" data-slider-max=\"21\" min=\"1\" max=\"21\" class=\"form-control\" id=\"maxVolumeHeadphone\" name=\"maxVolumeHeadphone\"\
data-slider-value=\"%MAX_VOLUME_HEADPHONE%\" value=\"%MAX_VOLUME_HEADPHONE%\" required> <i class=\"fas fa-volume-up fa-2x .icon-pos\"></i>\
</div>\
</fieldset>\
</div>\
<br>\
<div class=\"form-group col-md-12\">\
<fieldset >\
<legend class=\"w-auto\">Neopixel (Helligkeit)</legend>\
<label for=\"initBrightness\">Nach dem Einschalten:</label>\
<div class=\"text-center\">\
<i class=\"far fa-sun fa-2x .icon-pos\"></i>\
<input data-provide=\"slider\" type=\"number\" data-slider-min=\"0\" data-slider-max=\"255\" min=\"0\" max=\"255\" class=\"form-control\" id=\"initBrightness\" name=\"initBrightness\"\
data-slider-value=\"%INIT_LED_BRIGHTNESS%\" value=\"%INIT_LED_BRIGHTNESS%\" required><i class=\"fas fa-sun fa-2x .icon-pos\"></i>\
</div>\
\
<label for=\"nightBrightness\">Im Nachtmodus</label>\
<div class=\"text-center\">\
<i class=\"far fa-sun fa-2x .icon-pos\"></i><input data-provide=\"slider\" type=\"number\" data-slider-min=\"0\" data-slider-max=\"255\" min=\"0\" max=\"255\" class=\"form-control\" id=\"nightBrightness\" name=\"nightBrightness\" data-slider-value=\"%NIGHT_LED_BRIGHTNESS%\" value=\"%NIGHT_LED_BRIGHTNESS%\" required><i class=\"fas fa-sun fa-2x .icon-pos\"></i>\
</div>\
</fieldset>\
</div>\
<br>\
<div class=\"form-group col-md-12\">\
<fieldset>\
<legend>Deep Sleep</legend>\
\
<label for=\"inactivityTime\">Inaktivität nach (in Minuten)</label>\
<div class=\"text-center\"><i class=\"fas fa-hourglass-start fa-2x .icon-pos\"></i> <input type=\"number\" data-provide=\"slider\" data-slider-min=\"0\" data-slider-max=\"30\" min=\"1\" max=\"120\" class=\"form-control\" id=\"inactivityTime\" name=\"inactivityTime\"\
data-slider-value=\"%MAX_INACTIVITY%\" value=\"%MAX_INACTIVITY%\" required><i class=\"fas fa-hourglass-end fa-2x .icon-pos\"></i></div>\
</fieldset>\
</div>\
<br>\
\
<div class=\"form-group col-md-12\">\
<fieldset>\
<legend>Batterie</legend>\
<div>Status über Neopixel anzeigen</div>\
<br>\
<label for=\"warningLowVoltage\">Spannungsgrenze für Status Anzeige.\
</label>\
<div class=\"text-center\">\
<i class=\"fas fa-battery-quarter fa-2x .icon-pos\"></i> <input data-provide=\"slider\" data-slider-step=\"0.1\" data-slider-min=\"3.0\" data-slider-max=\"5.0\" min=\"3.0\" max=\"5.0\" type=\"text\" class=\"form-control\" id=\"warningLowVoltage\" name=\"warningLowVoltage\"\
data-slider-value=\"%WARNING_LOW_VOLTAGE%\" value=\"%WARNING_LOW_VOLTAGE%\" pattern=\"^\\d{1,2}(\\.\\d{1,3})?\" required> <i class=\"fas fa-battery-three-quarters fa-2x .icon-pos\" fa-2x .icon-pos></i>\
</div>\
<br>\
<label for=\"voltageIndicatorLow\">Unterer Akkuspannungslevel\
</label>\
<div class=\"text-center\">\
<i class=\"fas fa-battery-quarter fa-2x .icon-pos\"></i> <input data-provide=\"slider\" min=\"2.0\" data-slider-step=\"0.1\" data-slider-min=\"2.0\" data-slider-max=\"5.0\" max=\"5.0\"type=\"text\" class=\"form-control\" id=\"voltageIndicatorLow\" name=\"voltageIndicatorLow\"\
data-slider-value=\"%VOLTAGE_INDICATOR_LOW%\" value=\"%VOLTAGE_INDICATOR_LOW%\" pattern=\"^\\d{1,2}(\\.\\d{1,3})?\" required> <i class=\"fas fa-battery-three-quarters fa-2x .icon-pos\" fa-2x .icon-pos></i>\
</div>\
<br>\
<label for=\"voltageIndicatorHigh\">Oberer Akkuspannungslevel</label>\
\
<div class=\"text-center\">\
<i class=\"fas fa-battery-quarter fa-2x .icon-pos\"></i><input data-provide=\"slider\" data-slider-step=\"0.1\" data-slider-min=\"2.0\" data-slider-max=\"5.0\" min=\"2.0\" max=\"5.0\" type=\"text\" class=\"form-control\" id=\"voltageIndicatorHigh\" name=\"voltageIndicatorHigh\"\
data-slider-value=\"%VOLTAGE_INDICATOR_HIGH%\" value=\"%VOLTAGE_INDICATOR_HIGH%\" pattern=\"^\\d{1,2}(\\.\\d{1,3})?\" required> <i class=\"fas fa-battery-three-quarters fa-2x .icon-pos\" fa-2x .icon-pos></i>\
</div>\
\
<br>\
<label for=\"voltageCheckInterval\"> Interval der Messung (in Minuten)</label>\
<div class=\"text-center\"><i class=\"fas fa-hourglass-start fa-2x .icon-pos\"></i>\
<input data-provide=\"slider\" data-slider-min=\"1\" data-slider-max=\"60\" type=\"number\" min=\"1\" max=\"60\" class=\"form-control\" id=\"voltageCheckInterval\"\
data-slider-value=\"%VOLTAGE_CHECK_INTERVAL%\" name=\"voltageCheckInterval\" value=\"%VOLTAGE_CHECK_INTERVAL%\" required><i class=\"fas fa-hourglass-end fa-2x .icon-pos\"></i>\
</div>\
\
</fieldset>\
</div>\
<br>\
<div class=\"text-center\">\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</div>\
</form>\
</div>\
</div>\
<div class=\"tab-pane fade\" id=\"nav-tools\" role=\"tabpanel\" aria-labelledby=\"nav-tools-tab\">\
<div class=\"container my-5\" id=\"importNvs\">\
<h2>NVS-Importer</h2>\
<form action=\"/upload\" enctype=\"multipart/form-data\" method=\"POST\">\
<div class=\"form-group\">\
<label for=\"nvsUpload\">Hier kann eine Backup-Datei importiert werden.</label>\
<input type=\"file\" class=\"form-control-file\" id=\"nvsUpload\" name=\"nvsUpload\" accept=\".txt\">\
</div>\
<br>\
<div class=\"text-center\">\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</div>\
</form>\
</div>\
</div>\
</div>\
<script type=\"text/javascript\">\
var DEBUG = false;\
var lastIdclicked = '';\
var host = $(location).attr('hostname');\
\
if (DEBUG) {\
host = \"192.168.178.114\";\
}\
\
toastr.options = {\
\"closeButton\": false,\
\"debug\": false,\
\"newestOnTop\": false,\
\"progressBar\": false,\
\"positionClass\": \"toast-top-right\",\
\"preventDuplicates\": false,\
\"onclick\": null,\
\"showDuration\": \"300\",\
\"hideDuration\": \"1000\",\
\"timeOut\": \"5000\",\
\"extendedTimeOut\": \"1000\",\
\"showEasing\": \"swing\",\
\"hideEasing\": \"linear\",\
\"showMethod\": \"fadeIn\",\
\"hideMethod\": \"fadeOut\"\
};\
\
function postRendering(event, data) {\
Object.keys(data.instance._model.data).forEach(function (key, index) {\
\
var cur = data.instance.get_node(data.instance._model.data[key]);\
var lastFolder = cur['id'].split('/').filter(function (el) {\
return el.trim().length > 0;\
}).pop();\
if ((/\\.(mp3|MP3|ogg|wav|WAV|OGG|wma|WMA|acc|ACC|flac|FLAC)$/i).test(lastFolder)) {\
data.instance.set_type(data.instance._model.data[key], 'audio');\
} else {\
if (data.instance._model.data[key]['type'] == \"file\") {\
data.instance.disable_node(data.instance._model.data[key]);\
}\
}\
data.instance.rename_node(data.instance._model.data[key], lastFolder);\
});\
}\
\
function renderFileTree() {\
\
var filesURI = \"/files\";\
if (DEBUG) {\
filesURI = \"http://\" + host + \"/files\";\
}\
$('#filetree').jstree({\
'core': {\
'check_callback': true,\
'data': {\
url: filesURI,\
error: function (XMLHttpRequest, textStatus, errorThrown) {\
$('#j1_loading').hide();\
$(\"#refreshAction\").hide();\
$('#filetree').html(\"<div class='clickForRefresh' ><i class='fas fa-sync fa-1x'><span id='#clickForRefresh' > Dateien suchen.</span></div>\");\
$('#filetree').on(\"click\", function () {\
refreshFileList();\
});\
toastr.error(\"Die Verzeichnis-Liste konnte nicht geladen werden.\");\
}\
},\
\
},\
'types': {\
'folder': {\
'icon': \"fa fa-folder\"\
},\
'file': {\
'icon': \"fa fa-file\"\
},\
'audio': {\
'icon': \"fa fa-file-audio\"\
},\
'default': {\
'icon': \"fa fa-folder\"\
}\
},\
'plugins': [\"themes\", \"types\"]\
}).bind('loaded.jstree', function (event, data) {\
postRendering(event, data);\
if ((data.instance._model.data['#']['children'].length == 0)) {\
toastr.info(\"Der SD-Karten-Index muss erzeugt werden.\");\
}\
}).bind('refresh.jstree', function (event, data) {\
postRendering(event, data);\
});\
}\
\
$('#filetree').on('select_node.jstree', function (e, data) {\
$('input[name=fileOrUrl]').val(data.node.id);\
\
if (data.node.type == \"folder\") {\
$('.option-folder').show();\
$('.option-file').hide();\
$('#playMode option').removeAttr('selected').filter('[value=3]').attr('selected', true);\
}\
\
if (data.node.type == \"audio\") {\
$('.option-file').show();\
$('.option-folder').hide();\
$('#playMode option').removeAttr('selected').filter('[value=1]').attr('selected', true);\
}\
});\
\
$('#refreshAction').on(\"click\", function () {\
refreshFileList();\
$(\"#indexing-progress\").show();\
$(\"#refreshAction\").hide();\
});\
\
$('#playMode').on(\"change\", function () {\
if (this.value == 8) {\
$('#filebrowser').slideUp();\
} else {\
$('#filebrowser').slideDown();\
}\
});\
\
function showFileIndexingState() {\
$(\"#indexing-progress\").show();\
$(\"#refreshAction\").hide();\
}\
\
function hideFileIndexingState() {\
$(\"#indexing-progress\").hide();\
$(\"#refreshAction\").show();\
}\
\
var socket = undefined;\
var tm;\
\
function connect() {\
socket = new WebSocket(\"ws://\" + host + \"/ws\");\
\
socket.onopen = function () {\
setInterval(ping, 15000);\
};\
\
socket.onclose = function (e) {\
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);\
socket = null;\
setTimeout(function () {\
connect();\
}, 5000);\
};\
\
socket.onerror = function (err) {\
console.error('Socket encountered error: ', err.message, 'Closing socket');\
};\
\
socket.onmessage = function(event) {\
console.log(event.data);\
var socketMsg = JSON.parse(event.data);\
if (socketMsg.rfidId != null) {\
document.getElementById('rfidIdMod').value = socketMsg.rfidId;\
document.getElementById('rfidIdMusic').value = socketMsg.rfidId;\
toastr.info(\"RFID Tag mit \"+ socketMsg.rfidId + \" erkannt.\" );\
\
$(\"#rfidIdMusic\").effect(\"highlight\", {color:\"#abf5af\"}, 3000);\
$(\"#rfidIdMod\").effect(\"highlight\", {color:\"#abf5af\"}, 3000);\
\
} if (\"status\" in socketMsg) {\
if (socketMsg.status == \"ok\") {\
toastr.success(\"Aktion erfolgreich ausgeführt.\" );\
}\
} if (\"pong\" in socketMsg) {\
if (socketMsg.pong == 'pong') {\
pong();\
}\
} if (\"refreshFileList\" in socketMsg) {\
hideFileIndexingState();\
toastr.info(\"Die Dateiliste wurde neu erzeugt!\");\
$('#filetree').jstree(true).refresh();\
\
}\
if (\"indexingState\" in socketMsg) {\
if(socketMsg.indexingState != null) {\
$(\"#currentProcessedFile\").text(socketMsg.indexingState);\
console.log(socketMsg.indexingState);\
}\
}\
};\
}\
\
function ping() {\
var myObj = {\
\"ping\": {\
ping: 'ping'\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
tm = setTimeout(function () {\
toastr.warning('Die Verbindung zum Tonuino ist unterbrochen! Bitte Seite neu laden.');\
}, 5000);\
}\
\
function pong() {\
clearTimeout(tm);\
}\
\
function refreshFileList(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"refreshFileList\": true\
};\
var myJSON = JSON.stringify(myObj);\
$(\"#refreshAction\").hide();\
socket.send(myJSON);\
showFileIndexingState();\
};\
\
function genSettings(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"general\": {\
iVol: document.getElementById('initialVolume').value,\
mVolSpeaker: document.getElementById('maxVolumeSpeaker').value,\
mVolHeadphone: document.getElementById('maxVolumeHeadphone').value,\
iBright: document.getElementById('initBrightness').value,\
nBright: document.getElementById('nightBrightness').value,\
iTime: document.getElementById('inactivityTime').value,\
vWarning: document.getElementById('warningLowVoltage').value,\
vIndLow: document.getElementById('voltageIndicatorLow').value,\
vIndHi: document.getElementById('voltageIndicatorHigh').value,\
vInt: document.getElementById('voltageCheckInterval').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function ftpSettings(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"ftp\": {\
ftpUser: document.getElementById('ftpUser').value,\
ftpPwd: document.getElementById('ftpPwd').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function mqttSettings(clickedId) {\
lastIdclicked = clickedId;\
var val;\
if (document.getElementById('mqttEnable').checked) {\
val = document.getElementById('mqttEnable').value;\
} else {\
val = 0;\
}\
var myObj = {\
\"mqtt\": {\
mqttEnable: val,\
mqttServer: document.getElementById('mqttServer').value,\
mqttUser: document.getElementById('mqttUser').value,\
mqttPwd: document.getElementById('mqttPwd').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function rfidMods(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"rfidMod\": {\
rfidIdMod: document.getElementById('rfidIdMod').value,\
modId: document.getElementById('modId').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function removeTrSlash(str) {\
if (str.substr(-1) === '/') {\
return str.substr(0, str.length - 1);\
}\
return str;\
}\
\
function rfidAssign(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"rfidAssign\": {\
rfidIdMusic: document.getElementById('rfidIdMusic').value,\
fileOrUrl: removeTrSlash(document.getElementById('fileOrUrl').value),\
playMode: document.getElementById('playMode').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function wifiConfig(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"wifiConfig\": {\
ssid: document.getElementById('ssid').value,\
pwd: document.getElementById('pwd').value,\
hostname: document.getElementById('hostname').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
$(document).ready(function () {\
connect();\
renderFileTree();\
\
console.log(parseInt(document.getElementById('warningLowVoltage').value));\
$(function () {\
$('[data-toggle=\"tooltip\"]').tooltip();\
});\
\
});\
</script>\
</body>\
</html>\
";

24
src/websiteMgmt_EN.h → src/HTMLmanagement_EN.h

@ -1,4 +1,4 @@
static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
static const char management_HTML[] PROGMEM = "<!DOCTYPE html>\
<html lang=\"de\">\
<head>\
<title>ESPuino-configuration</title>\
@ -180,6 +180,22 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<label for=\"inactivityTime\">Deepsleep after inactivity (minutes)</label>\
<input type=\"number\" min=\"1\" max=\"1440\" class=\"form-control\" id=\"inactivityTime\" name=\"inactivityTime\" value=\"%MAX_INACTIVITY%\" required>\
</div>\
<div class=\"form-group col-md-6\">\
<label for=\"warningLowVoltage\">(Optional) Neopixel-warning will indicated below this battery-voltage (e.g. 3.4)</label>\
<input type=\"text\" class=\"form-control\" id=\"warningLowVoltage\" name=\"warningLowVoltage\" value=\"%WARNING_LOW_VOLTAGE%\" pattern=\"^\\d{1,2}(\\.\\d{1,3})?\" required>\
</div>\
<div class=\"form-group col-md-6\">\
<label for=\"voltageIndicatorLow\">(Optional) Lower voltage (battery) for Neopixel-visualisation (z.B. 3.1)</label>\
<input type=\"text\" class=\"form-control\" id=\"voltageIndicatorLow\" name=\"voltageIndicatorLow\" value=\"%VOLTAGE_INDICATOR_LOW%\" pattern=\"^\\d{1,2}(\\.\\d{1,3})?\" required>\
</div>\
<div class=\"form-group col-md-6\">\
<label for=\"voltageIndicatorHigh\">(Optional) Upper voltage (battery) for Neopixel-visualisation (z.B. 4.2)</label>\
<input type=\"text\" class=\"form-control\" id=\"voltageIndicatorHigh\" name=\"voltageIndicatorHigh\" value=\"%VOLTAGE_INDICATOR_HIGH%\" pattern=\"^\\d{1,2}(\\.\\d{1,3})?\" required>\
</div>\
<div class=\"form-group col-md-6\">\
<label for=\"voltageCheckInterval\">(Optional) Interval of battery-measurement (minutes)</label>\
<input type=\"number\" min=\"1\" max=\"180\" class=\"form-control\" id=\"voltageCheckInterval\" name=\"voltageCheckInterval\" value=\"%VOLTAGE_CHECK_INTERVAL%\" required>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Submit</button>\
</form>\
@ -271,7 +287,11 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
mVolHeadphone: document.getElementById('maxVolumeHeadphone').value,\
iBright: document.getElementById('initBrightness').value,\
nBright: document.getElementById('nightBrightness').value,\
iTime: document.getElementById('inactivityTime').value\
iTime: document.getElementById('inactivityTime').value,\
vWarning: document.getElementById('warningLowVoltage').value,\
vIndLow: document.getElementById('voltageIndicatorLow').value,\
vIndHi: document.getElementById('voltageIndicatorHigh').value,\
vInt: document.getElementById('voltageCheckInterval').value\
}\
};\
var myJSON = JSON.stringify(myObj);\

28
src/logmessages.h

@ -18,7 +18,7 @@ static const char freeMemory[] PROGMEM = "Freier Speicher";
static const char writeEntryToNvs[] PROGMEM = "Schreibe Eintrag in NVS";
static const char freeMemoryAfterFree[] PROGMEM = "Freier Speicher nach Aufräumen";
static const char releaseMemoryOfOldPlaylist[] PROGMEM = "Gebe Speicher der alten Playlist frei.";
static const char dirOrFileDoesNotExist[] PROGMEM = "Datei oder Verzeichnis existiert nicht!";
static const char dirOrFileDoesNotExist[] PROGMEM = "Datei oder Verzeichnis existiert nicht ";
static const char unableToAllocateMemForPlaylist[] PROGMEM = "Speicher für Playlist konnte nicht allokiert werden!";
static const char unableToAllocateMem[] PROGMEM = "Speicher konnte nicht allokiert werden!";
static const char fileModeDetected[] PROGMEM = "Dateimodus erkannt.";
@ -47,6 +47,7 @@ static const char endOfPlaylistReached[] PROGMEM = "Ende der Playlist erreicht."
static const char trackStartatPos[] PROGMEM = "Titel wird abgespielt ab Position";
static const char rfidScannerReady[] PROGMEM = "RFID-Tags koennen jetzt gescannt werden...";
static const char rfidTagDetected[] PROGMEM = "RFID-Karte erkannt: ";
static const char rfid15693TagDetected[] PROGMEM = "RFID-Karte (ISO-15693) erkannt: ";
static const char rfidTagReceived[] PROGMEM = "RFID-Karte empfangen";
static const char rfidTagUnknownInNvs[] PROGMEM = "RFID-Karte ist im NVS nicht hinterlegt.";
static const char goToSleepDueToIdle[] PROGMEM = "Gehe in Deep Sleep wegen Inaktivität...";
@ -138,10 +139,33 @@ static const char ssidNotFoundInNvs[] PROGMEM = "SSID wurde im NVS nicht gefunde
static const char wifiPwdNotFoundInNvs[] PROGMEM = "WLAN-Passwort wurde im NVS nicht gefunden.";
static const char wifiStaticIpConfigNotFoundInNvs[] PROGMEM = "Statische WLAN-IP-Konfiguration wurde im NVS nicht gefunden.";
static const char wifiHostnameNotSet[] PROGMEM = "Keine Hostname-Konfiguration im NVS gefunden.";
static const char mqttConnFailed[] PROGMEM = "Verbindung fehlgeschlagen, versuche erneut in Kürze erneut";
static const char mqttConnFailed[] PROGMEM = "Verbindung fehlgeschlagen, versuche in Kürze erneut";
static const char restoredHostnameFromNvs[] PROGMEM = "Hostname aus NVS geladen";
static const char currentVoltageMsg[] PROGMEM = "Aktuelle Batteriespannung";
static const char voltageTooLow[] PROGMEM = "Batteriespannung niedrig";
static const char sdBootFailedDeepsleep[] PROGMEM = "Bootgang wegen SD fehlgeschlagen. Gehe in Deepsleep...";
static const char wifiEnabledAfterRestart[] PROGMEM = "WLAN wird aktiviert.";
static const char wifiDisabledAfterRestart[] PROGMEM = "WLAN wird deaktiviert.";
static const char voltageIndicatorLowFromNVS[] PROGMEM = "Unterer Spannungslevel (Batterie) fuer Neopixel-Anzeige aus NVS geladen";
static const char voltageIndicatorHighFromNVS[] PROGMEM = "Oberer Spannungslevel (Batterie) fuer Neopixel-Anzeige aus NVS geladen";
static const char voltageCheckIntervalFromNVS[] PROGMEM = "Zyklus für Spannungsmessung (Batterie) fuer Neopixel-Anzeige aus NVS geladen";
static const char warningLowVoltageFromNVS[] PROGMEM = "Spannungslevel (Batterie) fuer Warnung via Neopixel aus NVS geladen";
static const char unableToRestoreLastRfidFromNVS[] PROGMEM = "Letzte RFID konnte nicht aus NVS geladen werden";
static const char restoredLastRfidFromNVS[] PROGMEM = "Letzte RFID wurde aus NVS geladen";
static const char failedOpenFileForWrite[] PROGMEM = "Öffnen der Datei für den Schreibvorgang fehlgeschlagen";
static const char fileWritten[] PROGMEM = "Schreibe Datei";
static const char writeFailed[] PROGMEM = "Schreibvorgang fehlgeschlagen";
static const char writingFile[] PROGMEM = "Schreibe Datei";
static const char failedToOpenFileForAppending[] PROGMEM = "Öffnen der Datei zum Schreiben der JSON-Datei fehlgeschlagen";
static const char listingDirectory[] PROGMEM = "Verzeichnisinhalt anzeigen";
static const char failedToOpenDirectory[] PROGMEM = "Öffnen des Verzeichnisses fehlgeschlagen";
static const char notADirectory[] PROGMEM = "Kein Verzeichnis";
static const char sdMountedMmc1BitMode[] PROGMEM = "Versuche SD-Karte wird im SD_MMC-Modus (1 Bit) zu mounten...";
static const char sdMountedSpiMode[] PROGMEM = "Versuche SD-Karte wird im SPI-Modus zu mounten...";
static const char backupRecoveryWebsite[] PROGMEM = "<p>Das Backup-File wird eingespielt...<br />Zur letzten Seite <a href=\"javascript:history.back()\">zur&uuml;ckkehren</a>.</p>";
static const char restartWebsite[] PROGMEM = "<p>Der Tonuino wird neu gestartet...<br />Zur letzten Seite <a href=\"javascript:history.back()\">zur&uuml;ckkehren</a>.</p>";
static const char mqttMsgReceived[] PROGMEM = "MQTT-Nachricht empfangen";
static const char trackPausedAtPos[] PROGMEM = "Titel pausiert bei Position";
static const char freeHeapWithoutFtp[] PROGMEM = "Freier Heap-Speicher vor FTP-Instanzierung";
static const char freeHeapWithFtp[] PROGMEM = "Freier Heap-Speicher nach FTP-Instanzierung";
static const char freeHeapAfterSetup[] PROGMEM = "Freier Heap-Speicher nach Setup-Routine";

26
src/logmessages_EN.h

@ -18,7 +18,7 @@ static const char freeMemory[] PROGMEM = "Free memory";
static const char writeEntryToNvs[] PROGMEM = "Storing data to NVS";
static const char freeMemoryAfterFree[] PROGMEM = "Free memory after cleaning";
static const char releaseMemoryOfOldPlaylist[] PROGMEM = "Releasing memory of old playlist.";
static const char dirOrFileDoesNotExist[] PROGMEM = "File of directory does not exist!";
static const char dirOrFileDoesNotExist[] PROGMEM = "File of directory does not exist";
static const char unableToAllocateMemForPlaylist[] PROGMEM = "Unable to allocate memory for playlist!";
static const char unableToAllocateMem[] PROGMEM = "Unable to allocate memory!";
static const char fileModeDetected[] PROGMEM = "File-mode detected.";
@ -47,6 +47,7 @@ static const char endOfPlaylistReached[] PROGMEM = "Reached end of playlist.";
static const char trackStartatPos[] PROGMEM = "Starting track at position";
static const char rfidScannerReady[] PROGMEM = "RFID-tags can now be applied...";
static const char rfidTagDetected[] PROGMEM = "RFID-tag detected: ";
static const char rfid15693TagDetected[] PROGMEM = "RFID-ta (ISO-15693) detected: ";
static const char rfidTagReceived[] PROGMEM = "RFID-tag received";
static const char rfidTagUnknownInNvs[] PROGMEM = "RFID-tag is unkown to NVS.";
static const char goToSleepDueToIdle[] PROGMEM = "Going to deepsleep due to inactivity-timer...";
@ -145,3 +146,26 @@ static const char voltageTooLow[] PROGMEM = "Low battery-voltage";
static const char sdBootFailedDeepsleep[] PROGMEM = "Failed to boot due to SD. Will go to deepsleep...";
static const char wifiEnabledAfterRestart[] PROGMEM = "WiFi will be enabled.";
static const char wifiDisabledAfterRestart[] PROGMEM = "WiFi will be disabled .";
static const char voltageIndicatorLowFromNVS[] PROGMEM = "Restored lower voltage-level for Neopixel-display from NVS";
static const char voltageIndicatorHighFromNVS[] PROGMEM = "Restored upper voltage-level for Neopixel-display from NVS";
static const char voltageCheckIntervalFromNVS[] PROGMEM = "Restored interval of battery-measurement or Neopixel-display from NVS";
static const char warningLowVoltageFromNVS[] PROGMEM = "Restored battery-voltage-level for warning via Neopixel from NVS";
static const char unableToRestoreLastRfidFromNVS[] PROGMEM = "Unable to restore last RFID from NVS";
static const char restoredLastRfidFromNVS[] PROGMEM = "Restored last RFID from NVS";
static const char failedOpenFileForWrite[] PROGMEM = "Failed to open file for writing";
static const char fileWritten[] PROGMEM = "File written";
static const char writeFailed[] PROGMEM = "Write failed";
static const char writingFile[] PROGMEM = "Writing file";
static const char failedToOpenFileForAppending[] PROGMEM = "Failed to open file for appending";
static const char listingDirectory[] PROGMEM = "Listing directory";
static const char failedToOpenDirectory[] PROGMEM = "Failed to open directory";
static const char notADirectory[] PROGMEM = "Not a directory";
static const char sdMountedMmc1Bit[] PROGMEM = "SD-card in SD_MMC 1 Bit-mode configured...";
static const char sdMountedSpiMode[] PROGMEM = "SD card mounted in SPI-mode configured...";
static const char backupRecoveryWebsite[] PROGMEM = "<p>Backup-file is being applied...<br />Back to <a href=\"javascript:history.back()\">last page</a>.</p>";
static const char restartWebsite[] PROGMEM = "<p>Tonuino is being restarted...<br />Back to <a href=\"javascript:history.back()\">last page</a>.</p>";
static const char mqttMsgReceived[] PROGMEM = "MQTT-message received";
static const char trackPausedAtPos[] PROGMEM = "Track paused at position";
static const char freeHeapWithoutFtp[] PROGMEM = "Free heap before FTP-allocation";
static const char freeHeapWithFtp[] PROGMEM = "Free heap after FTP-allocation";
static const char freeHeapAfterSetup[] PROGMEM = "Free heap after setup";

1054
src/main.cpp
File diff suppressed because it is too large
View File

66
src/settings-espa1s.h

@ -0,0 +1,66 @@
#include "Arduino.h"
//################## GPIO-configuration ##############################
#ifdef SD_MMC_1BIT_MODE
#define SPISD_CS 15 // GPIO for chip select (SD)
// uSD-card-reader (via SD-MMC 1Bit)
//
// SD_MMC uses fixed pins
// MOSI 15
// SCKK 14
// MISO 2 // hardware pullup may required
#else
// uSD-card-reader (via SPI)
#define SPISD_CS 15 // GPIO for chip select (SD)
#ifndef SINGLE_SPI_ENABLE
#define SPISD_MOSI 13 // GPIO for master out slave in (SD) => not necessary for single-SPI
#define SPISD_MISO 16 // GPIO for master in slave ou (SD) => not necessary for single-SPI
#define SPISD_SCK 14 // GPIO for clock-signal (SD) => not necessary for single-SPI
#endif
#endif
#define MFRC522_RST_PIN 12 // needed for i2c-comm MTDI on JTAG
#define MFRC522_ADDR 0x28 // default Address of MFRC522
#define ext_IIC_CLK 23 // 14-pin-header
#define ext_IIC_DATA 18 // 14-pin-header
// I2S (DAC)
#define I2S_DOUT 25 // Digital out (I2S)
#define I2S_BCLK 27 // BCLK (I2S)
#define I2S_LRC 26 // LRC (I2S)
// I2C (AC101)
#define IIC_CLK 32 // internal
#define IIC_DATA 33 // internal
// Amp enable
#define GPIO_PA_EN GPIO_NUM_21 // internal
#define GPIO_SEL_PA_EN GPIO_SEL_21
// Rotary encoder
#define DREHENCODER_CLK 5 // If you want to reverse encoder's direction, just switch GPIOs of CLK with DT (in software or hardware)
#define DREHENCODER_DT 18 // Info: Lolin D32 / Lolin D32 pro 35 are using 35 for battery-voltage-monitoring!
#define DREHENCODER_BUTTON 4 // Button is used to switch Tonuino on and off
// Control-buttons
#define PAUSEPLAY_BUTTON 36 // GPIO to detect pause/play
#define NEXT_BUTTON 199 // GPIO to detect next
#define PREVIOUS_BUTTON 198 // GPIO to detect previous (Important: as of 19.11.2020 changed from 33 to 2)
// Power-control
#define POWER 19 // GPIO used to drive transistor-circuit, that switches off peripheral devices while ESP32-deepsleep
// (optional) Neopixel
#define LED_PIN 23 // GPIO for Neopixel-signaling
// (optinal) Headphone-detection
#ifdef HEADPHONE_ADJUST_ENABLE
#define HP_DETECT 39 // GPIO that detects, if there's a plug in the headphone jack or not
#endif
// (optional) Monitoring of battery-voltage via ADC
#ifdef MEASURE_BATTERY_VOLTAGE
#define VOLTAGE_READ_PIN 33 // GPIO used to monitor battery-voltage. Change to 35 if you're using Lolin D32 or Lolin D32 pro as it's hard-wired there!
#endif

61
src/settings-lolin32.h

@ -0,0 +1,61 @@
#include "Arduino.h"
//################## GPIO-configuration ##############################
#ifdef SD_MMC_1BIT_MODE
// uSD-card-reader (via SD-MMC 1Bit)
//
// SD_MMC uses fixed pins
// MOSI 15
// SCKK 14
// MISO 2 // hardware pullup may required
#else
// uSD-card-reader (via SPI)
#define SPISD_CS 15 // GPIO for chip select (SD)
#ifndef SINGLE_SPI_ENABLE
#define SPISD_MOSI 13 // GPIO for master out slave in (SD) => not necessary for single-SPI
#define SPISD_MISO 16 // GPIO for master in slave ou (SD) => not necessary for single-SPI
#define SPISD_SCK 14 // GPIO for clock-signal (SD) => not necessary for single-SPI
#endif
#endif
// RFID (via SPI)
#define RST_PIN 99 // Not necessary but has to be set anyway; so let's use a dummy-number
#define RFID_CS 21 // GPIO for chip select (RFID)
#define RFID_MOSI 23 // GPIO for master out slave in (RFID)
#define RFID_MISO 19 // GPIO for master in slave out (RFID)
#define RFID_SCK 18 // GPIO for clock-signal (RFID)
#ifdef RFID_READER_TYPE_PN5180
#define RFID_BUSY 16 // PN5180 BUSY PIN
#define RFID_RST 22 // PN5180 RESET PIN
#endif
// I2S (DAC)
#define I2S_DOUT 25 // Digital out (I2S)
#define I2S_BCLK 27 // BCLK (I2S)
#define I2S_LRC 26 // LRC (I2S)
// Rotary encoder
#define DREHENCODER_CLK 34 // If you want to reverse encoder's direction, just switch GPIOs of CLK with DT (in software or hardware)
#define DREHENCODER_DT 35 // Info: Lolin D32 / Lolin D32 pro 35 are using 35 for battery-voltage-monitoring!
#define DREHENCODER_BUTTON 32 // Button is used to switch Tonuino on and off
// Control-buttons
#define PAUSEPLAY_BUTTON 5 // GPIO to detect pause/play
#define NEXT_BUTTON 4 // GPIO to detect next
#define PREVIOUS_BUTTON 2 // GPIO to detect previous (Important: as of 19.11.2020 changed from 33 to 2)
// (optional) Power-control
#define POWER 17 // GPIO used to drive transistor-circuit, that switches off peripheral devices while ESP32-deepsleep
// (optional) Neopixel
#define LED_PIN 12 // GPIO for Neopixel-signaling
// (optinal) Headphone-detection
#ifdef HEADPHONE_ADJUST_ENABLE
#define HP_DETECT 22 // GPIO that detects, if there's a plug in the headphone jack or not
#endif
// (optional) Monitoring of battery-voltage via ADC
#ifdef MEASURE_BATTERY_VOLTAGE
#define VOLTAGE_READ_PIN 33 // GPIO used to monitor battery-voltage. Change to 35 if you're using Lolin D32 or Lolin D32 pro as it's hard-wired there!
#endif

117
src/settings.h

@ -0,0 +1,117 @@
#include "Arduino.h"
//########################## MODULES #################################
//#define MDNS_ENABLE // When enabled, you don't have to handle with Tonuino's IP-address. If hostname is set to "tonuino", you can reach it via tonuino.local
//#define MQTT_ENABLE // Make sure to configure mqtt-server and (optionally) username+pwd
//#define FTP_ENABLE // Enables FTP-server; DON'T FORGET TO ACTIVATE AFTER BOOT BY PRESSING PAUSE + NEXT-BUTTONS (IN PARALLEL)!
//#define NEOPIXEL_ENABLE // Don't forget configuration of NUM_LEDS if enabled
#define NEOPIXEL_REVERSE_ROTATION // Some Neopixels are adressed/soldered counter-clockwise. This can be configured here.
#define LANGUAGE 1 // 1 = deutsch; 2 = english
//#define HEADPHONE_ADJUST_ENABLE // Used to adjust (lower) volume for optional headphone-pcb (refer maxVolumeSpeaker / maxVolumeHeadphone)
#define SHUTDOWN_IF_SD_BOOT_FAILS // Will put ESP to deepsleep if boot fails due to SD. Really recommend this if there's in battery-mode no other way to restart ESP! Interval adjustable via deepsleepTimeAfterBootFails.
//#define MEASURE_BATTERY_VOLTAGE // Enables battery-measurement via GPIO (ADC) and voltage-divider
//#define PLAY_LAST_RFID_AFTER_REBOOT // When restarting Tonuino, the last RFID that was active before, is recalled and played
//#define BLUETOOTH_ENABLE // Doesn't work currently (so don't enable) as there's not enough DRAM available
//################## select SD card mode #############################
#define SD_MMC_1BIT_MODE // run SD card in SD-MMC 1Bit mode
#define SINGLE_SPI_ENABLE // If only one SPI-instance should be used instead of two (not yet working!) (Works on ESP32-A1S with RFID via I2C)
//################## select RFID reader ##############################
//#define RFID_READER_TYPE_MFRC522_SPI // use MFRC522 via SPI
#define RFID_READER_TYPE_MFRC522_I2C // use MFRC522 via I2C
//#define RFID_READER_TYPE_PN5180
//################## select Hardware Platform ##############################
// #define HAL 2 // HAL 1 = LoLin32, 2 = AI AudioKit - no need to define when using platformIO BuildProcess
//#################### Various settings ##############################
// Loglevels available (don't change!)
#define LOGLEVEL_ERROR 1 // only errors
#define LOGLEVEL_NOTICE 2 // errors + important messages
#define LOGLEVEL_INFO 3 // infos + errors + important messages
#define LOGLEVEL_DEBUG 4 // almost everything
// Serial-logging-configuration
const uint8_t serialDebug = LOGLEVEL_DEBUG; // Current loglevel for serial console
// Buttons (better leave unchanged if in doubts :-))
uint8_t buttonDebounceInterval = 50; // Interval in ms to software-debounce buttons
uint16_t intervalToLongPress = 700; // Interval in ms to distinguish between short and long press of previous/next-button
// RFID
#define RFID_SCAN_INTERVAL 300 // Interval-time in ms (how often is RFID read?)
// Automatic restart
#ifdef SHUTDOWN_IF_SD_BOOT_FAILS
uint32_t deepsleepTimeAfterBootFails = 20; // Automatic restart takes place if boot was not successful after this period (in seconds)
#endif
// FTP
// Nothing to be configured here...
// Default user/password is esp32/esp32 but can be changed via webgui
// Tonuino will create a WiFi if joing existing WiFi was not possible. Name can be configured here.
static const char accessPointNetworkSSID[] PROGMEM = "Tonuino"; // Access-point's SSID
// Where to store the backup-file for NVS-records
static const char backupFile[] PROGMEM = "/backup.txt"; // File is written every time a (new) RFID-assignment via GUI is done
// (webgui) File Browser
uint8_t FS_DEPTH = 5; // Max. recursion-depth of file tree
const char *DIRECTORY_INDEX_FILE = "/files.json"; // Filename of files.json index file
//#################### Settings for optional Modules##############################
// (optinal) Neopixel
#ifdef NEOPIXEL_ENABLE
#define NUM_LEDS 24 // number of LEDs
#define CHIPSET WS2812B // type of Neopixel
#define COLOR_ORDER GRB
#endif
// (optional) Default-voltages for battery-monitoring via Neopixel
float warningLowVoltage = 3.4; // If battery-voltage is >= this value, a cyclic warning will be indicated by Neopixel (can be changed via GUI!)
uint8_t voltageCheckInterval = 10; // How of battery-voltage is measured (in minutes) (can be changed via GUI!)
float voltageIndicatorLow = 3.0; // Lower range for Neopixel-voltage-indication (0 leds) (can be changed via GUI!)
float voltageIndicatorHigh = 4.2; // Upper range for Neopixel-voltage-indication (all leds) (can be changed via GUI!)
// (optinal) For measuring battery-voltage a voltage-divider is necessary. Their values need to be configured here.
#ifdef MEASURE_BATTERY_VOLTAGE
uint8_t rdiv1 = 129; // Rdiv1 of voltage-divider (kOhms) (measure exact value with multimeter!)
uint16_t rdiv2 = 389; // Rdiv2 of voltage-divider (kOhms) (measure exact value with multimeter!) => used to measure voltage via ADC!
#endif
// (optinal) Headphone-detection (leave unchanged if in doubts...)
#ifdef HEADPHONE_ADJUST_ENABLE
uint16_t headphoneLastDetectionDebounce = 1000; // Debounce-interval in ms when plugging in headphone
#endif
// (optional) Topics for MQTT
#ifdef MQTT_ENABLE
uint16_t mqttRetryInterval = 15; // Try to reconnect to MQTT-server every (n) seconds if connection is broken
uint8_t mqttMaxRetriesPerInterval = 1; // Number of retries per time-interval (mqttRetryInterval). mqttRetryInterval 15 / mqttMaxRetriesPerInterval 1 => once every 15s
#define DEVICE_HOSTNAME "ESP32-Tonuino" // Name that that is used for MQTT
static const char topicSleepCmnd[] PROGMEM = "Cmnd/Tonuino/Sleep";
static const char topicSleepState[] PROGMEM = "State/Tonuino/Sleep";
static const char topicTrackCmnd[] PROGMEM = "Cmnd/Tonuino/Track";
static const char topicTrackState[] PROGMEM = "State/Tonuino/Track";
static const char topicTrackControlCmnd[] PROGMEM = "Cmnd/Tonuino/TrackControl";
static const char topicLoudnessCmnd[] PROGMEM = "Cmnd/Tonuino/Loudness";
static const char topicLoudnessState[] PROGMEM = "State/Tonuino/Loudness";
static const char topicSleepTimerCmnd[] PROGMEM = "Cmnd/Tonuino/SleepTimer";
static const char topicSleepTimerState[] PROGMEM = "State/Tonuino/SleepTimer";
static const char topicState[] PROGMEM = "State/Tonuino/State";
static const char topicCurrentIPv4IP[] PROGMEM = "State/Tonuino/IPv4";
static const char topicLockControlsCmnd[] PROGMEM ="Cmnd/Tonuino/LockControls";
static const char topicLockControlsState[] PROGMEM ="State/Tonuino/LockControls";
static const char topicPlaymodeState[] PROGMEM = "State/Tonuino/Playmode";
static const char topicRepeatModeCmnd[] PROGMEM = "Cmnd/Tonuino/RepeatMode";
static const char topicRepeatModeState[] PROGMEM = "State/Tonuino/RepeatMode";
static const char topicLedBrightnessCmnd[] PROGMEM = "Cmnd/Tonuino/LedBrightness";
static const char topicLedBrightnessState[] PROGMEM = "State/Tonuino/LedBrightness";
#ifdef MEASURE_BATTERY_VOLTAGE
static const char topicBatteryVoltage[] PROGMEM = "State/Tonuino/Voltage";
#endif
#endif

24
src/websiteBasic.h

@ -1,24 +0,0 @@
static const char basicWebsite[] PROGMEM = "<!DOCTYPE html>\
<html>\
<head>\
<title>WLAN-Einrichtung</title>\
</head>\
<body>\
<form action=\"/init\" method=\"POST\">\
<fieldset>\
<legend>Initiale WLAN-Einrichtung</legend>\
<label for=\"ssid\">SSID:</label><br>\
<input type=\"text\" id=\"ssid\" name=\"ssid\" placeholder=\"SSID\" required><br>\
<label for=\"pwd\">Passwort:</label><br>\
<input type=\"password\" id=\"pwd\" name=\"pwd\" autocomplete=\"off\" required><br>\
<label for=\"hostname\">Tonuino-Name (Hostname):</label><br>\
<input type=\"text\" id=\"hostname\" name=\"hostname\" required><br><br>\
<input type=\"submit\" value=\"Absenden\">\
</fieldset>\
</form>\
<form action=\"/restart\">\
<button type=\"submit\">Neustart</button>\
</form>\
</body>\
</html>\
";

360
src/websiteMgmt.h

@ -1,360 +0,0 @@
static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<html lang=\"de\">\
<head>\
<title>ESPuino-Konfiguration</title>\
<meta charset=\"utf-8\">\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\
<link rel=\"stylesheet\" href=\"https://ts-cs.de/tonuino/css/bootstrap.min.css\">\
<script src=\"https://ts-cs.de/tonuino/js/jquery.min.js\"></script>\
<script src=\"https://ts-cs.de/tonuino/js/popper.min.js\"></script>\
<script src=\"https://ts-cs.de/tonuino/js/bootstrap.min.js\"></script>\
</head>\
<body>\
<nav class=\"navbar navbar-expand-sm bg-primary navbar-dark\">\
<button class=\"navbar-toggler\" type=\"button\" data-toggle=\"collapse\" data-target=\"#navbarSupportedContent\" aria-controls=\"navbarSupportedContent\" aria-expanded=\"false\" aria-label=\"Toggle navigation\">\
<span class=\"navbar-toggler-icon\"></span>\
</button>\
<a class=\"navbar-brand\">\
<img src=\"https://raw.githubusercontent.com/biologist79/Tonuino-ESP32-I2S/master/html/tonuino_logo.png\" width=\"30\" height=\"30\" class=\"d-inline-block align-top\" alt=\"\" />\
Tonuino\
</a>\
<div class=\"collapse navbar-collapse\" id=\"collapsibleNavbar\">\
<ul class=\"navbar-nav mr-auto\">\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#wifiConfig\">WLAN</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#rfidMusicTags\">RFID-Zuweisungen</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#rfidModTags\">RFID-Modifikationen</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#mqttConfig\">MQTT</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#ftpConfig\">FTP</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#generalConfig\">Allgemein</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"#importNvs\">NVS-Importer</a>\
</li>\
<li class=\"nav-item\">\
<a class=\"nav-link\" href=\"/restart\" style=\"color: orange\">Neustart Tonuino</a>\
</li>\
</ul>\
</div>\
</nav>\
<br />\
<div class=\"container\" id=\"wifiConfig\">\
<h2>WLAN-Konfiguration</h2>\
<form action=\"#wifiConfig\" method=\"POST\" onsubmit=\"wifiConfig('wifiConfig'); return false\">\
<div class=\"form-group col-md-6\">\
<label for=\"ssid\">WLAN-Name (SSID):</label>\
<input type=\"text\" class=\"form-control\" id=\"ssid\" placeholder=\"SSID\" name=\"ssid\" required>\
<div class=\"invalid-feedback\">\
Bitte SSID des WLANs eintragen.\
</div>\
<label for=\"pwd\">Passwort:</label>\
<input type=\"password\" class=\"form-control\" id=\"pwd\" placeholder=\"Passwort\" name=\"pwd\" required>\
<label for=\"hostname\">Tonuino-Name (Hostname):</label>\
<input type=\"text\" class=\"form-control\" id=\"hostname\" placeholder=\"tonuino\" name=\"hostname\" value=\"%HOSTNAME%\" pattern=\"^[^-\\.]{2,32}\" required>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</form>\
<div class=\"messages col-md-6 my-2\"></div>\
</div>\
<div class=\"container my-5\" id=\"rfidMusicTags\">\
<h2>RFID-Zuweisungen</h2>\
<form action=\"#rfidMusicTags\" method=\"POST\" onsubmit=\"rfidAssign('rfidMusicTags'); return false\">\
<div class=\"form-group col-md-6\">\
<label for=\"rfidIdMusic\">RFID-Chip-Nummer (12-stellig)</label>\
<input type=\"text\" class=\"form-control\" id=\"rfidIdMusic\" maxlength=\"12\" pattern=\"[0-9]{12}\" placeholder=\"%RFID_TAG_ID%\" name=\"rfidIdMusic\" required>\
<label for=\"fileOrUrl\">Datei, Verzeichnis oder URL (^ und # als Zeichen nicht erlaubt)</label>\
<input type=\"text\" class=\"form-control\" id=\"fileOrUrl\" maxlength=\"255\" placeholder=\"z.B. /mp3/Hoerspiele/Yakari/Yakari_und_seine_Freunde.mp3\" pattern=\"^[^\\^#]+$\" name=\"fileOrUrl\" required>\
<label for=\"playMode\">Abspielmodus</label>\
<select class=\"form-control\" id=\"playMode\" name=\"playMode\">\
<option value=\"1\">Einzelner Titel</option>\
<option value=\"2\">Einzelner Titel (Endlosschleife)</option>\
<option value=\"3\">Hörbuch</option>\
<option value=\"4\">Hörbuch (Endlosschleife)</option>\
<option value=\"5\">Alle Titel eines Verzeichnis (sortiert)</option>\
<option value=\"6\">Alle Titel eines Verzeichnis (zufällig)</option>\
<option value=\"7\">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option>\
<option value=\"9\">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>\
<option value=\"8\">Webradio</option>\
</select>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</form>\
<div class=\"messages col-md-6 my-2\"></div>\
</div>\
<div class=\"container my-5\" id=\"rfidModTags\">\
<h2>RFID-Modifkationen</h2>\
<form class=\"needs-validation\" action=\"#rfidModTags\" method=\"POST\" onsubmit=\"rfidMods('rfidModTags'); return false\">\
<div class=\"form-group col-md-6\">\
<label for=\"rfidIdMod\">RFID-Chip-Nummer (12-stellig)</label>\
<input type=\"text\" class=\"form-control\" id=\"rfidIdMod\" maxlength=\"12\" pattern=\"[0-9]{12}\" placeholder=\"%RFID_TAG_ID%\" name=\"rfidIdMod\" required>\
<div class=\"invalid-feedback\">\
Bitte eine 12-stellige Zahl eingeben.\
</div>\
<label for=\"modId\">Abspielmodus</label>\
<select class=\"form-control\" id=\"modId\" name=\"modId\">\
<option value=\"100\">Tastensperre</option>\
<option value=\"101\">Schlafen nach 15 Minuten</option>\
<option value=\"102\">Schlafen nach 30 Minuten</option>\
<option value=\"103\">Schlafen nach 1 Stunde</option>\
<option value=\"104\">Schlafen nach 2 Stunden</option>\
<option value=\"105\">Schlafen nach Ende des Titels</option>\
<option value=\"106\">Schlafen nach Ende der Playlist</option>\
<option value=\"107\">Schlafen nach fünf Titeln</option>\
<option value=\"110\">Wiederhole Playlist (endlos)</option>\
<option value=\"111\">Wiederhole Titel (endlos)</option>\
<option value=\"112\">Dimme LEDs (Nachtmodus)</option>\
<option value=\"130\">Aktiviere/deaktive WLAN</option>\
</select>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</form>\
<div class=\"messages col-md-6 my-2\"></div>\
</div>\
<div class=\"container my-5\" id=\"mqttConfig\">\
<h2>MQTT-Konfiguration</h2>\
<form class=\"needs-validation\" action=\"#mqttConfig\" method=\"POST\" onsubmit=\"mqttSettings('mqttConfig'); return false\">\
<div class=\"form-check col-md-6\">\
<input class=\"form-check-input\" type=\"checkbox\" value=\"1\" id=\"mqttEnable\" name=\"mqttEnable\" %MQTT_ENABLE%>\
<label class=\"form-check-label\" for=\"mqttEnable\">\
MQTT aktivieren\
</label>\
</div>\
<div class=\"form-group my-2 col-md-6\">\
<label for=\"mqttServer\">MQTT-Server</label>\
<input type=\"text\" class=\"form-control\" id=\"mqttServer\" minlength=\"7\" maxlength=\"%MQTT_SERVER_LENGTH%\" placeholder=\"z.B. 192.168.2.89\" name=\"mqttServer\" value=\"%MQTT_SERVER%\">\
<label for=\"mqttUser\">MQTT-Benutzername (optional):</label>\
<input type=\"text\" class=\"form-control\" id=\"mqttUser\" maxlength=\"%MQTT_USER_LENGTH%\" placeholder=\"Benutzername\" name=\"mqttUser\" value=\"%MQTT_USER%\">\
<label for=\"mqttPwd\">Passwort (optional):</label>\
<input type=\"password\" class=\"form-control\" id=\"mqttPwd\" maxlength=\"%MQTT_PWD_LENGTH%\" placeholder=\"Passwort\" name=\"mqttPwd\" value=\"%MQTT_PWD%\">\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</form>\
<div class=\"messages col-md-6 my-2\"></div>\
</div>\
<div class=\"container\" id=\"ftpConfig\">\
<h2>FTP-Konfiguration</h2>\
<form action=\"#ftpConfig\" method=\"POST\" onsubmit=\"ftpSettings('ftpConfig'); return false\">\
<div class=\"form-group col-md-6\">\
<label for=\"ftpUser\">FTP-Benutzername:</label>\
<input type=\"text\" class=\"form-control\" id=\"ftpUser\" maxlength=\"%FTP_USER_LENGTH%\" placeholder=\"Benutzername\" name=\"ftpUser\" value=\"%FTP_USER%\" required>\
<label for=\"pwd\">Passwort:</label>\
<input type=\"password\" class=\"form-control\" id=\"ftpPwd\" maxlength=\"%FTP_PWD_LENGTH%\" placeholder=\"Passwort\" name=\"ftpPwd\" value=\"%FTP_PWD%\" required>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</form>\
<div class=\"messages col-md-6 my-2\"></div>\
</div>\
<div class=\"container my-5\" id=\"generalConfig\">\
<h2>Allgemeine Konfiguration</h2>\
<form action=\"#generalConfig\" method=\"POST\" onsubmit=\"genSettings('generalConfig'); return false\">\
<div class=\"form-group col-md-6\">\
<label for=\"initialVolume\">Lautstärke nach dem Einschalten</label>\
<input type=\"number\" min=\"1\" max=\"21\" class=\"form-control\" id=\"initialVolume\" name=\"initialVolume\" value=\"%INIT_VOLUME%\" required>\
<label for=\"maxVolumeSpeaker\">Maximale Lautstärke (Lautsprecher)</label>\
<input type=\"number\" min=\"1\" max=\"21\" class=\"form-control\" id=\"maxVolumeSpeaker\" name=\"maxVolumeSpeaker\" value=\"%MAX_VOLUME_SPEAKER%\" required>\
<label for=\"maxVolumeHeadphone\">Maximale Lautstärke (Kopfhörer)</label>\
<input type=\"number\" min=\"1\" max=\"21\" class=\"form-control\" id=\"maxVolumeHeadphone\" name=\"maxVolumeHeadphone\" value=\"%MAX_VOLUME_HEADPHONE%\" required>\
</div>\
<div class=\"form-group col-md-6\">\
<label for=\"initBrightness\">Neopixel-Helligkeit nach dem Einschalten</label>\
<input type=\"number\" min=\"0\" max=\"255\" class=\"form-control\" id=\"initBrightness\" name=\"initBrightness\" value=\"%INIT_LED_BRIGHTNESS%\" required>\
<label for=\"nightBrightness\">Neopixel-Helligkeit im Nachtmodus</label>\
<input type=\"number\" min=\"0\" max=\"255\" class=\"form-control\" id=\"nightBrightness\" name=\"nightBrightness\" value=\"%NIGHT_LED_BRIGHTNESS%\" required>\
</div>\
<div class=\"form-group col-md-6\">\
<label for=\"inactivityTime\">Deep-Sleep nach Inaktivität (Minuten)</label>\
<input type=\"number\" min=\"1\" max=\"1440\" class=\"form-control\" id=\"inactivityTime\" name=\"inactivityTime\" value=\"%MAX_INACTIVITY%\" required>\
</div>\
<button type=\"reset\" class=\"btn btn-secondary\">Reset</button>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</form>\
<div class=\"messages col-md-6 my-2\"></div>\
</div>\
<div class=\"container my-5\" id=\"importNvs\">\
<h2>NVS-Importer</h2>\
<form action=\"/upload\" enctype=\"multipart/form-data\" method=\"POST\">\
<div class=\"form-group\">\
<label for=\"nvsUpload\">Hier kann eine Backup-Datei importiert werden.</label>\
<input type=\"file\" class=\"form-control-file\" id=\"nvsUpload\" name=\"nvsUpload\" accept=\".txt\">\
</div>\
<button type=\"submit\" class=\"btn btn-primary\">Absenden</button>\
</form>\
<div class=\"messages col-md-6 my-2\"></div>\
</div>\
<script>\
var lastIdclicked = '';\
var errorBox = '<div class=\"alert alert-danger alert-dismissible fade show\" role=\"alert\">Es ist ein Fehler aufgetreten!<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button></div>';\
var okBox = '<div class=\"alert alert-success alert-dismissible fade show\" role=\"alert\">Aktion erfolgreich ausgeführt.<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button></div>';\
\
var socket = new WebSocket(\"ws://%IPv4%/ws\");\
\
function connect() {\
socket = new WebSocket(\"ws://%IPv4%/ws\");\
}\
\
function ping() {\
var myObj = {\
\"ping\": {\
ping: 'ping'\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
tm = setTimeout(function () {\
alert(\"Die Verbindung zum Tonuino ist unterbrochen!\\nBitte Seite neu laden.\");\
}, 5000);\
}\
\
function pong() {\
clearTimeout(tm);\
}\
\
socket.onopen = function () {\
setInterval(ping, 15000);\
};\
\
socket.onclose = function(e) {\
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);\
setTimeout(function() {\
connect();\
}, 5000);\
};\
\
socket.onerror = function(err) {\
console.error('Socket encountered error: ', err.message, 'Closing socket');\
socket.close();\
};\
\
socket.onmessage = function(event) {\
console.log(event.data);\
var socketMsg = JSON.parse(event.data);\
if (socketMsg.rfidId != null) {\
document.getElementById('rfidIdMod').value = socketMsg.rfidId;\
document.getElementById('rfidIdMusic').value = socketMsg.rfidId;\
$(\"#rfidIdMusic\").fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500);\
$(\"#rfidIdMod\").fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500);\
\
} if (socketMsg.status != null) {\
if (socketMsg.status == 'ok') {\
$(\"#\" + lastIdclicked).find('.messages').html(okBox);\
} else {\
$(\"#\" + lastIdclicked).find('.messages').html(errorBox);\
}\
} if (socketMsg.pong != null) {\
if (socketMsg.pong == 'pong') {\
pong();\
}\
}\
};\
\
function genSettings(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"general\": {\
iVol: document.getElementById('initialVolume').value,\
mVolSpeaker: document.getElementById('maxVolumeSpeaker').value,\
mVolHeadphone: document.getElementById('maxVolumeHeadphone').value,\
iBright: document.getElementById('initBrightness').value,\
nBright: document.getElementById('nightBrightness').value,\
iTime: document.getElementById('inactivityTime').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function ftpSettings(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"ftp\": {\
ftpUser: document.getElementById('ftpUser').value,\
ftpPwd: document.getElementById('ftpPwd').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function mqttSettings(clickedId) {\
lastIdclicked = clickedId;\
var val;\
if (document.getElementById('mqttEnable').checked) {\
val = document.getElementById('mqttEnable').value;\
} else {\
val = 0;\
}\
var myObj = {\
\"mqtt\": {\
mqttEnable: val,\
mqttServer: document.getElementById('mqttServer').value,\
mqttUser: document.getElementById('mqttUser').value,\
mqttPwd: document.getElementById('mqttPwd').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function rfidMods(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"rfidMod\": {\
rfidIdMod: document.getElementById('rfidIdMod').value,\
modId: document.getElementById('modId').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function removeTrSlash(str) {\
if(str.substr(-1) === '/') {\
return str.substr(0, str.length - 1);\
}\
return str;\
}\
\
function rfidAssign(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"rfidAssign\": {\
rfidIdMusic: document.getElementById('rfidIdMusic').value,\
fileOrUrl: removeTrSlash(document.getElementById('fileOrUrl').value),\
playMode: document.getElementById('playMode').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
\
function wifiConfig(clickedId) {\
lastIdclicked = clickedId;\
var myObj = {\
\"wifiConfig\": {\
ssid: document.getElementById('ssid').value,\
pwd: document.getElementById('pwd').value,\
hostname: document.getElementById('hostname').value\
}\
};\
var myJSON = JSON.stringify(myObj);\
socket.send(myJSON);\
}\
</script>\
</body>\
</html>\
";
Loading…
Cancel
Save