- 
					1.gitignore
- 
					54Hardware-Plaforms/Wemos Lolin32/README.md
- 
					BINPCBs/Wemos Lolin32/Gerber/gerber.zip
- 
					BINPCBs/Wemos Lolin32/KiCad/Kicad-files.zip
- 
					BINPCBs/Wemos Lolin32/Pictures/3d-Model downside.jpg
- 
					BINPCBs/Wemos Lolin32/Pictures/3d-Model upside.jpg
- 
					BINPCBs/Wemos Lolin32/Pictures/Tonuino-Lolin32-Schematics.pdf
- 
					54PCBs/Wemos Lolin32/README.md
- 
					40README.md
- 
					432html/website.html
- 
					263html/websiteMgmt.h
- 
					BINpictures/Mgmt-GUI1.jpg
- 
					BINpictures/Mgmt-GUI2.jpg
- 
					BINpictures/Mgmt-GUI3.jpg
- 
					BINpictures/Mgmt-GUI4.jpg
- 
					BINpictures/Mgmt-GUI5.jpg
- 
					BINpictures/Mgmt-GUI6.jpg
- 
					BINpictures/Mgmt-GUI_connection_broken.jpg
- 
					BINpictures/Mgmt_GUI_action_ok.jpg
- 
					6platformio.ini
- 
					6processHtml.py
- 
					373src/main.cpp
- 
					153src/settings.h
- 
					432src/websiteMgmt.h
| @ -0,0 +1,54 @@ | |||||
|  | # 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. | ||||
|  | 
 | ||||
|  | ## 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 and battery (not 2.54mm pitch!) | ||||
|  | * 2.54mm-connectors for MAX98354a 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%20TMD1308) 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%20TMD1308) makes use of an amp that (probably) "allows" children to damage ears. This maximum volume can be set and re-adjusted via webgui. | ||||
|  | 
 | ||||
|  | ## Prerequisites | ||||
|  | * If no [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TMD1308) 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` und `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. This is because I already had ten of them (see link below). Accidently, on my PCB-layout, I switched the direction of one of these two connectors. However, it's just an visual issue. | ||||
|  | * 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. 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" (black => (+); red => GND). | ||||
|  | * Better don't solder Lolin32 directly to the PCB. I recommend to make use of female connectors instead (link below). | ||||
|  | 
 | ||||
|  | ## 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) | ||||
|  | 
 | ||||
|  | ## 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) | ||||
|  | 
 | ||||
|  | ## 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. | ||||
| After Width: 1438 | Height: 799 | Size: 53 KiB | 
| After Width: 1438 | Height: 799 | Size: 65 KiB | 
| @ -0,0 +1,54 @@ | |||||
|  | # 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. | ||||
|  | 
 | ||||
|  | ## 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 and battery (not 2.54mm pitch!) | ||||
|  | * 2.54mm-connectors for MAX98354a 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%20TMD1308) 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%20TMD1308) makes use of an amp that (probably) "allows" children to damage ears. This maximum volume can be set and re-adjusted via webgui. | ||||
|  | 
 | ||||
