├── LICENSE ├── README.md └── esp8266-audio-output.ino /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ahans 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266 Audio Output 2 | 3 | This is a simple Arduino sketch demonstrating how to produce analog audio 4 | output using the ESP8266's I2S interface. 5 | 6 | Large portions of this code are taken from 7 | https://janostman.wordpress.com/audio-hacking-with-the-esp8266/. 8 | 9 | To run this, open the project in the Arduino IDE, compile and upload it to 10 | your ESP8266. Also, provide at least one WAV/PCM file in the data folder and 11 | use something like Tools/ESP8266 Sketch Data Upload to get it into the 12 | ESP8266's SPIFFS. 13 | 14 | Files can be converted using ``sox``: 15 | 16 | sox in.mp3 -b 16 out.wav channels 1 rate 32000 trim 0 =30 17 | 18 | This would take the first 30 seconds of ``in.mp3`` and convert it to 16-bit 19 | mono 32 kHz data in ``out.wav``. 20 | 21 | The sampling frequency can be adjusted in the code, it is currently set to 32 22 | kHz, so the above ``sox`` command would produce a file that should work. Note 23 | that on my ESP8266 it seems that playback is a bit slower than the given rate. 24 | Increasing the rate slightly, for instance setting it to 32080 Hz for a 32 kHz 25 | file, seems to fix that. However, I'm pretty sure that's not the proper fix 26 | for this and something else is still wrong here ... -------------------------------------------------------------------------------- /esp8266-audio-output.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | int16_t buffer[512]; 8 | 9 | void setup(void) { 10 | Serial.begin(9600); 11 | Serial.println(""); 12 | Serial.println("putting WiFi to sleep"); 13 | WiFi.forceSleepBegin(); // turn off ESP8266 RF 14 | delay(1); // give RF section time to shutdown 15 | 16 | Serial.println("Starting SPIFFS"); 17 | SPIFFS.begin(); 18 | 19 | Serial.println("Setup finished"); 20 | } 21 | 22 | // Taken from https://github.com/espressif/ESP8266_MP3_DECODER/blob/master/mp3/user/user_main.c 23 | // 2nd order delta-sigma DAC 24 | // See http://www.beis.de/Elektronik/DeltaSigma/DeltaSigma.html for a nice explanation 25 | int sampToI2sDeltaSigma(short s) { 26 | int x; 27 | int val = 0; 28 | int w; 29 | static int i1v = 0, i2v = 0; 30 | static int outReg = 0; 31 | for (x = 0; x < 32; x++) { 32 | val <<= 1; //next bit 33 | w = s; 34 | if (outReg > 0) w -= 32767; else w += 32767; //Difference 1 35 | w += i1v; i1v = w; //Integrator 1 36 | if (outReg > 0) w -= 32767; else w += 32767; //Difference 2 37 | w += i2v; i2v = w; //Integrator 2 38 | outReg = w; //register 39 | if (w > 0) val |= 1; //comparator 40 | } 41 | return val; 42 | } 43 | 44 | int rate = 32000; 45 | 46 | void play() { 47 | Serial.println(F("Playing hill32.wav")); 48 | File f = SPIFFS.open("/hill32.wav", "r"); 49 | if (!f) { 50 | Serial.println("file open failed"); 51 | return; 52 | } //- See more at: http://www.esp8266.com/viewtopic.php?f=29&t=8194#sthash.u5P6kDr6.ycI23aTr.dpuf 53 | 54 | i2s_begin(); 55 | i2s_set_rate(rate); 56 | 57 | while (f.position() < (f.size()-1)) { 58 | int numBytes = _min(sizeof(buffer), f.size() - f.position() - 1); 59 | f.readBytes((char*)buffer, numBytes); 60 | for (int i = 0; i < numBytes / 2; i++) { 61 | i2s_write_sample(sampToI2sDeltaSigma(buffer[i])); 62 | } 63 | } 64 | 65 | f.close(); 66 | i2s_end(); 67 | } 68 | 69 | void loop() { 70 | play(); 71 | #if 0 72 | while (Serial.available() > 0) { 73 | char in = Serial.read(); 74 | Serial.print("read "); 75 | Serial.println(in); 76 | switch(in) { 77 | case '+': 78 | rate += 5; 79 | break; 80 | case '-': 81 | rate -= 5; 82 | break; 83 | case 'p': 84 | play(); 85 | break; 86 | } 87 | Serial.print("rate = "); 88 | Serial.println(rate); 89 | } 90 | #endif 91 | } 92 | --------------------------------------------------------------------------------