ESP8266Audio & AdafruitSSD1306 Lib: Writing to an I2C OLED Causes I2S Communication to Halt

Welcome.

I am working on an internet radio project using a NodeMCU breakout board w/ ESP-12. I have some user input functionality working along with the I2S audio stream accomplished by the ESP8266Audio library here. When I switched over to writing code for the display I chose, I hit a small road-block. The display is an I2C monochrome OLED with SSD1306 driver. I chose an I2C display to use minimum GPIO on the NodeMCU. I am using the AdafruitSSD1306 library for portability/simplicity. Most of what I am doing is static text and only in certain places the text must be updated. Graphics would be a plus for the future but not expecting anything crazy (maybe a loading animation or something).

I am running into an issue where when I call display.display() to update the text on the screen (in response to a user input; a station change or volume increment), the I2S communication briefly cuts out. Here is a video that better demonstrates the issue I am describing. At the midpoint of the video you can really hear the choppiness of the Audio.

I've had issues in the past with calls to display() on my display object wayyy slowing down execution in my programs. The most pertinent info I've found on this is the following:

...the SSD1306 module has no onboard buffer for the screen. The buffer is simply 128 x 64 pixels and it is an area of memory that resides on the Arduino module.

When you want to display something on the screen, first you modify your own local buffer, then you send the whole buffer, all the screen bits, rather than sending just the bits that have changed.

I understand that the I2C protocol is inherently sluggish, could it somehow be affecting my I2S data? I'm not all too sure what is being realized here, whether it is some sort of blocking in the code, or bus contention. I'd love to give some sort of visual +/- indicator and also update the display according to the current station, volume, etc. without the audio cutting out.

Here is the code I am using to change the volume and update the display. It uses a boolean flag set by an external interrupt to determine whether or not to jump into the appropriate if block during the loop() execution.

