ESP32 & SSD1322 - 4 Wire SPI vs 8080 Parallel

Hi guys, can anyone educate me on SPI 4 Wire & 8080 SPI for talking to OLED Displays?

I ordered 4 @ 256 x 64 OLED Displays ( Link Here ) from China, and asked for 4 Wire SPI as this is how the Tutorial i was following is configured, but the displays have arrived configured for 8080 Parallel, so i am trying to decide if i should re-solder the board, send them back (painful) or try to use them how they are configured.

I am using a ESP32 and the OLED controller is a SSD1322.

This is what i am working on copying:

Im not educated enough to know if how the displays are configured at the moment is suitable for the project, if it is slower than 4 Wire SPI, etc.

Any advice would be greatly appreciated.

Thanks

Alex

First off. Does the item on your desk match the photos in your link ? Especially the pcb view.

Your video shows an I2C SSD1306 / SH1106 display. i.e. pins VCC, GND, SDA, SCL

Your link shows a 256x64 SSD1322 SPI/Parallel display.

I suggest that you buy 128x64 I2C display like the ones in the video. They are very cheap.

Yes, the 256x64 OLED in your link looks very nice. There is not much support for SSD1322 yet. U8g2lib seems to support every hardware mode for your display. SPI uses less wires.
It should work just fine with your ESP32. Just change the links to suit 4-wire SPI.

Ask if you are unsure.

David.

Hi David,

Thanks for taking the time to reply.

Yes, apologies - i have not explained my goals very well. The video i have linked to is what i am basing my project on, this OLED is to go into 4 Bang & Olufsen speakers i have, the display is the Perfect Size to replace the redundant one that is already there.

My plan was to modify the code to include more frequencies (as stated is possible using the ESP32 by the developer) and distribute them across the OLEDs that i have.

I had ordered the 4 OLEDs to be 4 Wire SPI, however they have arrived as 8080.

This is probably out of my depth currently but i think i can achieve what i want, with what i have?

Here is the rear of my OLED display:

I am pretty sure it is configured for 8080, as long as i can achieve my goals using what i have - then i am happy to learn how to do it (sounds fun to be honest).

In my head i figured i needed to:

  • Figure out which libraries i need to utilize to interface with the OLED (U8g2lib as you previously mentioned?)
  • Study the code from the Audio Spectrum project and modify it so that it uses the correct functions from the U8g2lib to display whats needed.

If the only difference in using 8080 is needing more wiring then i am happy to go with it.

Thanks

Alex

GRRR. I just wrote a longish reply and the Forum "signed me out"

Yes your display is set for 8080 parallel.

Connect all the wires. Add the pins to the constructor:

U8G2_SSD1322_NHD_256X64_F_8080(rotation, d0, d1, d2, d3, d4, d5, d6, d7, enable, cs, dc [, reset]) full framebuffer, size = 2048 bytes

Run all the U8g2lib examples.

If you have a soldering iron, change R5, R6 for 4SPI.
Use the software SPI constructor:

U8G2_SSD1322_NHD_256X64_F_4W_SW_SPI(rotation, clock, data, cs, dc [, reset]) full framebuffer, size = 2048 bytes

Then try the hardware SPI constructor.

There is no point in trying to write your own code until you have run library examples and verified your hardware.

David.

Doh thats annoying!!

Ok brilliant, thanks for the clarification, and so there is no real drawback or benefit to using 8080 vs SPI? Other than additional wiring?

Thanks Again

Alex

Hi David,

I finally had a chance to visit this tonight, i reconfigured the display for 4 Wire SPI. I simply tried to power up the board from ESP32 using the 3.3v & Gnd but it remained dead. Im not sure how this is supposed to work but i was expecting maybe a backlight to come on upon doing this, maybe i have damaged the display in doing this?

I loaded up the Hello World Ug8 example project and followed your instruction, however i have no idea if i am assigning the pins correctly. I used the numbers that were on my board physically here but when i search google for a proper pinout i get many many different variations of pin positions.

The display remains off / not working.

