[example] ESPHome & Playing WAV files with a speaker

I don’t know if someone else is interested in playing sounds with ESPHome
TLTR: https://www.youtube.com/watch?v=RI8nUdsZprM

The hardware I’m using is an ESP8266 / D1 mini, a BC338 and a 470ohm resistant. The xtensia arduino plugin is 2.5.2 and ESP8266Audio 1.1.3 with my patch - https://github.com/earlephilhower/ESP8266Audio/pull/224. In addition to my patch I had to comment-out AudioFileSourceSD in the .h and .cpp file.
Until now I didn’t manage to play a mp3 - not enough free ram …
My hint: disconnect the speaker while flashing the esp :wink:

esphome:
  name: $devicename
  platform: ESP8266
  board: nodemcuv2
  arduino_version: 2.5.2
  libraries:
    - esp8266Audio
  includes:
    - EsphomeAudio.h

switch:
  - platform: custom
    lambda: |-
      auto esphomeAudio = new EsphomeAudio();
      App.register_component(esphomeAudio);
      return {esphomeAudio};
    switches:
      name: "Play Sound"

EsphomeAudio.h:

#include "esphome.h"

#include <alloca.h>

#include <AudioFileSourceSPIFFS.h>
#include "AudioFileSourceHTTPStream.h"
#include "AudioGeneratorWAV.h"
#include "AudioOutputI2SNoDAC.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGenerator.h"

static const char *TAG = "audio";

#define DEBUGf(format, ...) Serial.printf(format "\n", ##__VA_ARGS__)

/**
 * im cbData muss das TAG stehn - vom regster
 */
void statusCBFn(void *cbData, int code, const char *message) {
        // PROGMEM vs RAM
        if (message >= (const char *)0x40000000) {
                char *s=(char *) alloca(strlen_P(message));
                strcpy_P(s, message);
                message=s;
        }
        DEBUGf("'%s': code:%d statusCBFn: %s", (char *) cbData, code, message);
}

// == RX
#define I2SO_DATA 3

class EsphomeAudio : public Component, public Switch {
public:
        EsphomeAudio() : wav(NULL), file(NULL), out(NULL), buff(NULL) {}
        void setup() override {
                pinMode(I2SO_DATA, OUTPUT);
                digitalWrite(I2SO_DATA,false);
        }
        void loop() override {
                if (this->wav && this->wav->isRunning()) {
                        if (!this->wav->loop()) {
                                this->wav->stop();
                                delete(this->wav); this->wav=NULL;
                                delete(this->buff); this->buff=NULL;
                                delete(this->out); this->out=NULL;
                                delete(this->file); this->file=NULL;
                                pinMode(I2SO_DATA, OUTPUT);
                                digitalWrite(I2SO_DATA,false);
                                this->publish_state(false);
                        }
                } 

        }

        void write_state(bool state) override {
                if(state && !this->file) {
                        DEBUGf("%s: init i2s %s", this->get_name().c_str(), state ? "ON" : "OFF");
                        this->file = new AudioFileSourceHTTPStream("http://home-assistant/old-car-engine_daniel_simion.wav");
                        this->file->RegisterStatusCB(statusCBFn, (void *) "AudioFileSourceHTTPStream");
                        // Create a buffer using that stream
                        this->buff = new AudioFileSourceBuffer(this->file, 2048);
                        this->buff->RegisterStatusCB(statusCBFn, (void *) "AudioFileSourceBuffer");
                        this->out = new AudioOutputI2SNoDAC();
                        this->out->RegisterStatusCB(statusCBFn, (void *) "AudioOutputI2SNoDAC");

                        this->wav = new AudioGeneratorWAV();
                        this->wav->RegisterStatusCB(statusCBFn, (void *) "AudioGeneratorWAV");

                        uint32_t free = ESP.getFreeHeap();

                        DEBUGf("%s:wav->begin, free=%d", this->get_name().c_str(), free);
                        if(this->wav->begin(this->buff, this->out)) {
                                DEBUGf("%s: started playing", this->get_name().c_str());
                        } else {
                                DEBUGf("%s: failed to play", this->get_name().c_str());
                        }
                        this->publish_state(state);
                }
        }

        ~EsphomeAudio() {
                DEBUGf("%s:~EsphomeAudio", this->get_name().c_str());
                if(this->file)
                        this->file->RegisterStatusCB(NULL, NULL);

        }
private:
        AudioGeneratorWAV *wav;
        AudioFileSourceHTTPStream *file;
        AudioOutputI2SNoDAC *out;
        AudioFileSourceBuffer *buff;

};
5 Likes

Hi mind sharing the wiring diagram?

The one-transistor wiring diagram is described here, however this is sufficent for testing only.

For a real usage I would use a PAM8403. Doesn’t cost too much :wink:
Of course using a MAX98357A would improve the audio quality. However I don’t have one and you would have to adapt the code.

Is it possible to add multiple files with selection 1, 2, 3… or just one file per esp?

I didn’t try it yet, but it seems to be possible: https://esphome.io/components/sensor/custom.html#bonus-sensors-with-multiple-output-values
So, class EsphomeAudio should not be derived from Switch but implement own objects.

This is a very interesting share that you have made here , thank you !
it seems the situation doesn’t evolve too much but audio on ESPhome has a lot of sense !

Do you still recommend it this way ? I’m going to try it with an ESP32.

I also like this approch : https://gitlab.com/MrDIYca/mrdiy-audio-notifier
May be it could be converted into an ESPHome library too ? (EDIT : after looking at the code it is finally also based on ESP8266Audio library. The options like TSS and tone still interesting :slight_smile:

Thank you.

2 Likes

I didn’t update the device for a while, so maybe the code has to be slightly adopted.

I am very interested by a full native integration of I2S in ESPhome … especially with the max98357A amplifier…

1 Like

I am more interested into I2S for reading from microphones. And thought about working in that to make it more general usable in ESPHome.

But well there currently is no infrastructure for audio at all. We would need input, ouput and processing things. My first goal would be to read from an I2S mic, and have a sensor thats uses audio input and generates a loudness value.

I will start with just a custom sensor. Maybe sometimes later we can get more things together as native parts of ESPHome.

has anybody manage to integrade it to esp home finally?

You can use a DF-player.
DF-Player mini — ESPHome

Hi, i have flash esphome to esp8266 connected with a MAX98357 I2S. I can paly files located to sdcard of rpi through mqtt

switch:

i have of install apache server in home assistant.All great until here.
The thing i want to do is to play files from sdcard of my rpi but not through mqtt, i want to play through my local network. I have samba share and i can play files with google nest mini from my local network. But i have not find any way to play files with MAX98357 and esphome from loacl netwotk. I hope you unterstand what i am saying …

1 Like

nice @alfadex !!!

can you share you YAML code and custom_component please?

and where do you put your 2.mp3 please to access with “http://192.168.1.xxx/2.mp3” i cant manage to access any mp3 file on home assistant Os…

thanks

Hi, there is not yaml code. Everything is done from mqtt. xxx is your local ip from rpi. when you 'll install apache and you run http://192.168.1.xxx/2.mp3 , the mp3 file wil play. You have to install firmware to your esp8266

1 Like

thanks i will try

Hey, great idea, thanks for sharing! Is it possible to save the Audi file on the esp32 itself?

I can’t figure out what the problem is. MAX98357 Works great with esphome and I2S Audio Media Player

short video example