void loop() {
  static int lastms = 0;

  // MP3 Stream is running...
  if (mp3->isRunning()) {
    if (VOL_UP_FLAG || VOL_DN_FLAG) {
      Serial.print("Volume: ");
      Serial.println(volume);
      out->SetGain(volume);
      // update dipaly
      updateDisplayVolume();
      VOL_UP_FLAG = false;
      VOL_DN_FLAG = VOL_UP_FLAG;
    }
    if (CHANGE_STATION_FLAG) {
      Serial.println("Changing station URL...");
      if (station < NUM_STATIONS) station++;
      else station = 0;
      newStation(URL_ARRAY[station], true);
      Serial.println("Done.");
      //Update display
      updateDisplayStationInfo();
      CHANGE_STATION_FLAG = false; 
    }
    // TO DO: Implement heartbeat/other flash codes using ESP8266 timer interrupt library
    if (millis()-lastms > 5*INTERVAL) {
      cnt++;
      lastms = millis();
      if (cnt % 2 == 0) GPOS = (1 << 13);
      else GPOC = (1 << 13);
      Serial.printf("Running for %d ms...\n", lastms);
      Serial.flush();
    }
    if (!mp3->loop()) {
      mp3->stop();
      GPOC = (1 << 13);
    }
  } 

And the code to write new text to the display (the setTextColor swap along with the printing of the blank spaces is meant to erase the previous entry):

void updateDisplayStationInfo() {
  display.setTextColor(WHITE, BLACK);
  display.setCursor(stationNameUpdatePosX, stationNameUpdatePosY);
  display.print(F("               "));
  display.setCursor(stationNameUpdatePosX, stationNameUpdatePosY);
  display.print(stationName[station]);
  display.display();
  //Not yet implemented
  //display.setCursor(stationURLUpdatePosX, stationURLUpdatePosY);
}

void updateDisplayVolume() {
  display.setTextColor(WHITE, BLACK);
  /*
  Not yet implemented
  display.setCursor(volIndicatorPosX, volIndicatorPosY);
  display.print(" ");*/
  display.setCursor(volUpdatePosX, volUpdatePosY);
  display.print(F("   "));
  display.setCursor(volUpdatePosX, volUpdatePosY);
  display.print(volume);
  display.display();
}

The audio cutting out is not a problem when the display doesn't need to be refreshed during the audio stream (if I simply comment out the calls to display() the issue disappears). Should I look to an alternative display driver library? Apart from that, an idea is to swap to an ESP32 and use hardware SPI since it is faster. But I don't really know if that will solve my problem and I really would like to stick with the NodeMCU module. Maybe I could cleverly update the display when more opportune for the I2S stream?

TLDR; (on ESP8266) I2S cuts out with calls to display.display()

Post complete code. The interesting code is not in that part you posted.

Calling display() may need more than 100ms to finish. I guess your MP3 player is simply starving of not getting enough data during that time. But that's a lot of guessing without having seen the complete code.

Hey there, thanks for taking the time to read and respond. Here is the complete code in its' current form:

/* ============================================================================================
 * To run, set your ESP8266 build to 160MHz, update the SSID info, and upload.
 * 
 * The pins for I2S on ESP8266 using the ESP8266Audio lib are:
 * LRC - D4 (GPIO2)
 * BCLK - D8 (GPIO15)
 * DIN - RX (GPIO3, RXD0)
 * 
 * The extra pins on the MAX98357A board are:
 * SD - taken care of by adafruit breakout board HW; mode set to stereo average with 5V VCC
 * Gain - Tie to 100k resistor to 5V VCC for 3dB gain... default is 9dB
 * Vin - tie to 5V VCC
 * GND - have a guess
 * 
 * 
 * // Impolement eeprom to save last channel and volume???
 *   // add tft stuff for rolling display of station?
  // animated icon to indicate playing?
  // metadata(ICY) contains stream information
  metadata not always populated! just hard code the names and whatnot and use metadata when avail
  add indicator LEDs

  // grab time from internet...
  connecting to two webpages at once or quickly retriving data from a page?
  //In the past I have used a circuit to detect that power is dropping out (have to do that before the regulator) and do your write only then...
  //rtry same for rst
  save data to eeprom in a stuct!!!!


  Switch to a lighter display lib???
  sometimes the volume wont go down to zero

  All the stations that I used in the url list is 64kbps rate for light stream buffer.

The esp8266 have very small buffer space so for my experience its not able to stream flowently above 96kbps and its also depends on your Internet bandwidth and traffic And the server that's transmit the stream some of them weeker and some of them stronger.

The internal DAC of the esp8266 is a 10 bit DAC so don't expect great sound from this DAC.


/*
 * 
 * 
 * Important Notes about ISR
Inside the attached function, delay() won’t work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function.

Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile.
 * 
 * ============================================================================================ */
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "AudioFileSourceHTTPStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2S.h"
#include <Wire.h>              // For I2C on GPIO
#include <Adafruit_GFX.h>      // Core graphics library (!not being used currently)
#include <Adafruit_SSD1306.h>  // I2C OLED display driver library (Hardware-specific library)

// WiFi setup
// To include multiple, define an enum type?
#ifndef STASSID
#define STASSID "SSID"
#define STAPSK  "PASSWORD"
#endif

#define NUM_STATIONS 11

#define VOL_UP_PIN D3
#define VOL_DN_PIN D6
#define VOL_MAX 2.00
#define VOL_MIN 0.05     
#define VOL_INCR 0.05

#define INTERVAL 100

#define CHANGE_STATION_PIN D5
#define STATUS_LED D7
#define SCL_PIN D1
#define SDA_PIN D2

// Declaration for SSD1306 OLED display. The pins for I2C are defined by the Wire-library. 
#define OLED_RESET       -1   // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C   // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define SCREEN_WIDTH    128   // OLED display width, in pixels
#define SCREEN_HEIGHT    64   // OLED display height, in pixels

double volume = VOL_MIN + VOL_INCR;
double fuzz = 0.000001;
bool VOL_UP_FLAG = false;
bool VOL_DN_FLAG = false;
bool CHANGE_STATION_FLAG = false;

const char* ssid = STASSID;
const char* password = STAPSK;

unsigned int station = 0;
unsigned int cnt = 0;

// These are initialized with radioDisplayInit(), cuz im lazy and couldnt bother finding the exact pixel nums
unsigned int sigPosX;
unsigned int sigPosY;
unsigned int stationNameUpdatePosX;
unsigned int stationNameUpdatePosY;
unsigned int stationURLUpdatePosX;
unsigned int stationURLUpdatePosY;
unsigned int volUpdatePosX;
unsigned int volUpdatePosY;
unsigned int volIndicatorPosX;
unsigned int volIndicatorPosY;

//URL'S
const char* URL_ARRAY[NUM_STATIONS] = {"http://a1rj.streams.com.br:7801/sm",
                                       "http://jazz.streamr.ru/jazz-64.mp3",
                                       "http://www.golden-apple.com:680/;",
                                       "http://stm14.mfmedios.info:8048/;",
                                       "http://cast2.servcast.net:3020/;",
                                       "http://live02.rfi.fr/rfimonde-64.mp3",
                                       "http://live.wbcb1490.com:88/broadwavehigh.mp3",
                                       "http://14543.live.streamtheworld.com:3690/XHFO_FM_SC",
                                       "http://14523.live.streamtheworld.com:3690/KNBAFM_SC",
                                       "http://sa.mp3.icecast.magma.edge-access.net:7200/sc_rad31",
                                       "http://stream.lt8.com.ar:8080/delsiglo995.mp3"
                                       };

//URL'S Names
const char* stationName[NUM_STATIONS] = {"ALJ",
                                         "Jazz RU",
                                         "Golden Apple",
                                         "Mfmedios",
                                         "Servcast",
                                         "RFI - Monde",
                                         "WBCB UK",
                                         "XHFO FM",
                                         "KNBA FM",
                                         "Radio Nacional",
                                         "Del Siglo"
                                         };

AudioGeneratorMP3 *mp3;
AudioFileSourceHTTPStream *file;
AudioFileSourceBuffer *buff;
AudioOutputI2S *out;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Construct a display object

void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string);
void StatusCallback(void *cbData, int code, const char *string);
void ICACHE_RAM_ATTR volUp();
void ICACHE_RAM_ATTR volDown();
void ICACHE_RAM_ATTR changeStation();
void newStation(const char* URL);
void radioDisplayInit();              // pass eeprom read values in the future
void updateDisplayStationInfo();
void updateDisplayVolume();