Any help would be greatly appreciated. Feel like i have wasted 4 hours and am not even slightly more educated than i was previously.

Thanks

Alex

Post a photo of your soldering of the configuration links.
Life is simpler with HWSPI. Less wires.

David.

/*
LCD  ESP32
1    0V
2    3.3V
4    GPIO18  (SCLK)
5    GPIO23  (SDIN)
14   GPIO13  (DC)
15   GPIO12  (RST)
16   GPIO5   (CS)
*/

// full framebuffer, size = 2048 bytes
//U8G2_SSD1322_NHD_256X64_F_4W_SW_SPI(rotation, clock, data, cs, dc [, reset])
U8G2_SSD1322_NHD_256X64_F_4W_SW_SPI(U8G2_R0   ,    18,   23,  5, 13,      12 );

Hi David,

Thanks Again for your help, i have success! Seems i was missing the:

LCD ESP32
4 GPIO18 (SCLK)
5 GPIO23 (SDIN)

Could not seem to figure this out based on the info being given on the AliExpress Product Page.

Now i need to try and adapt the ESP32 Spectrum Display Code, to work with this Display, looks like the Library he is using to drive the display is not from the U8G2 selection, his instructions are starting with "display."

I will try and work through this, thanks again for your help its greatly appreciated.

Alex

As always, run all the library examples.

You will understand what is possible and become familiar with how the U8g2lib library works.

I have no idea what your ESP32 Spectrum Display Code does or what it looks like.
But it should be straightforward to adapt for different libraries.

David.

Hi David,

Yes i am slowly going through these trying to soak in as much as i can.

This is the code im going to attempt to use:

#include <U8g2lib.h>
#include <U8x8lib.h>

/* ESP8266/32 Audio Spectrum Analyser on an SSD1306/SH1106 Display
 * The MIT License (MIT) Copyright (c) 2017 by David Bird. 
 * The formulation and display of an AUdio Spectrum using an ESp8266 or ESP32 and SSD1306 or SH1106 OLED Display using a Fast Fourier Transform
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
 * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
 * publish, distribute, but not to use it commercially for profit making or to sub-license and/or to sell copies of the Software or to 
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:  
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 * See more at http://dsbird.org.uk 
*/

#include <Wire.h>
#include "arduinoFFT.h" // Standard Arduino FFT library 
// https://github.com/kosme/arduinoFFT, in IDE, Sketch, Include Library, Manage Library, then search for FFT
arduinoFFT FFT = arduinoFFT();

U8G2_SSD1322_NHD_256X64_F_4W_SW_SPI u8g2(U8G2_R2, 18, 23, 5, 13, 12);

/////////////////////////////////////////////////////////////////////////
#define SAMPLES 512              // Must be a power of 2
#define SAMPLING_FREQUENCY 40000 // Hz, must be 40000 or less due to ADC conversion time. Determines maximum frequency that can be analysed by the FFT Fmax=sampleF/2.
#define amplitude 200            // Depending on your audio source level, you may need to increase this value
unsigned int sampling_period_us;
unsigned long microseconds;
byte peak[] = {0,0,0,0,0,0,0};
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;
/////////////////////////////////////////////////////////////////////////
void setup() {
  Serial.begin(115200);
  Wire.begin(5,4); // SDA, SCL
  display.init();
  display.setFont(ArialMT_Plain_10);
  display.flipScreenVertically(); // Adjust to suit or remove
  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
}

