Browse Source

Introduced optional headphone-volume

master
Torsten Stauder 5 years ago
parent
commit
24b6dc3f0d
  1. 25
      README.md
  2. 9
      html/website.html
  3. 13
      html/website_EN.html
  4. 7
      src/logmessages.h
  5. 7
      src/logmessages_EN.h
  6. 113
      src/main.cpp
  7. 9
      src/websiteMgmt.h
  8. 13
      src/websiteMgmt_EN.h

25
README.md

@ -1,7 +1,7 @@
# Tonuino based on ESP32 with I2S-DAC-support
## NEWS
Currently I'm working on a new Tonuino that is completely based on 3.3V. As uC-develboard a Lolin32 is used and it's (optionally) battery-powered. So stay tuned...
Currently I'm working on a new Tonuino that is completely based on 3.3V and makes use of an (optional) headphone-pcb. As uC-develboard a Lolin32 is used and it's (optionally) battery-powered. So stay tuned...
## 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/).
@ -16,26 +16,27 @@ The core of my implementation is based on the popular [ESP32 by Espressif](https
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.
## Hardware-setup
The heart of my project is an ESP32 on a development-board that more or less looks like [this](https://docs.zerynth.com/latest/official/board.zerynth.nodemcu_esp32/docs/index.html). 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 for "ESP32S" or "Lolin 32".
* [Adafruit's MAX98357A](https://learn.adafruit.com/adafruit-max98357-i2s-class-d-mono-amp/pinouts)
* [uSD-card-reader](https://www.amazon.de/AZDelivery-Reader-Speicher-Memory-Arduino/dp/B077MB17JB)
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".
* [Adafruit's MAX98357A](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-SD-Karten-SD-Card-Modul-SPI-fur-Breadboard-Arduino-Raspberry-Pi/183106778276)
* [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/16Bit-RGB-LED-Ring-WS2812-5V-ahnl-Neopixel-fur-Arduino-Raspberry-Pi/173881828935)
* [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)
* [Buttons](https://de.aliexpress.com/item/32697109472.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.
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. I don't earn money with my links.
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.
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)!
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 but 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 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 set to GND, if there's no plug and vice versa. Using for example a MOSFET-circuit, this 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.
| ESP32 (GPIO) | Hardware | Pin | Comment |
| ------------- | --------------------- | ------ | ------------------------------------------------------------ |
@ -47,7 +48,6 @@ A lot of wiring is necessary to get ESP32-Tonuino working. After my first experi
| 14 | SD-reader | SCK | |
| 17 | RFID-reader | 3.3V | Connect directly to GPIO 17 for power-saving when uC is off |
| GND | RFID-reader | GND | |
| 22 | RFID-reader | RST | |
| 21 | RFID-reader | CS/SDA | |
| 23 | RFID-reader | MOSI | |
| 19 | RFID-reader | MISO | |
@ -57,6 +57,7 @@ A lot of wiring is necessary to get ESP32-Tonuino working. After my first experi
| 25 | MAX98357 | DIN | |
| 27 | MAX98357 | BCLK | |
| 26 | MAX98357 | LRC | |
| --- | MAX98357 | SD | 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 |
| 32 | Rotary encoder | BUTTON | |
@ -87,7 +88,6 @@ In this case RFID-reader + SD-reader share SPI's SCK, MISO and MOSI. But make su
| 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 |
| GND | RFID-reader | GND | |
| 22 | RFID-reader | RST | |
| 21 | RFID-reader | CS/SDA | Don't share with SD! |
| 23 | RFID+SD-reader | MOSI | |
| 19 | RFID+SD-reader | MISO | |
@ -97,6 +97,7 @@ In this case RFID-reader + SD-reader share SPI's SCK, MISO and MOSI. But make su
| 25 | MAX98357 | DIN | |
| 27 | MAX98357 | BCLK | |
| 26 | MAX98357 | LRC | |
| --- | MAX98357 | SD | 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 |
| 32 | Rotary encoder | BUTTON | |
@ -114,7 +115,7 @@ In this case RFID-reader + SD-reader share SPI's SCK, MISO and MOSI. But make su
| 17 | (e.g.) BC337 (via R5) | Base | Don't forget R5! |
## Wiring (custom) / different pinout
When using a develboard with for example SD-card-reader already integrated, the pinouts described above my not fit your needs. Additionaly some boards may use one or some of the GPIOs I used for internal purposes and 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 />
When using a develboard with for example SD-card-reader already integrated, the pinouts described above my not fit your needs; 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

9
html/website.html

@ -164,8 +164,10 @@
<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>
<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>
@ -264,7 +266,8 @@
var myObj = {
"general": {
iVol: document.getElementById('initialVolume').value,
mVol: document.getElementById('maxVolume').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

13
html/website_EN.html

@ -97,10 +97,10 @@
<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>
<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 12-digits-number.
Please enter a number with 12 digits.
</div>
<label for="modId">Abspielmodus</label>
<select class="form-control" id="modId" name="modId">
@ -164,8 +164,10 @@
<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="maxVolume">Maximum volume</label>
<input type="number" min="1" max="21" class="form-control" id="maxVolume" name="maxVolume" value="%MAX_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>
@ -264,7 +266,8 @@
var myObj = {
"general": {
iVol: document.getElementById('initialVolume').value,
mVol: document.getElementById('maxVolume').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

7
src/logmessages.h

@ -118,8 +118,11 @@ static const char restoredMaxInactivityFromNvs[] PROGMEM = "Maximale Inaktivitä
static const char wroteMaxInactivityToNvs[] PROGMEM = "Maximale Inaktivitätszeit wurde ins NVS geschrieben.";
static const char restoredInitialLoudnessFromNvs[] PROGMEM = "Initiale Lautstärke wurde aus NVS geladen";
static const char wroteInitialLoudnessToNvs[] PROGMEM = "Initiale Lautstärke wurde ins NVS geschrieben.";
static const char restoredMaxLoudnessFromNvs[] PROGMEM = "Maximale Lautstärke wurde aus NVS geladen";
static const char wroteMaxLoudnessToNvs[] PROGMEM = "Maximale Lautstärke wurde ins NVS geschrieben.";
static const char restoredMaxLoudnessForSpeakerFromNvs[] PROGMEM = "Maximale Lautstärke für Lautsprecher wurde aus NVS geladen";
static const char restoredMaxLoudnessForHeadphoneFromNvs[] PROGMEM = "Maximale Lautstärke für Kopfhörer wurde aus NVS geladen";
static const char wroteMaxLoudnessForSpeakerToNvs[] PROGMEM = "Maximale Lautstärke für Lautsprecher wurde ins NVS geschrieben.";
static const char wroteMaxLoudnessForHeadphoneToNvs[] PROGMEM = "Maximale Lautstärke für Kopfhörer wurde ins NVS geschrieben.";
static const char maxVolumeSet[] PROGMEM = "Maximale Lautstärke wurde gesetzt auf ";
static const char wroteMqttFlagToNvs[] PROGMEM = "MQTT-Flag wurde ins NVS geschrieben.";
static const char restoredMqttActiveFromNvs[] PROGMEM = "MQTT-Flag (aktiviert) wurde aus NVS geladen";
static const char restoredMqttDeactiveFromNvs[] PROGMEM = "MQTT-Flag (deaktiviert) wurde aus NVS geladen";

7
src/logmessages_EN.h

@ -118,8 +118,11 @@ static const char restoredMaxInactivityFromNvs[] PROGMEM = "Restored maximum ina
static const char wroteMaxInactivityToNvs[] PROGMEM = "Stored maximum inactivity-time to NVS.";
static const char restoredInitialLoudnessFromNvs[] PROGMEM = "Restored initial volume from NVS";
static const char wroteInitialLoudnessToNvs[] PROGMEM = "Stored initial volume to NVS.";
static const char restoredMaxLoudnessFromNvs[] PROGMEM = "Restored maximum volume from NVS";
static const char wroteMaxLoudnessToNvs[] PROGMEM = "Stored maximum volume to NVS.";
static const char restoredMaxLoudnessForSpeakerFromNvs[] PROGMEM = "Restored maximum volume for speaker from NVS";
static const char restoredMaxLoudnessForHeadphoneFromNvs[] PROGMEM = "Restored maximum volume for headphone from NVS";
static const char wroteMaxLoudnessForSpeakerToNvs[] PROGMEM = "Wrote maximum volume for speaker to NVS.";
static const char wroteMaxLoudnessForHeadphoneToNvs[] PROGMEM = "Wrote maximum volume for headphone to NVS.";
static const char maxVolumeSet[] PROGMEM = "Maximum volume set to ";
static const char wroteMqttFlagToNvs[] PROGMEM = "Stored MQTT-flag to NVS.";
static const char restoredMqttActiveFromNvs[] PROGMEM = "Restored MQTT-flag (enabled) from NVS";
static const char restoredMqttDeactiveFromNvs[] PROGMEM = "Restored MQTT-flag (disabled) from NVS";

113
src/main.cpp

@ -4,6 +4,7 @@
#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 SINGLE_SPI_ENABLE // If only one SPI-instance should be used instead of two
//#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 )
@ -77,7 +78,7 @@ char *logBuf = (char*) calloc(serialLoglength, sizeof(char)); // Buffer for all
#endif
// GPIOs (RFID-readercurrentRfidTagId)
#define RST_PIN 22
#define RST_PIN 99 // Not necessary but has to be set anyway; so let's use a dummy-number
#define RFID_CS 21
#define RFID_MOSI 23
#define RFID_MISO 19
@ -88,6 +89,14 @@ char *logBuf = (char*) calloc(serialLoglength, sizeof(char)); // Buffer for all
#define I2S_BCLK 27
#define I2S_LRC 26
// GPIO to detect if headphone was plugged in (set to GND)
#ifdef HEADPHONE_ADJUST_ENABLE
#define HP_DETECT 22 // Detects if there's a plug in the headphone jack or not
bool headphoneLastDetectionState;
uint32_t headphoneLastDetectionTimestamp = 0;
uint16_t headphoneLastDetectionDebounce = 1000; // Debounce-interval in ms when plugging in headphone
#endif
#ifdef BLUETOOTH_ENABLE
BluetoothA2DPSink a2dp_sink;
#endif
@ -199,9 +208,13 @@ bool enableMqtt = true;
#define RFID_SCAN_INTERVAL 300 // in ms
uint8_t const cardIdSize = 4; // RFID
// Volume
uint8_t maxVolume = 21; // Maximum volume that can be adjusted (default; can be changed later via GUI)
uint8_t maxVolume = 21; // Current maximum volume that can be adjusted
uint8_t maxVolumeSpeaker = 21; // Maximum volume that can be adjusted in speaker-mode (default; can be changed later via GUI)
uint8_t minVolume = 0; // Lowest volume that can be adjusted
uint8_t initVolume = 3; // 0...21 (If not found in NVS, this one will be taken) (default; can be changed later via GUI)
#ifdef HEADPHONE_ADJUST_ENABLE
uint8_t maxVolumeHeadphone = 11; // Maximum volume that can be adjusted in headphone-mode (default; can be changed later via GUI)
#endif
// Sleep
uint8_t maxInactivityTime = 10; // Time in minutes, after uC is put to deep sleep because of inactivity
uint8_t sleepTimer = 30; // Sleep timer in minutes that can be optionally used (and modified later via MQTT or RFID)
@ -370,6 +383,7 @@ bool endsWith (const char *str, const char *suf);
bool fileValid(const char *_fileItem);
void freeMultiCharArray(char **arr, const uint32_t cnt);
uint8_t getRepeatMode(void);
void headphoneVolumeManager(void);
bool isNumber(const char *str);
void loggerNl(const char *str, const uint8_t logLevel);
void logger(const char *str, const uint8_t logLevel);
@ -396,6 +410,7 @@ String templateProcessor(const String& templ);
void trackControlToQueueSender(const uint8_t trackCommand);
void rfidPreferenceLookupHandler (void);
void sendWebsocketData(uint32_t client, uint8_t code);
void setupVolume(void);
void trackQueueDispatcher(const char *_sdFile, const uint32_t _lastPlayPos, const uint32_t _playMode, const uint16_t _trackLastPlayed);
void volumeHandler(const int32_t _minVolume, const int32_t _maxVolume);
void volumeToQueueSender(const int32_t _newVolume);
@ -2675,8 +2690,10 @@ String templateProcessor(const String& templ) {
return String(prefsSettings.getUInt("mInactiviyT", 0));
} else if (templ == "INIT_VOLUME") {
return String(prefsSettings.getUInt("initVolume", 0));
} else if (templ == "MAX_VOLUME") {
return String(prefsSettings.getUInt("maxVolume", 0));
} else if (templ == "MAX_VOLUME_SPEAKER") {
return String(prefsSettings.getUInt("maxVolumeSp", 0));
} else if (templ == "MAX_VOLUME_HEADPHONE") {
return String(prefsSettings.getUInt("maxVolumeHp", 0));
} else if (templ == "MQTT_SERVER") {
return prefsSettings.getString("mqttServer", "-1");
} else if (templ == "MQTT_ENABLE") {
@ -2730,20 +2747,23 @@ bool processJsonRequest(char *_serialJson) {
if (doc.containsKey("general")) {
uint8_t iVol = doc["general"]["iVol"].as<uint8_t>();
uint8_t mVol = doc["general"]["mVol"].as<uint8_t>();
uint8_t mVolSpeaker = doc["general"]["mVolSpeaker"].as<uint8_t>();
uint8_t mVolHeadphone = doc["general"]["mVolHeadphone"].as<uint8_t>();
uint8_t iBright = doc["general"]["iBright"].as<uint8_t>();
uint8_t nBright = doc["general"]["nBright"].as<uint8_t>();
uint8_t iTime = doc["general"]["iTime"].as<uint8_t>();
prefsSettings.putUInt("initVolume", iVol);
prefsSettings.putUInt("maxVolume", mVol);
prefsSettings.putUInt("maxVolumeSp", mVolSpeaker);
prefsSettings.putUInt("maxVolumeHp", mVolHeadphone);
prefsSettings.putUChar("iLedBrightness", iBright);
prefsSettings.putUChar("nLedBrightness", nBright);
prefsSettings.putUInt("mInactiviyT", iTime);
// Check if settings were written successfully
if (prefsSettings.getUInt("initVolume", 0) != iVol ||
prefsSettings.getUInt("maxVolume", 0) != mVol ||
prefsSettings.getUInt("maxVolumeSp", 0) != mVolSpeaker ||
prefsSettings.getUInt("maxVolumeHp", 0) != mVolHeadphone |
prefsSettings.getUChar("iLedBrightness", 0) != iBright ||
prefsSettings.getUChar("nLedBrightness", 0) != nBright ||
prefsSettings.getUInt("mInactiviyT", 0) != iTime) {
@ -2914,6 +2934,46 @@ void onWebsocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsE
}
}
// Set maxVolume depending on headphone-adjustment is enabled and headphone is/is not connected
void setupVolume(void) {
#ifndef HEADPHONE_ADJUST_ENABLE
maxVolume = maxVolumeSpeaker;
return;
#else
if (digitalRead(HP_DETECT)) {
maxVolume = maxVolumeSpeaker; // 1 if headphone is not connected
} else {
maxVolume = maxVolumeHeadphone; // 0 if headphone is connected (put to GND)
}
snprintf(logBuf, serialLoglength, "%s: %u", (char *) FPSTR(maxVolumeSet), maxVolume);
loggerNl(logBuf, LOGLEVEL_INFO);
return;
#endif
}
#ifdef HEADPHONE_ADJUST_ENABLE
void headphoneVolumeManager(void) {
bool currentHeadPhoneDetectionState = digitalRead(HP_DETECT);
if (headphoneLastDetectionState != currentHeadPhoneDetectionState && (millis() - headphoneLastDetectionTimestamp >= headphoneLastDetectionDebounce)) {
if (currentHeadPhoneDetectionState) {
maxVolume = maxVolumeSpeaker;
} else {
maxVolume = maxVolumeHeadphone;
if (currentVolume > maxVolume) {
volumeToQueueSender(maxVolume); // Lower volume for headphone if headphone's maxvolume is exceeded by volume set in speaker-mode
}
}
headphoneLastDetectionState = currentHeadPhoneDetectionState;
headphoneLastDetectionTimestamp = millis();
snprintf(logBuf, serialLoglength, "%s: %u", (char *) FPSTR(maxVolumeSet), maxVolume);
loggerNl(logBuf, LOGLEVEL_INFO);
}
}
#endif
bool isNumber(const char *str) {
byte i = 0;
@ -3105,6 +3165,11 @@ void setup() {
#endif
}
#ifdef HEADPHONE_ADJUST_ENABLE
pinMode(HP_DETECT, INPUT);
headphoneLastDetectionState = digitalRead(HP_DETECT);
#endif
#ifdef BLUETOOTH_ENABLE
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCLK,
@ -3206,16 +3271,33 @@ void setup() {
loggerNl((char *) FPSTR(wroteInitialLoudnessToNvs), LOGLEVEL_ERROR);
}
// Get maximum volume from NVS
uint32_t nvsMaxVolume = prefsSettings.getUInt("maxVolume", 0);
if (nvsMaxVolume) {
maxVolume = nvsMaxVolume;
snprintf(logBuf, serialLoglength, "%s: %u", (char *) FPSTR(restoredMaxLoudnessFromNvs), nvsMaxVolume);
// Get maximum volume for speaker from NVS
uint32_t nvsMaxVolumeSpeaker = prefsSettings.getUInt("maxVolumeSp", 0);
if (nvsMaxVolumeSpeaker) {
maxVolumeSpeaker = nvsMaxVolumeSpeaker;
maxVolume = maxVolumeSpeaker;
snprintf(logBuf, serialLoglength, "%s: %u", (char *) FPSTR(restoredMaxLoudnessForSpeakerFromNvs), nvsMaxVolumeSpeaker);
loggerNl(logBuf, LOGLEVEL_INFO);
} else {
prefsSettings.putUInt("maxVolumeSp", nvsMaxVolumeSpeaker);
loggerNl((char *) FPSTR(wroteMaxLoudnessForSpeakerToNvs), LOGLEVEL_ERROR);
}
#ifdef HEADPHONE_ADJUST_ENABLE
// Get maximum volume for headphone from NVS
uint32_t nvsMaxVolumeHeadphone = prefsSettings.getUInt("maxVolumeHp", 0);
if (nvsMaxVolumeHeadphone) {
maxVolumeHeadphone = nvsMaxVolumeHeadphone;
snprintf(logBuf, serialLoglength, "%s: %u", (char *) FPSTR(restoredMaxLoudnessForHeadphoneFromNvs), nvsMaxVolumeHeadphone);
loggerNl(logBuf, LOGLEVEL_INFO);
} else {
prefsSettings.putUInt("maxVolume", maxVolume);
loggerNl((char *) FPSTR(wroteMaxLoudnessToNvs), LOGLEVEL_ERROR);
prefsSettings.putUInt("maxVolumeHp", nvsMaxVolumeHeadphone);
loggerNl((char *) FPSTR(wroteMaxLoudnessForHeadphoneToNvs), LOGLEVEL_ERROR);
}
#endif
// Adjust volume depending on headphone is connected and volume-adjustment is enabled
setupVolume();
// Get MQTT-enable from NVS
uint8_t nvsEnableMqtt = prefsSettings.getUChar("enableMQTT", 99);
@ -3364,6 +3446,9 @@ void setup() {
void loop() {
#ifdef HEADPHONE_ADJUST_ENABLE
headphoneVolumeManager();
#endif
volumeHandler(minVolume, maxVolume);
buttonHandler();
doButtonActions();

9
src/websiteMgmt.h

@ -164,8 +164,10 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<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>\
<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>\
@ -264,7 +266,8 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
var myObj = {\
\"general\": {\
iVol: document.getElementById('initialVolume').value,\
mVol: document.getElementById('maxVolume').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\

13
src/websiteMgmt_EN.h

@ -97,10 +97,10 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<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>\
<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 12-digits-number.\
Please enter a number with 12 digits.\
</div>\
<label for=\"modId\">Abspielmodus</label>\
<select class=\"form-control\" id=\"modId\" name=\"modId\">\
@ -164,8 +164,10 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
<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=\"maxVolume\">Maximum volume</label>\
<input type=\"number\" min=\"1\" max=\"21\" class=\"form-control\" id=\"maxVolume\" name=\"maxVolume\" value=\"%MAX_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>\
@ -264,7 +266,8 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
var myObj = {\
\"general\": {\
iVol: document.getElementById('initialVolume').value,\
mVol: document.getElementById('maxVolume').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\

Loading…
Cancel
Save