What is LittleSpeaker

The idea is to build a small mono speaker that is repairable, hackable and can do some more advanced things than just being a Bluetooth speaker.

Design of the enclosure is by Jennifer Leis, Firmware and electronics by me.

Assembly graphic

You can find all design files and the firmware in the LittleSpeaker Github Repository

This blog entry is basically a modified Readme from that repository.


The prototype enclosure was 3D printed and is held together by a hinge and just one M3 screw.

You will need:

  1. 1x Front bezel
    Front bezel graphic
  2. 1x Back bezel
    Back bezel graphic
  3. 1x Back plate
    Back plate graphic
  4. 1x Front plate
    Front plate graphic
  5. 1x Hinge
    Hinge graphic
  6. 2x Knob
    Knob graphic
  7. Two small pieces of wood or plastic as holding braces for the PCBs that will be accessible from the outside (15 x 40mm two M3 trough holes)
    Braces graphic
  8. Brass hotmelt inserts for screwing the front and back together and for the braces:
    • 1 x M3x6
    • 4 x M3x4
      Brass insert graphic
  9. 5x M3x10 Cap head screws
  10. Some self-tapping screws to attach the power switch.
  11. A piece of Copper wire to fit into the hinge to connect the front and back pieces
  12. Glue to glue the front and back plates into the bezels and some foam rubber strips or foam tape for securing stuff
  13. Some wool or cotton to fill the empty spaces so no wires rattle around.

When printing the front plate make sure to change the settings for the speaker grille part to not print any bottom or top layers, use hexagon/honeycomb infill and use about 40% infill strength. You can remove the speaker grille part if you want to have the speaker open like you can see in the rendered graphic of the assembly.

After printing

  1. Remove the supports from the holding brackets of the speaker driver. There should be thin walled supports that have been designed into the part.
  2. Melt the inserts into the brace holes and the front plate.
  3. Glue the front and backplates into their bezels
  4. Find a piece of copper wire that fits into the holes in the hinge. The front and back bezels both have a small hole through which you can insert the wire through the hinge part. If assembled and the hinge is freely rotating you can use a small screw driver to bend the wire in the hole to avoid it falling out. Alternatively insert a dab of glue to close the hole.
  5. The SD card slot and USB-C port probably need some cleanup and filing to fit perfectly.


The electronics in this devices are meant to be modular, so developer boards for all components were used and the wiring connections mostly have dupont connectors to be able to remove/replace a component without de-soldering anything. You can of course skip the connectors and solder everything together, that's your choice.


SD-Card reader:

SD-Card reader Mod

Get a reader that has the Card slot as close to the edge as possible, if it is not at the edge you can usually file away some of the FR4 board to make it fit. (Usually there are no traces there to destroy...).

Most SD-Card readers you can get on the market are designed to be used with 5V electronics and contain a linear regulator (red X) to step down that voltage to 3.3V. As the ESP32 works on 3.3V already you'll have to remove that LDO because the minimal voltage it needs to run is usually 4.5V. When removing the part either use hot air or just a soldering iron and cook that thing, as we won't be needing it you do not have to save it. After removal just bridge input and output of the part (marked in green).


Schematic preview

Wired up

Make sure to connect the Amplifier directly to the battery and not the ESP32. You will crash the ESP with loud music as the regulator on the PCB will not deliver enough power!

For the button pullup resistors I soldered them in-line with a wire and applied heatshrink over them (greenish heatshrink in the photo).

The development boards are glued with double sided tape and if needed you can apply the braces on top and stuff them with some foam tape or something rubbery to hold the boards down firmly.

The Speaker will slide into the printed rails but it will rattle around, you need some foam tape or foam rubber strips to wedge the speaker into.

Anti rattle stuffing


The firmware is in the Github repository and is to be built with platform.io

The Arduino framework is used and these external libraries:

Just open this repository in a Platform.io IDE of your choice and click "Build and Upload"

Stuff you may want to change

SD-Card read speed

If you get errors on the serial debug console output about the SD-Card not working, you may have a slow SD-Card, go to src/main.cpp around line 35 and change the following line:

#define SPI_SPEED 4000000u

40 MHz is the default and the maximum speed the ESP32 can handle. Try reducing to 20MHz or 15 MHz until the SD-Card works. If you go too slow sound will be crackling and boot times will increase.

Equalizer settings

As the EQ settings depend on the materials you printed the enclosure, print settings and the used speaker driver you probably want to play with those settings a bit. You can find them in src/main.cpp at around line 150.

Search for these lines:

  eq = new AudioOutputFilter3BandEQ(output, 500, 5000);
  eq->setBandGains(1.5, 0.9, 1.3);

The first line sets the cutoff frequency of the EQ bands, so in this example we have 3 Bands:

Those values are approximate as the algorithm implemented for the EQ is very simple and not accurate at all, but as we're dealing with very simple and cheap components here it is usually ok to use that.

The second line sets the equalizer band gains. If you set them all to 1.0 you should hear the original sound. I recommend to use values in the range of 0.75 to 1.5 . If you go under or over those the sound may distort or clip, I warned you!