void loop() {
  display.clear();
  display.drawString(0,0,"0.1 0.2 0.5 1K  2K  4K  8K");
  for (int i = 0; i < SAMPLES; i++) {
    newTime = micros()-oldTime;
    oldTime = newTime;
    vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
    vImag[i] = 0;
    while (micros() < (newTime + sampling_period_us)) { /* do nothing to wait */ }
  }
  FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
  for (int i = 2; i < (SAMPLES/2); i++){ // Don't use sample 0 and only first SAMPLES/2 are usable. Each array eleement represents a frequency and its value the amplitude.
    if (vReal[i] > 2000) { // Add a crude noise filter, 10 x amplitude or more
      if (i<=2 )             displayBand(0,(int)vReal[i]/amplitude); // 125Hz
      if (i >3   && i<=5 )   displayBand(1,(int)vReal[i]/amplitude); // 250Hz
      if (i >5   && i<=7 )   displayBand(2,(int)vReal[i]/amplitude); // 500Hz
      if (i >7   && i<=15 )  displayBand(3,(int)vReal[i]/amplitude); // 1000Hz
      if (i >15  && i<=30 )  displayBand(4,(int)vReal[i]/amplitude); // 2000Hz
      if (i >30  && i<=53 )  displayBand(5,(int)vReal[i]/amplitude); // 4000Hz
      if (i >53  && i<=200 ) displayBand(6,(int)vReal[i]/amplitude); // 8000Hz
      if (i >200           ) displayBand(7,(int)vReal[i]/amplitude); // 16000Hz
      //Serial.println(i);
    }
    for (byte band = 0; band <= 6; band++) display.drawHorizontalLine(18*band,64-peak[band],14);
  }
  if (millis()%4 == 0) {for (byte band = 0; band <= 6; band++) {if (peak[band] > 0) peak[band] -= 1;}} // Decay the peak
  display.display();
}

void displayBand(int band, int dsize){
  int dmax = 50;
  if (dsize > dmax) dsize = dmax;
  if (band == 7) display.drawHorizontalLine(18*6,0, 14);
  for (int s = 0; s <= dsize; s=s+2){display.drawHorizontalLine(18*band,64-s, 14);}
  if (dsize > peak[band]) {peak[band] = dsize;}
}

Thanks

Alex

I would regard "Adafruit_SSD1306" and "U8g2lib" as being the two standard libraries.

I would be 99% confident that U8g2lib will work on your ESP32

Your project is using an unusual library : GitHub - ThingPulse/esp8266-oled-ssd1306: Driver for the SSD1306 and SH1106 based 128x64, 128x32, 64x48 pixel OLED display running on ESP8266/ESP32

But since there are very few OLED commands, you just have to compare docs and change:

change constructor to U8g2lib style

and calls:

init() to initDisplay()
setFont() to a valid U8g2 font
flipScreenVertically() to setFlipMode()
drawHorizontalLine() to drawHLine()

Personally, I would port to SSD1306 first. You can verify behaviour against the video.
Then you replace constructor to suit your hardware.
The sketch should still give the same behaviour.

Finally, you modify the sketch to suit your 256x64 geometry.

David.

I had a chance to get some more work done on the project last night, i spent abit of time creating an LM7805 Circuit to take the 10v AC from the Speaker Amp and turn it into 5v DC for the ESP32 and verify it could run the Board & OLED - which it did, success!

I have ported the code to use the u8g2 library, i had the display running the project! I honestly thought it was going to be so much more work so far, i now need to modify the sketch to extend the width of my display and add in some additional wavebands to analyse.

One thing i noticed was that, the refresh of the screen was very delayed, almost a whole second between changes, also the waveband titles were clearing and re-drawing on each change, im not sure if this is because of how i sent the data to display (i dont have the project in front of me at the moment), from memory i was using the Sendbuffer call or something.

I remember seeing in the Graphical example code you can re-send only sections of the display if you want so maybe i should only be re-sending the graphical data and not the column headings (text).

Thanks again for your help.

I will keep this updated mainly for my own tracking, but also if anyone else actually cares about it :stuck_out_tongue:

Alex

Compare how your project works with the video.
Make notes on paper.

Post your sketch. As I suggested earlier, it only needs minor changes.

The way that U8g2lib works is that you write all the graphics commands to a buffer.
Nothing gets displayed until you call display.display()

The ..F.. constructor means the buffer can hold the whole display.
The ESP32 has plenty of SRAM

David.

Ok so here is how it is operating currently. Im not entirely convinced the actual Audio Spectrum part is working correctly, but its a start.

Here is the code i am using so far:

#include <U8g2lib.h>
#include <U8x8lib.h>