|  | ## Prerequisites | ||||
|  | * If no [headphone-pcb](https://github.com/biologist79/Tonuino-ESP32-I2S/tree/master/PCBs/Headphone%20with%20PCM5102a%20and%20TMD1308) 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` und `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. This is because I already had ten of them (see link below). Accidently, on my PCB-layout, I switched the direction of one of these two connectors. However, it's just an visual issue. | ||||
|  | * 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. 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" (black => (+); red => GND). | ||||
|  | * Better don't solder Lolin32 directly to the PCB. I recommend to make use of female connectors instead (link below). | ||||
|  | 
 | ||||
|  | ## 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) | ||||
|  | 
 | ||||
|  | ## 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) | ||||
|  | 
 | ||||
|  | ## 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. | ||||
| @ -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>\ |  | ||||
| "; |  | ||||
| Before Width: 1080 | Height: 1856 | Size: 336 KiB After Width: 1080 | Height: 1208 | Size: 197 KiB | 
| Before Width: 1080 | Height: 1223 | Size: 188 KiB After Width: 883 | Height: 1280 | Size: 190 KiB | 
| Before Width: 1080 | Height: 1644 | Size: 240 KiB After Width: 825 | Height: 1280 | Size: 132 KiB | 
| Before Width: 1080 | Height: 1471 | Size: 245 KiB After Width: 921 | Height: 1280 | Size: 132 KiB | 
| Before Width: 1080 | Height: 602 | Size: 81 KiB After Width: 851 | Height: 1280 | Size: 156 KiB | 
| After Width: 1080 | Height: 1857 | Size: 313 KiB | 
| Before Width: 1080 | Height: 1853 | Size: 242 KiB After Width: 1080 | Height: 1206 | Size: 236 KiB | 
| Before Width: 1080 | Height: 1843 | Size: 268 KiB After Width: 1032 | Height: 1280 | Size: 137 KiB | 
| @ -0,0 +1,153 @@ | |||||
|  | #include "Arduino.h" | ||||
|  | 
 | ||||
|  | //########################## MODULES ################################# | ||||
|  | #define MQTT_ENABLE                 // Make sure to configure mqtt-server and (optionally) username+pwd | ||||
|  | #define FTP_ENABLE                  // Enables FTP-server | ||||
|  | #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 SINGLE_SPI_ENABLE         // If only one SPI-instance should be used instead of two (not yet working!) | ||||
|  | //#define SD_NOT_MANDATORY_ENABLE   // Only for debugging-purposes: Tonuino will also start without mounted SD-card anyway (will only try once to mount it). Will overwrite SHUTDOWN_IF_SD_BOOT_FAILS! | ||||
|  | //#define BLUETOOTH_ENABLE          // Doesn't work currently (so don't enable) as there's not enough DRAM available | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | //################## GPIO-configuration ############################## | ||||
|  | // 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 | ||||
|  | 
 | ||||
|  | // 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) | ||||
|  | 
 | ||||
|  | // 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 | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | //#################### 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 | ||||
|  | #ifndef SD_NOT_MANDATORY_ENABLE | ||||
|  |     static const char backupFile[] PROGMEM = "/backup.txt"; // File is written every time a (new) RFID-assignment via GUI is done | ||||
|  | #endif | ||||
|  | 
 | ||||
|  | // (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 | ||||
|  | 
 | ||||
|  | // (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 | ||||
|  |     #define DEVICE_HOSTNAME "ESP32-Tonuino-Leonie"                 // Name that that is used for MQTT | ||||
|  |     static const char topicSleepCmnd[] PROGMEM = "Cmnd/Tonuino-Leonie/Sleep"; | ||||
|  |     static const char topicSleepState[] PROGMEM = "State/Tonuino-Leonie/Sleep"; | ||||
|  |     static const char topicTrackCmnd[] PROGMEM = "Cmnd/Tonuino-Leonie/Track"; | ||||
|  |     static const char topicTrackState[] PROGMEM = "State/Tonuino-Leonie/Track"; | ||||
|  |     static const char topicTrackControlCmnd[] PROGMEM = "Cmnd/Tonuino-Leonie/TrackControl"; | ||||
|  |     static const char topicLoudnessCmnd[] PROGMEM = "Cmnd/Tonuino-Leonie/Loudness"; | ||||
|  |     static const char topicLoudnessState[] PROGMEM = "State/Tonuino-Leonie/Loudness"; | ||||
|  |     static const char topicSleepTimerCmnd[] PROGMEM = "Cmnd/Tonuino-Leonie/SleepTimer"; | ||||
|  |     static const char topicSleepTimerState[] PROGMEM = "State/Tonuino-Leonie/SleepTimer"; | ||||
|  |     static const char topicState[] PROGMEM = "State/Tonuino-Leonie/State"; | ||||
|  |     static const char topicCurrentIPv4IP[] PROGMEM = "State/Tonuino-Leonie/IPv4"; | ||||
|  |     static const char topicLockControlsCmnd[] PROGMEM ="Cmnd/Tonuino-Leonie/LockControls"; | ||||
|  |     static const char topicLockControlsState[] PROGMEM ="State/Tonuino-Leonie/LockControls"; | ||||
|  |     static const char topicPlaymodeState[] PROGMEM = "State/Tonuino-Leonie/Playmode"; | ||||
|  |     static const char topicRepeatModeCmnd[] PROGMEM = "Cmnd/Tonuino-Leonie/RepeatMode"; | ||||
|  |     static const char topicRepeatModeState[] PROGMEM = "State/Tonuino-Leonie/RepeatMode"; | ||||
|  |     static const char topicLedBrightnessCmnd[] PROGMEM = "Cmnd/Tonuino-Leonie/LedBrightness"; | ||||
|  |     static const char topicLedBrightnessState[] PROGMEM = "State/Tonuino-Leonie/LedBrightness"; | ||||
|  |     static const char topicBatteryVoltage[] PROGMEM = "State/Tonuino-Leonie/Voltage"; | ||||
|  | #endif | ||||