void setup() {
  Serial.begin(115200);
  delay(INTERVAL);
  
  pinMode(STATUS_LED, OUTPUT);
  
  pinMode(VOL_UP_PIN, INPUT);
  pinMode(VOL_DN_PIN, INPUT);
  pinMode(CHANGE_STATION_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(VOL_UP_PIN), volUp, FALLING);
  attachInterrupt(digitalPinToInterrupt(VOL_DN_PIN), volDown, RISING); 
  attachInterrupt(digitalPinToInterrupt(CHANGE_STATION_PIN), changeStation, RISING); 

  GPOC = (1 << 13);

  // Init display for displaying
  Serial.printf("Starting I2C OLED display with addr %x\n", SCREEN_ADDRESS);
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.clearDisplay();
  radioDisplayInit();
  
  Serial.println("Connecting to WiFi");

  WiFi.disconnect();
  WiFi.softAPdisconnect(true);
  WiFi.mode(WIFI_STA);
  
  WiFi.begin(ssid, password);

  // Try forever
  while (WiFi.status() != WL_CONNECTED) {
    cnt++;
    if (cnt % 2 == 0) GPOS = (1 << 13);
    else GPOC = (1 << 13);
    Serial.println("...Connecting to WiFi");
    delay(100);
  }
  Serial.println("Connected");
  GPOC = (1 << 13);

  audioLogger = &Serial;
  newStation(URL_ARRAY[station], false);
}