/* ESP8266/32 Audio Spectrum Analyser on an SSD1306/SH1106 Display
 * The MIT License (MIT) Copyright (c) 2017 by David Bird. 
 * The formulation and display of an AUdio Spectrum using an ESp8266 or ESP32 and SSD1306 or SH1106 OLED Display using a Fast Fourier Transform
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
 * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
 * publish, distribute, but not to use it commercially for profit making or to sub-license and/or to sell copies of the Software or to 
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:  
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 * See more at http://dsbird.org.uk 
*/

#include <Wire.h>
#include "arduinoFFT.h" // Standard Arduino FFT library 
// https://github.com/kosme/arduinoFFT, in IDE, Sketch, Include Library, Manage Library, then search for FFT
arduinoFFT FFT = arduinoFFT();

U8G2_SSD1322_NHD_256X64_F_4W_SW_SPI u8g2(U8G2_R2, 18, 23, 5, 13, 12);

/////////////////////////////////////////////////////////////////////////
#define SAMPLES 512              // Must be a power of 2
#define SAMPLING_FREQUENCY 40000 // Hz, must be 40000 or less due to ADC conversion time. Determines maximum frequency that can be analysed by the FFT Fmax=sampleF/2.
#define amplitude 200            // Depending on your audio source level, you may need to increase this value
unsigned int sampling_period_us;
unsigned long microseconds;
byte peak[] = {0,0,0,0,0,0,0};
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;
/////////////////////////////////////////////////////////////////////////
void setup() {
  u8g2.begin();
  u8g2.setColorIndex(1);
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_prospero_bold_nbp_tf);
  //display.flipScreenVertically(); // Adjust to suit or remove
  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
}

void loop() {
  u8g2.clear();
  u8g2.drawStr(30,10,"0.1 0.2 0.5 1K  2K  4K  8K");
  for (int i = 0; i < SAMPLES; i++) {
    newTime = micros()-oldTime;
    oldTime = newTime;
    vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
    vImag[i] = 0;
    while (micros() < (newTime + sampling_period_us)) { /* do nothing to wait */ }
  }
  FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
  for (int i = 2; i < (SAMPLES/2); i++){ // Don't use sample 0 and only first SAMPLES/2 are usable. Each array eleement represents a frequency and its value the amplitude.
    if (vReal[i] > 2000) { // Add a crude noise filter, 10 x amplitude or more
      if (i<=2 )             displayBand(0,(int)vReal[i]/amplitude); // 125Hz
      if (i >3   && i<=5 )   displayBand(1,(int)vReal[i]/amplitude); // 250Hz
      if (i >5   && i<=7 )   displayBand(2,(int)vReal[i]/amplitude); // 500Hz
      if (i >7   && i<=15 )  displayBand(3,(int)vReal[i]/amplitude); // 1000Hz
      if (i >15  && i<=30 )  displayBand(4,(int)vReal[i]/amplitude); // 2000Hz
      if (i >30  && i<=53 )  displayBand(5,(int)vReal[i]/amplitude); // 4000Hz
      if (i >53  && i<=200 ) displayBand(6,(int)vReal[i]/amplitude); // 8000Hz
      if (i >200           ) displayBand(7,(int)vReal[i]/amplitude); // 16000Hz
      //Serial.println(i);
    }
    for (byte band = 0; band <= 6; band++) u8g2.drawHLine(18*band,64-peak[band],14);
  }
  if (millis()%4 == 0) {for (byte band = 0; band <= 6; band++) {if (peak[band] > 0) peak[band] -= 1;}} // Decay the peak
   u8g2.sendBuffer();  
}

void displayBand(int band, int dsize){
  int dmax = 50;
  if (dsize > dmax) dsize = dmax;
  if (band == 7) u8g2.drawHLine(18*6,10, 14);//16*6
  for (int s = 0; s <= dsize; s=s+2)u8g2.drawHLine(18*band,64-s, 14);
  if (dsize > peak[band]) {peak[band] = dsize;}
}
U8G2_SSD1322_NHD_256X64_F_4W_SW_SPI u8g2(U8G2_R2, 18, 23, 5, 13, 12);

