Last three events:
CMD_SEEK_BACKWARDSit's possible to jump a number of seconds defined in
PLAY_MONO_SPEAKER. If active, mono is used while headphones remain stereo (if
The basic idea of ESPuino 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.
The core of my implementation is based on the popular ESP32 by Espressif. 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. And nonetheless Bluetooth, too! However, my primary focus was to port the project to a modular base. Having said this mp3-decoding is done in software with a dedicated µSD-card-slot and music-output is done via I2S-protocol. I did all my tests on Adafruit's MAX98357A, UDA1334 and headphone-pcb. Hopefully, not only in theory, other DACs that support I2S can be used as well.
The heart of my project is an ESP32 on a Wemos Lolin32 development-board. 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.). But probably it's better to use it's successor (which has battery-measurement already included): Wemos Lolin D32. Or, if you're planning to enjoy webstreams regularly, Wemos Lolin D32 pro might be a good decision. It comes with µSD-slot onboard and because of its ESP32-WROVER-B, it has PSRAM included. PSRAM is used as webstream-cache and might be used more intensively in future by ESPuino in general.
git clone https://github.com/biologist79/ESPuino.git. Using git you can keep your local repository easily up to date without doing copy'n'paste. To keep it up to date run
git pull origin master. Further infos [here}(https://stackoverflow.com/questions/1443210/updating-a-local-repository-with-changes-from-a-github-repository).
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 to know what to change.
src/settings.haccording your needs. Especially don't forget to set
HALdepending on your develboard.
HAL) config-file (e.g.
settings-lolin32.hfor Lolin32 or
settings-lolin_d32.hfor Lolin D32). If you're running a board that is not listed there: start with
settings-custom.hand change it according your needs.
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. Depending on your develboard it might me necessary to push a button in order to allow ESP32 to enter flashmode (not necessary für Lolin32, D32 und D32 pro).
192.168.4.1to your webbrowser. Enter WiFi-credentials and the hostname. After saving the configuraton, restart ESPuino. Hint: I tried to connect this access-point via Android mobile. Basically that's no problem, but as my mobile detected there'd be no internet-connection, it keept LTE-connection open and prevented me from connecting to
192.168.4.1. So if in doubts use a computer.
src/settings.h, you can use the hostname configured extended by .local instead the IP. So if you configured
espuinoas hostname, you can use
espuino.localfor webgui and FTP.
settings.hto be able to activate it. Neopixel flashes green (1x) if enabling was successful. It'll be disabled automatically after next reboot. Means: you have to enable it every time you need it (if reboot was in between). Sounds annoying and maybe it is, but's running this way in order to have more heap-memory available (for webstream) if FTP isn't running.
values.h(values >= 100).
Huge Appas otherwise the sketch won't fit into the flash-memory.
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 + 130k resistors (VBat(+)--130k--X--130k--VBat(-)). X is the measure-point where to connect the GPIO to. If using Lolin D32 or Lolin D32 pro, make sure to leave both resistor-values unchanged at 100k. Same for GPIO: unchanged at 35. 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.
HEADPHONE_ADJUST_ENABLEto 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.
SHUTDOWN_IF_SD_BOOT_FAILSis really recommended if you run your ESPuino in battery-mode without having a restart-button exposed to the outside of ESPuino's enclosure. Because otherwise there's no way to restart your ESPuino and the error-state will remain until battery is empty (or you open the enclosure, hehe).
PLAY_LAST_RFID_AFTER_REBOOTwill tell ESPuino to remember the last RFID-tag played after next reboot. So rebooting ESPuino will end up in autoplay. I don't really recommend this feature because if you use it along with a webstream, in case of streaming-issues this might end up in an infinite loop.
Having SD working is mandatory. However, there are two modes available to access SD-cards: SPI and SD-MMC (1 bit).
Advantages SD-MMC (1 bit) over SPI:
pinMode(2, INPUT_PULLUP);. So it's not really a problem but you have to take note of that!
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' (you need a password to read Tonies). You can also wake-up the board with the card. Disadvantages: is a bit more expensive and needs more GPIOs (6/7 instead of 4). Refer PN5180's wire-section below for further informations. Hint: if using 3.3V make sure to connect PN5180 to +5V AND 3.3V. Sounds weird but it's necessary.
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 ESPuino working. After my first experiments on a breadboard I soldered all the stuff onto a PCB in order to avoid wild-west-cabling. Especially for the interconnect between µC and µSD-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. 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 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 mute MAX98537a and so 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 that makes use of GND.
Have a look at the PCB-folder. I provided PCBs for a number of develboards. Probably this makes things easier for you.
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.
| ESP32 (GPIO) | Hardware | Pin | Comment | | ------------- | --------------------- | ------ | ------------------------------------------------------------ | | 3.3 (5) V | SD-reader | VCC | Connect to p-channel MOSFET for power-saving when µC is off | | GND | SD-reader | GND | | | 15 | SD-reader | CS | | | 13 | SD-reader | MOSI | | | 16 | SD-reader | MISO | | | 14 | SD-reader | SCK | | | 3.3 V | RFID-reader | 3.3V | (Connect directly to GPIO 17 for power-saving when µC is off) | | GND | RFID-reader | GND | | | 21 | RFID-reader | CS/SDA | | | 23 | RFID-reader | MOSI | | | 19 | RFID-reader | MISO | | | 18 | RFID-reader | SCK | | | 5 / 3.3 V | MAX98357 | VIN | Connect to p-channel MOSFET for power-saving when µC 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 | CLK | Change CLK with DT if you want to change the direction of RE | | 35 | Rotary encoder | DT | Change CLK with DT if you want to change the direction of RE | | 32 | Rotary encoder | BUTTON | | | 3.3 V | Rotary encoder | + | | | GND | Rotary encoder | GND | | | 4 | Button (next) | | | | GND | Button (next) | | | | 2 | Button (previous) | | | | GND | Button (previous) | | | | 5 | Button (pause/play) | | | | GND | Button (pause/play) | | | | 3.3 V | Neopixel | V | Connect to p-channel MOSFET for power-saving when µC is off | | GND | Neopixel | G | | | 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. Please refer the schematics for my Lolin32-PCB for further informations. If you need further informations on transistor-circuits visit this website.
In general I recommend using a µSD-card-reader 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 Lolin-PCBs with 3.3V only.
|--||SD-reader||CS||no CS required|
|2||SD-reader||MISO||make sure there's no hardware-pullup for MISO|
Make sure to enable
SD_MMC_1BIT_MODE if you want to use this feature. Don't(!) enable
SINGLE_SPI_ENABLE. 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).
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. 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 :-)
|3.3 (5) V||SD-reader||VCC||Connect to p-channel MOSFET for power-saving when µC is off|
|15||SD-reader||CS||Don't share with RFID!|
|3.3 V||RFID-reader||3.3V||Connect directly to GPIO 17 for power-saving when µC is off|
|21||RFID-reader||CS/SDA||Don't share with SD!|
|5 / 3.3 V||MAX98357||VIN||Connect to p-channel MOSFET for power-saving when µC is off|
|---||MAX98357||SD||Info: if pulled down to GND amp will turn off|
|34||Rotary encoder||CLK||Change CLK with DT if you want to change the direction of RE|
|35||Rotary encoder||DT||Change CLK with DT if you want to change the direction of RE|
|3.3 V||Rotary encoder||+|
|5 / 3.3 V||Neopixel||V||Connect to p-channel MOSFET for power-saving when µC is off|
|33||Voltage-divider / BAT||Optional: voltage-divider to monitor battery-voltage|
|22||Headphone jack||Optional: if pulled to ground, headphone-volume is set|
PN5180-reader needs at least two more GPIOs: 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 if doing so!
You can enable low power card-detection with
PN5180_ENABLE_LPCD, but this needs another GPIO for IRQ. With low power card detection (LPCD) you can wake-up the ESP32 from deep-sleep just by applying a card to the reader. You need a PN5180-firmware >= 4.0. Most china-boards comes with older firmware. The latest firmware can be flashed with this project.
|3.3 V||PN5180 RFID-reader||3.3V||Connect directly to GPIO 17 for power-saving when µC is off|
|3.3 V||3.3V||For low power card detection mode (LPCD) connect directly to 3.3V|
|5 / 3.3 V||PN5180 RFID-reader||5V||Don't forget to connect this pin the same way as 3.3V|
|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|
|39||PN5180 RFID-reader||IRQ||optional, used for low power card detection (LPCD)|
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.
Here 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 there. Please note: the code-basis is outdated. Will need to re-integrate it into my master-branch...
WiFi is mandatory for webgui, FTP and MQTT. However, WiFi can be temporarily or permanently disabled. There are two ways to do that:
ESPuino can be used as bluetooth-sink (a2dp). This mode can be enabled/disabled via a RFID-modification-card. Applying one will restart ESPuino immediately. Two modes are available which are toggled in between: "normal" and "bluetooth". Normal means: SD + WiFi are available whereas in mode "bluetooth" only bluetooth-support can be provided. If bluetooth is active, this is indicated by four slow rotating blue LEDs. Now you can stream to your ESPuino e.g. with your mobile device. Tested this with Android 8 and worked 100% flawless.
There might be situations where you run out of GPIOs. To address this, port-expander PCA9555 can be used to extend number of input-channels (output-mode is not supported by ESPuino). This port-expander provides 2 ports with 8 channels each - so 16 channels in total. To activate PCA9555 you need to set
PORT_EXPANDER_ENABLE. Like GPIOs in your develboard-specific settings-file, you can assign numbers. Range is 100->115 where 100: port 0 channel 0 -> 107: port 0 channel 7; 108: port 1 channel 0 -> 115: port 1 channel 7. If you only need 8 channels: connect stuff only to port 0 and change
portsToRead to 1. Via
expanderI2cAddress port-expander's I2C-address can be changed. 0x20 is true, if all A0, A1, A2 are wired to GND.
Port-expander is read by hardware-timer. Interrupt-pin can be connected optionally and is only used to wake up ESP32. If so I suggest to use
After bringing ESPuino 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:
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 rebuild 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.
It's not just simply playing music; different playmodes are supported:
single track=> plays one track one time
single track (loop)=> plays one track forever
audiobook=> single file or playlist/folder; last play-position (file and playlist) is saved (when pushing pause or moving to another track) and re-used next time
audiobook(loop) => same as audiobook but loops forever
folder/playlist (alph. sorted)=> plays all tracks in alph. order from a folder one time
folder/playlist (random order)=> plays all tracks in random order from a folder one time
folder/playlist (alph. sorted)=> plays all tracks in alph. order from a folder forever
folder/playlist (random order)=> plays all tracks in random order from a folder forever
webradio=> always only one "track": plays a webstream
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:
Indicates different things. Don't forget configuration of number of LEDs via #define NUM_LEDS
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
Important: this section describes my default-design: 3 buttons + rotary-encoder. Feel free to change button-number and button-actions according your needs in
settings.h and your develboard-specific config-file (e.g.
settings-lolin32.h). At maximum you can activate five buttons + rotary-encoder.
Minimum duration for long press in ms is defined by
PLAY_LAST_RFID_AFTER_REBOOTis active, ESPuino will remember the last RFID applied => music-autoplay (if in doubts don't use this feature).
This mode is different from the other ones because the last playposition is saved. Playposition is saved when...
After having ESPuino running on your ESP32 in your local WiFi, the webinterface-configuration is accessable. Using this GUI you can configure:
settings.hto be able to activate it! Neopixel flashes green (1x) if enabling was successful. It'll be disabled automatically after next reboot. Means: you have to enable it every time you need it (if reboot was in between). Sounds annoying and maybe it is, but's running this way in order to have more heap-memory available (for webstream) if FTP isn't running.
esp32but can be changed later via GUI.
As already described in the modify-section, there are different sleepmodes available. Additionaly µC will be put into deepsleep after 10 minutes of inactivity (configurable my maxInactivityTime) unless ESPuino doesn't play music, has a FTP-client connected and any input via buttons. Every button-interaction resets the counter.
Everything that can be controlled via RFID-tags and buttons, can also be controlled via MQTT (excepting toggling WiFi-status as this doesn't make sense). All manual interactions (buttons, RFID-tags) are also sent to MQTT in parallel, so everything is always in-sync (unless Wifi/MQTT-connection is broken). In my home-setup I'm using openHAB to "encapsulate" MQTT into a nice GUI, that's accessible via APP + web. I described a sample-config for openHAB2. However, meanwhile openHAb3 is available and all the stuff described can also be configured via GUI. Be advised that openHAB is pretty complex and you have to spend some time to get familiar with it.
Please refer ESP32-audioI2S, as this is the library I used for music-decoding. Make sure to update especially this library regularly as it's development is still in progress.
As all assignments between RFID-IDs and actions (playmode, file to play...) is saved in ESP's NVS, the problem is that it's all gone when the ESP is broken. So that's where a backup comes into play. So every time you change or add a new assignment between a RFID-tag and an action via GUI, a backup-file is saved on the µSD-card. The file's name can be changed via
backupFile. So better don't delete it! Using the webgui you can use the upload-form to import such a file. To be honest: Sometimes I had some issues with Firefox doing this whereas Safari turned out to do it right. Don't know why :-(.
As already described, MQTT is supported. In order to use it it's necessary to run a MQTT-broker; Mosquitto for instance. After connecting to it, ESPuino subscribes to all command-topics. State-topics are used to push states to the broker in order to inform others if anything changed (change of volume, new playlist, new track... name it). Others, like openHAB, subscribe to state-topics end send commands via command-topics. So it's not just limited to openHAB. It's just necessary to use a platform, that supports MQTT. For further informations (and pictures) refer the subfolder.
Feel free to use your own smarthome-environments (instead of openHAB). The MQTT-topics available are described as follows. Please note: if you want to send a command to ESPuino, you have to use a cmnd-topic whereas ESPuino pushes its states back via state-topics. So guess you want to change the volume to 8 you have to send this number via topic-variable
topicLoudnessCmnd. Immediately after doing to, ESPuino sends a conformation of this command using
topicLoudnessState. To get hands on MQTT I recommend this one as introducton (covers more than you need for ESPuino).
|topicSleepCmnd||0 or OFF||Power off ESPuino immediately|
|topicSleepState||ON or OFF||Sends ESPuino's current/last state|
|topicRfidCmnd||12 digits||Set number of RFID-tag which 'emulates' an RFID-tag (e.g.
|topicRfidState||12 digits||ID of current RFID-tag (if not a modification-card)|
|topicTrackState||String||Sends current track number, total number of tracks and full path of curren track. E.g. "(2/10) /mp3/kinderlieder/Ri ra rutsch.mp3"|
|topicTrackControlCmnd||1 -> 7||
|topicLoudnessCmnd||0 -> 21||Set loudness (depends on minVolume / maxVolume)|
|topicLoudnessState||0 -> 21||Sends loudness (depends on minVolume / maxVolume|
|topicSleepTimerCmnd||EOP||Power off after end to playlist|
|EOT||Power off after end of track|
|EO5T||Power off after end of five tracks|
|1 -> 2^32||Duration in minutes to power off|
|0||Deactivate timer (if active)|
|topicSleepTimerState||various||Sends active timer (
|topicCurrentIPv4IP||IPv4-string||Sends ESPuino's IP-address (e.g.
|topicLockControlsCmnd||ON, OFF||Set if controls (buttons, rotary encoder) should be locked|
|topicLockControlsState||ON, OFF||Sends if controls (buttons, rotary encoder) are locked|
|topicPlaymodeState||0 - 10||Sends current playmode (single track, audiobook...; see playmodes)|
|topicRepeatModeCmnd||0 - 3||Set repeat-mode:
|topicRepeatModeState||0 - 3||Sends repeat-mode|
|topicLedBrightnessCmnd||0 - 255||Set brightness of Neopixel|
|topicLedBrightnessState||0 - 255||Sends brightness of Neopixel|
|topicBatteryVoltage||float||Voltage (e.g. 3.81)|