void loop() {
  static int lastms = 0;

  // MP3 Stream is running...
  if (mp3->isRunning()) {
    if (VOL_UP_FLAG || VOL_DN_FLAG) {
      Serial.print("Volume: ");
      Serial.println(volume);
      out->SetGain(volume);
      // update dipaly
      updateDisplayVolume();
      VOL_UP_FLAG = false;
      VOL_DN_FLAG = VOL_UP_FLAG;
    }
    if (CHANGE_STATION_FLAG) {
      Serial.println("Changing station URL...");
      if (station < NUM_STATIONS) station++;
      else station = 0;
      newStation(URL_ARRAY[station], true);
      Serial.println("Done.");
      //Update display
      updateDisplayStationInfo();
      CHANGE_STATION_FLAG = false; 
    }
    // Implement heartbeat using ESP8266 timer interrupt library
    if (millis()-lastms > 5*INTERVAL) {
      cnt++;
      lastms = millis();
      if (cnt % 2 == 0) GPOS = (1 << 13);
      else GPOC = (1 << 13);
      Serial.printf("Running for %d ms...\n", lastms);
      Serial.flush();
    }
    if (!mp3->loop()) {
      mp3->stop();
      GPOC = (1 << 13);
    }
  } 
  else {
    GPOC = (1 << 13);
    Serial.printf("MP3 done\n");
    Serial.println("Restarting...");
    newStation(URL_ARRAY[station], true);
    //delay(10*INTERVAL);
  }
  if (!WiFi.isConnected()) {
    Serial.printf("Reconnecting...\n.");
    WiFi.reconnect();
    while (WiFi.status() != WL_CONNECTED) {
      cnt++;
      if (cnt % 2 == 0) GPOS = (1 << 13);
      else GPOC = (1 << 13);
      if (cnt % 15 == 0) Serial.print("\n");
      Serial.print(".");
      delay(100);
    }
    Serial.print("Connected.");
    // begin stream again
    newStation(URL_ARRAY[station], true);
  }
}

// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string) {
  const char *ptr = reinterpret_cast<const char *>(cbData);
  (void) isUnicode; // Punt this ball for now
  // Note that the type and string may be in PROGMEM, so copy them to RAM for printf
  char s1[32], s2[64];
  strncpy_P(s1, type, sizeof(s1));
  s1[sizeof(s1)-1]=0;
  strncpy_P(s2, string, sizeof(s2));
  s2[sizeof(s2)-1]=0;
  Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
  Serial.flush();
}

// Called when there's a warning or error (like a buffer underflow or decode hiccup)
void StatusCallback(void *cbData, int code, const char *string) {
  const char *ptr = reinterpret_cast<const char *>(cbData);
  // Note that the string may be in PROGMEM, so copy it to RAM for printf
  char s1[64];
  strncpy_P(s1, string, sizeof(s1));
  s1[sizeof(s1)-1]=0;
  //if (cnt % 2 == 0) GPOS = (1 << 13);
  //else GPOC = (1 << 13);
  //GPOC = (1 << 13);
  Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
  Serial.flush();
}

// ISR for volume up button
void volUp() {
  if (volume < VOL_MAX + fuzz || volume < VOL_MAX - fuzz) {
    volume = volume + VOL_INCR;
    VOL_UP_FLAG = true;
  }  
  else VOL_UP_FLAG = false;
}

// ISR for volume down button
void volDown() {
  if (volume > VOL_MIN + fuzz || volume > VOL_MIN - fuzz) {
    volume = volume - VOL_INCR;
    while (volume  < 0) {
      volume += fuzz;
    }
    VOL_DN_FLAG = true;
  }
  else VOL_DN_FLAG = false;
}

void newStation(const char* URL, bool switchBit) {
  // Predeclarations for file, buff, out, and mp3 pointers
  /*AudioGeneratorMP3 *mp3;
    AudioFileSourceHTTPStream *file;
    AudioFileSourceBuffer *buff;
    AudioOutputI2S *out;*/
  // My attempt at managing the dynamic mem
  if (switchBit) {
    delete file;
    delete buff;
    delete out;
    delete mp3;
  }
  file = new AudioFileSourceHTTPStream(URL);
  file->RegisterMetadataCB(MDCallback, (void*)"ICY");
  buff = new AudioFileSourceBuffer(file, 2048);
  buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
  out = new AudioOutputI2S();
  mp3 = new AudioGeneratorMP3();
  mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
  mp3->begin(buff, out);
  // Added to properly initialize the vol
  out->SetGain(volume);
}