You should switch to HW SPI! It will be much much faster. However note, that the constructor arguments (sequence of GPIO pins) are different

It might be like this:

U8G2_SSD1322_NHD_256X64_F_4W_HW_SPI u8g2(U8G2_R2, 5, 13, 12, 18, 23);

Oliver

Hi olikraus,

Thanks for the reply, i had tried this method as you mentioned, however it only accepts 3 pins - cs, dc, rst. However the speed of the display appears to be the same. Im wondering if there is some kind of delay in the sketch that is causing this behaviour? when i run the example libraries it seems to work very quickly.

thanks

Alex

AlexD911:
However the speed of the display appears to be the same. Im wondering if there is some kind of delay in the sketch that is causing this behaviour? when i run the example libraries it seems to work very quickly.

Difficult to tell without knowing your code.

Oliver

The speed of your display is not very critical.
The speed of your ADC probably does matter. The GitHub code has different sampling for ESP8266 and ESP32.

I compiled the code for an SPI SSD1306 and ran it on a Due.
I do not have an audio signal. I just rubbed my finger on the A0 pin to create some random noise.

Your loop should only clear the buffer. clear() clears both buffer and screen which looks blinky:

void loop() {
    u8g2.clearBuffer();  //.kbv
    u8g2.drawStr(30, 10, "0.1 0.2 0.5 1K  2K  4K  8K");
    for (int i = 0; i < SAMPLES; i++) {
        newTime = micros() - oldTime;
        oldTime = newTime;
        vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
        vImag[i] = 0;
        while (micros() < (newTime + sampling_period_us)) {
            /* do nothing to wait */
        }
    }
    FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
    FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
    FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
    for (int i = 2; i < (SAMPLES / 2); i++) { // Don't use sample 0 and only first SAMPLES/2 are usable. Each array eleement represents a frequency and its value the amplitude.
        if (vReal[i] > 2000) { // Add a crude noise filter, 10 x amplitude or more
            if (i <= 2 )             displayBand(0, (int)vReal[i] / amplitude); // 125Hz
            if (i > 3   && i <= 5 )   displayBand(1, (int)vReal[i] / amplitude); // 250Hz
            if (i > 5   && i <= 7 )   displayBand(2, (int)vReal[i] / amplitude); // 500Hz
            if (i > 7   && i <= 15 )  displayBand(3, (int)vReal[i] / amplitude); // 1000Hz
            if (i > 15  && i <= 30 )  displayBand(4, (int)vReal[i] / amplitude); // 2000Hz
            if (i > 30  && i <= 53 )  displayBand(5, (int)vReal[i] / amplitude); // 4000Hz
            if (i > 53  && i <= 200 ) displayBand(6, (int)vReal[i] / amplitude); // 8000Hz
            if (i > 200           ) displayBand(7, (int)vReal[i] / amplitude); // 16000Hz
            //Serial.println(i);
        }
        for (byte band = 0; band <= 6; band++)
            u8g2.drawHLine(18 * band, 64 - peak[band], 14);
    }
    if (millis() % 4 == 0) {
        for (byte band = 0; band <= 6; band++) {
            if (peak[band] > 0) peak[band] -= 1;   // Decay the peak
        }
    }
    u8g2.sendBuffer();  //.kbv
}

Hi Guys!

Yes David, thanks for the advice - I had also figured this out when studying the weather example sketch. The display appears to be much more responsive now. I just need to figure out how to adjust the distance of the first column of bars from the left and increase some spacing. Again i have been very busy so have not had much of a chance to sink my teeth into it properly.

On a completely separate note, i will probably need to create a thread in the audio section to get this working correctly. This video shows the new speed i am getting from the ESP32 & Display but also shows its behaviour when nothing is playing. The AMP powering this is a 250 Watt Unit and not a small Microphone like in the tutorial video so i guess i need to step down or clean up the signal somehow.

Thanks again for all of the help!

Alex

Video:

Video