To properly function the speaker needs an SD-Card to store voice prompts and configuration. I personally tried with 16GB SD-Cards and it worked fine (those were the smallest cards i could get from my supplier), I think that is enough space for this project as two full days of music are just under 4GB and if you put too many directories on there the boot time of the device will take ages as it needs to build an index of the contents on boot up.

To prepare the SD-Card for the speaker:

  1. Format the SD-Card to FAT32
  2. Put all files from the sd-card folder directly onto the SD-Card

SD-Card contents

This is a quick how-to for the SD-Card contents of LittleSpeaker

root files

We have some files that reside on the root directory (Top level, under windows this is directly on the drive in no folder).

If the connection to the WiFi does not work check the following:

  1. WiFi has to be on the 2.4GHz band, 5GHz does not work
  2. WiFi has to support WPA2
  3. If nothing else is the problem, check that you're using "Unix line endings", this is usually only a problem on Windows machines.

System directory

The system directory should contain the announcer voice files to be used for the menu system, I will describe which one is which in the following.

The default announcer voice has been generated with a text to speech computer voice, but if you want you can exchange them for another voice.

Format of the files should be a stereo MP3 file with a reasonable bitrate and length usable for the task. Remove all ID3 tag data to make the menu more responsive.



There are some things you'll have to consider for the streams that can be played by the firmware of LittleSpeaker:

  1. The stream has to be an IceCast or ShoutCast stream.
  2. You have to link to a stream, not the stream's playlist. Usually webradio stations will link a playlist file (either m3u , m3u8 or pls ) which contains multiple streaming URLs to provide multiple servers.
  3. The stream has to be MP3, AAC and HeAAC (or AAC+) do not work currently.
  4. Keep the bitrate at a reasonable value, 320 kBit is cool and all, but you will probably not hear a difference to a 192 kBit (or even a 128 kBit) stream.
  5. Try to keep to http and not https as it is faster to process on device
  6. Maximum line/URL length is 255 characters
  7. Maximum number of stations is 25


If you want LittleSpeaker to tell you a webradio name instead of the channel number in the menu you can add an audio file to the webradio directory. The file should be a stereo MP3 file with a reasonable bitrate and length. Name it the channel number in two digits (leading zero). The file will be played back if you select the channel.


To verify a Stream URL fetch it with curl like this:

curl -v http://ice2.somafm.com/groovesalad-128-mp3

You will get an output like this:

*   Trying
* Connected to ice2.somafm.com ( port 80
> GET /groovesalad-128-mp3 HTTP/1.1
> Host: ice2.somafm.com
> User-Agent: curl/8.4.0
> Accept: */*
< HTTP/1.1 200 OK
< Content-Type: audio/mpeg
< Date: Fri, 12 Jan 2024 18:42:54 GMT
< icy-br:128
< icy-genre:Ambient Chill
< icy-name:Groove Salad: a nicely chilled plate of ambient beats and grooves. [SomaFM]
< icy-notice1:<BR>This stream requires <a href="http://www.winamp.com/">Winamp</a><BR>
< icy-notice2:SHOUTcast Distributed Network Audio Server/Linux v1.9.5<BR>
< icy-pub:0
< icy-url:http://somafm.com
< Server: Icecast 2.4.0-kh15
< Cache-Control: no-cache, no-store
< Expires: Mon, 26 Jul 1997 05:00:00 GMT
< Connection: Close
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type, Icy-MetaData
< Access-Control-Allow-Methods: GET, OPTIONS, SOURCE, PUT, HEAD, STATS
Warning: Binary output can mess up your terminal. Use "--output -" to tell 
Warning: curl to output it to your terminal anyway, or consider "--output 
Warning: <FILE>" to save to a file.
* Failure writing output to destination
* Closing connection

It is good if curl tells you the content is a binary stream as this means you actually got a stream URL and not the playlist that is linked. If you get back text, it's probably a list of URLs. Sometimes you will get back a redirect which means the service jumps to another server, you will see that if a Location header is in the server response. Try that URL instead.

To verify you got an IceCast stream look for icy- -Headers which tell the device Stream metadata.

You will get back a Content-Type -Header usually. This should be audio/mpeg for streams that are supported. If you get audio/aac or something like that you got an incompatible stream.

Album directories

All directories that are not named system or webradio are assumed to be music albums that can be played back. The Firmware can currently only play back MP3 files, but some MP3 files contain big chunks of ID3 Metatag data and will crash the Firmware. So if you're selecting an album, press play and then hear the announcer voice greeting you with a hello something went wrong. Check if you can remove the metadata from the files to fix that. Also make sure to put stereo MP3 files on the SD-Card, mono files may play back at double speed.

If you want LittleSpeaker to announce your albums put a album.mp3 in each directory that should have a title. If that file is missing you will get an announcer that tells you something like Album thirtyfive when selecting the album on the menu.

If for some reason the album will play back in a wrong order you can put an album.m3u or album.m3u8 into the directory. The firmware will then use the order specified in that playlist. Make sure the playlist only lists the filenames without any directory traversal.