void changeStation() {
  CHANGE_STATION_FLAG = true;
}

void radioDisplayInit() {
  //DISPLAY INIT STUFF
  //display.clearDisplay();
  display.setTextSize(2);           // Prepare font for header text
  display.setTextColor(WHITE);
  //The colors are fixed. The top rows are yellow and the rest white. 
  //You can put whatever text you want at the top but if the font is too big the colors will be split.
  display.setCursor(0, 0);
  display.print(F("ESP.RADIO"));
  display.setTextSize(1);
  sigPosX = display.getCursorX();
  sigPosY = display.getCursorY() + 9;
  display.print(F(" By"));
  display.setCursor(sigPosX, sigPosY);
  display.print(F(" KH"));
  display.drawLine(0, 17, 128, 17, SSD1306_WHITE);
  
  display.setCursor(0, 22);
  display.print(F("Ch: "));
  // Write read-in vals from EEPROM
  stationNameUpdatePosX = display.getCursorX();
  stationNameUpdatePosY = display.getCursorY();
  display.print(stationName[station]);
  display.println();
  display.println();
  
  display.print(F("@: "));
  stationURLUpdatePosX = display.getCursorX();
  stationURLUpdatePosY = display.getCursorY();
  //Implement string length limit and accompanying rolling text
  //display.print(URL_ARRAY[station]);
  display.println();
  display.println();

  display.print(F("Vol: "));
  volUpdatePosX = display.getCursorX();
  volUpdatePosY = display.getCursorY();
  display.print(volume / 2.0);
  display.print(F(" "));
  volIndicatorPosX = display.getCursorX();
  volIndicatorPosY = display.getCursorY();
  display.display();
}

void updateDisplayStationInfo() {
  display.setTextColor(WHITE, BLACK);
  display.setCursor(stationNameUpdatePosX, stationNameUpdatePosY);
  display.print(F("               "));
  display.setCursor(stationNameUpdatePosX, stationNameUpdatePosY);
  display.print(stationName[station]);
  display.display();
  //Not yet implemented
  //display.setCursor(stationURLUpdatePosX, stationURLUpdatePosY);
}

void updateDisplayVolume() {
  display.setTextColor(WHITE, BLACK);
  /*
  Not yet implemented
  display.setCursor(volIndicatorPosX, volIndicatorPosY);
  display.print(" ");*/
  display.setCursor(volUpdatePosX, volUpdatePosY);
  display.print(F("   "));
  display.setCursor(volUpdatePosX, volUpdatePosY);
  display.print(volume);
  display.display();
}

/*
Running for 289680 ms...
15:30:36.982 -> Running for 290681 ms...
15:30:37.949 -> Running for 291682 ms...
15:30:38.272 -> Changing station URL...
15:30:38.474 -> MP3 source file not open
15:30:38.474 -> Done.
15:30:38.474 -> 
15:30:38.474 -> User exception (panic/abort/assert)
15:30:38.514 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
15:30:38.514 -> 
15:30:38.514 -> Abort called
15:30:38.514 -> 
15:30:38.514 -> >>>stack>>>
15:30:38.514 -> 
15:30:38.514 -> ctx: cont
15:30:38.514 -> sp: 3ffefe60 end: 3ffeff10 offset: 0000
15:30:38.514 -> 3ffefe60:  00000001 3fff223c 3fff1974 40209b54  
15:30:38.514 -> 3ffefe70:  000000fe 00000000 00000000 00000000  
15:30:38.514 -> 3ffefe80:  00000000 00000000 00000000 3ffeff78  
15:30:38.514 -> 3ffefe90:  3ffeef10 000000fe 04300860 00000000  
15:30:38.514 -> 3ffefea0:  402094d8 4021c44c 3ffeec54 3ffeff78  
15:30:38.514 -> 3ffefeb0:  3fffdad0 00000020 04300860 40213ac2  
15:30:38.514 -> 3ffefec0:  3ffeed48 00000005 3ffeed48 40213ad4  
15:30:38.554 -> 3ffefed0:  3ffeed48 3ffeec50 3ffeec60 40100efb  
15:30:38.554 -> 3ffefee0:  3fffdad0 00000000 3fff1974 40209aa6  
15:30:38.554 -> 3ffefef0:  3fffdad0 00000000 3ffeff4c 40213400  
15:30:38.554 -> 3ffeff00:  feefeffe feefeffe 3fffdab0 401011b9  
15:30:38.554 -> <<<stack<<<
15:30:38.554 -> 
15:30:38.554 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
15:30:38.554 -> 
15:30:38.554 ->  ets Jan  8 2013,rst cause:2, boot mode:(3,6)
15:30:38.594 -> 
15:30:38.594 -> load 0x4010f000, len 3424, room 16 
15:30:38.594 -> tail 0
15:30:38.594 -> chksum 0x2e
15:30:38.594 -> load 0x3fff20b8, len 40, room 8 
15:30:38.594 -> tail 0
15:30:38.594 -> chksum 0x2b
15:30:38.594 -> csum 0x2b
15:30:38.594 -> v00073160
15:30:38.594 -> ~ld
15:30:39.641 -> Connecting to WiFi
15:30:39.763 -> ...Connecting to WiFi
15:30:40.772 -> ...Connecting to WiFi
15:30:41.749 -> ...Connecting to WiFi
15:30:43.561 -> ...Connecting to WiFi
15:30:44.566 -> Connected
*/

There's a lot I intend to remove, and of course it will eventually look a bit prettier. Since beginning this program I've also learned more about using ISR's especially with the Arduino IDE, so I think I'm missing some volatile/static keywords.

Not sure of what use it may prove to be, but got some scope captures of the I2S stopping during I2C writes. Here the yellow trace is I2C (writing to the display) and the green trace is I2S (triggered by pulse width). You can see that I2S stops entirely, but not immediately after an I2C write is initiated. The signal persists low a short while after the I2C stops.

I don't know if that is true but, if it is, why is it there? That said, it does appear that you only want text, so you might try the libraries more apprpropriate.

#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

The only success I have had with SSD1306 was with these libraries.

Hi Nick, thanks for taking the time to respond.

It looks like I do use the GFX library at the moment to draw a static line but that's all.

Would you mind elaborating on how you've had no luck with the Adafruit lib? What about the ASCII library will change the behavior that is observed?

I understand it has a larger footprint but I am not particularly concerned with memory (or am I)?

The library github page for the one you suggest says that it is "unbuffered". Does that mean changes are sent immediately, and if this is the case, is the entire 128x64 area of memory written each time (like with the Adafruit lib?). Is this not what is slowing down my program?

I am working on some other projects at work and will make use of some of the text only SSD1306 libs to get a feel for them. But I am not convinced that simply changing libraries will solve the problem if it takes the same amount of time to write to the display, buffered or not.

No, you only print what you want where you want it, but I would be surprised if the library you are using lumbers you with "the entire 128x64 area of memory written each time ". I have never known any to do that. I'm afraid I don't know anything about buffering, nor ever had the need to know, and I can't remember what the grief I was having with the SSD1306 was all about.

Gotcha, thanks. I'm not sure I know much about it myself. Oof. Thanks again for your input! I am messing about with the U8G2 library today at work.

An update:

I switched over to the ESP8266 SSD1306 OLED library since it boasted the ability to use the brzo I2C library for faster I2C (at 1MHZ for 160MHz esp12 board), the lib is written in assembly. I had some trouble getting the latest update of brzo (v1.3.3) to work (issue #44 on the gihub repo), downgrading to 1.3.0 allowed the code to compile. It seems that this approach to writing the OLED does not introduce any noticeable skips into the audio. Although the SSD1306 is out of spec at 1MHz.

From pg 49 of the datasheet
image_2023-05-27_220518132

My other idea was to use u8x8 lib in bufferless mode but I think I'll stick with whats working. I'm not sure if its simply the faster clock speed, but I also made it a point to do very little before having to call display.display() during streaming.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.