How to use a 1.54 inch 128x64 OLED with a sound sensor?
How to use a 1.54 inch 128x64 OLED with a sound sensor
You connect a 1.54 inch 128x64 oled display to a sound sensor to visualize audio levels in real time, and the process is straightforward if you follow a few electrical and coding rules. The OLED runs on SPI or I2C, but SPI is faster for updating a 128x64 pixel grid at 60 Hz or more, which is critical for capturing sound peaks. The sound sensor, typically an electret microphone with an LM393 comparator or a MAX4466 amplifier, outputs an analog voltage between 0 and 3.3V or 5V, depending on your module. You need an Arduino Uno, ESP32, or a STM32 board, because the OLED’s SPI clock speed can go up to 10 MHz, and the sound sensor’s analog read needs at least 10-bit resolution. The wiring is simple: connect the OLED’s CS to digital pin 10, DC to pin 9, RES to pin 8, SDA to pin 11, SCK to pin 13, and VCC to 3.3V or 5V (check your OLED spec—most tolerate 3.3V logic but some need 5V supply). The sound sensor’s OUT pin goes to analog pin A0, VCC to 5V, GND to GND. If you use a MAX4466, its output swings from 0.5V to 2.5V, so you can feed it directly to a 3.3V Arduino’s analog input without a voltage divider. For the LM393, the output is digital (high or low) unless you tap the analog output from the microphone’s preamp—many modules have a solder pad for that. Check your module’s datasheet; for example, the KY-038 sound sensor has a digital output and an analog output, but the analog pin is often labeled “A0” on the board. The OLED’s driver is typically the SSD1306, which supports 128x64 pixels with 1-bit color depth, so you can draw bar graphs, waveforms, or numeric values. The SPI interface uses four lines: CS (chip select), DC (data/command), RES (reset), and SDA (serial data) plus SCK (clock). The maximum SPI speed for SSD1306 is 10 MHz, but you can run it at 4 MHz to avoid signal noise on long wires. The sound sensor’s analog read on an Arduino Uno takes about 100 microseconds per sample, so you can sample at 10 kHz, but the OLED’s update rate limits you to 60 frames per second if you draw a full screen. For a real-time audio visualizer, you need to sample at 8 kHz to capture frequencies up to 4 kHz, then compute a Fast Fourier Transform (FFT) on a 64-point buffer, which takes about 5 milliseconds on an ESP32 at 240 MHz. On an Arduino Uno, you’re limited to simple peak detection or a bar showing amplitude, because the 16 MHz clock can’t handle FFT without dropping frames. The OLED’s pixel pitch is 0.185 mm, so the display is 23.7 mm wide and 12.7 mm tall, which is enough for a 10-bar equalizer with 12 pixels per bar. The sound sensor’s sensitivity is typically -44 dBV for the MAX4466, meaning it outputs 6.3 mV per Pascal of sound pressure, so a loud conversation at 60 dB SPL (0.02 Pa) gives 0.126 mV, which is below the ADC’s noise floor. You need to amplify the signal with a gain of 100 to 1000, which the MAX4466 does internally (it has a 60 dB gain). The LM393 module has a potentiometer to adjust the threshold, but its analog output is just the raw microphone signal without amplification, so you might need an external op-amp like the LM358. For a practical setup, use the MAX4466 module because it has a built-in amplifier with a gain of 60 dB, a bandwidth of 20 Hz to 20 kHz, and a supply voltage of 2.4V to 5.5V. The output impedance is 100 ohms, so you can drive a long cable up to 1 meter without signal loss. The OLED’s power consumption is 20 mA at 5V, and the sound sensor draws 0.5 mA, so a USB port can power both. The OLED’s contrast is adjustable via the SSD1306 command 0x81, and you can set it to 0x7F (half brightness) to save power. The refresh rate is 60 Hz by default, but you can increase it to 120 Hz by setting the display clock divide ratio to 0xF0, which reduces the frame time to 8.3 ms. However, the SSD1306’s internal oscillator runs at 8 MHz, so the maximum frame rate is 120 Hz for a full screen update. For a sound visualizer, you don’t need to update the entire screen every frame—only the changed pixels, which reduces the SPI data transfer to 64 bytes per bar instead of 1024 bytes. The SPI transaction takes 0.1 ms at 10 MHz for 64 bytes, so you can update 10 bars in 1 ms, leaving 99 ms for sampling and processing. The sound sensor’s analog output should be sampled at 10 kHz to capture the 5 kHz bandwidth of human speech, but the ADC on an Arduino Uno has a 10-bit resolution with a conversion time of 104 microseconds, so you can sample at 9.6 kHz maximum. On an ESP32, the ADC has 12-bit resolution and a conversion time of 2 microseconds, so you can sample at 500 kHz, but the sound sensor’s bandwidth limits you to 20 kHz. The Nyquist theorem says you need to sample at 40 kHz to capture 20 kHz, but the MAX4466’s output is noisy above 10 kHz due to the op-amp’s slew rate. So sample at 20 kHz and use a low-pass filter at 10 kHz in software. The OLED’s 128x64 resolution means you have 128 horizontal pixels for time or frequency, and 64 vertical pixels for amplitude. If you use a 64-point FFT, you get 32 frequency bins, each spanning 312.5 Hz at a 20 kHz sample rate (since bin width = sample rate / FFT size). You can display 32 bins on the OLED, but each bin would be 4 pixels wide, which is too narrow for a bar graph. Instead, group bins into 8 bands: bass (0-625 Hz), low-mid (625-1250 Hz), mid (1250-2500 Hz), high-mid (2500-5000 Hz), and treble (5000-10000 Hz). Each band is 16 pixels wide, and the height represents the amplitude in dB. The OLED’s pixel brightness is binary, so you need to use dithering for gray levels, but a simple bar graph with 64 levels is fine because the human eye perceives brightness logarithmically. The sound sensor’s dynamic range is 60 dB, so you can map the ADC values from 0 to 1023 (10-bit) to a dB scale: dB = 20 * log10(ADC / 1023). But the ADC’s noise floor is about 5 mV, so the lowest detectable signal is 5 mV, which corresponds to -46 dB relative to full scale. The MAX4466’s output is 0.5V to 2.5V, so the ADC range is 0 to 1023 for 0V to 5V, but you need to bias the signal at 1.5V (midpoint of 2.5V) to get both positive and negative swings. The MAX4466 has a built-in bias of 1.25V, so the output is 1.25V ± 1.25V, which fits the 0V to 2.5V range. If you connect it to a 5V Arduino, you need a voltage divider to shift the bias to 2.5V, but most Arduinos accept 0V to 5V, so you can use the full range. The OLED’s SPI interface requires a 3.3V logic level, but many modules are 5V tolerant. Check the datasheet: the SSD1306’s absolute maximum for VCC is 6V, but the logic pins are rated at 3.3V. If you use a 5V Arduino, you need a level shifter for the SPI lines, or you can use a voltage divider for each line. A 1k ohm resistor in series with a 2k ohm resistor to ground gives a 3.3V output from a 5V input. The CS line can be connected directly because it’s a chip select, but the SDA and SCK lines need level shifting. The RES pin is active low, so you can connect it directly to a 5V pin through a 10k ohm resistor to limit current. The DC pin needs level shifting too. Alternatively, use a 3.3V Arduino like the Arduino Pro Mini 3.3V or an ESP32, which runs at 3.3V logic. The ESP32’s ADC has 12-bit resolution and a 0V to 3.3V range, so you need to adjust the MAX4466’s output bias to 1.65V. The MAX4466’s bias is 1.25V, so you can add a capacitor in series to remove the DC offset, then bias the ADC input to 1.65V using a voltage divider from 3.3V. The capacitor value should be 10 uF to pass frequencies above 16 Hz (since cutoff frequency = 1 / (2 * pi * R * C), where R is the input impedance of the ADC, typically 10k ohms). So cutoff = 1 / (2 * 3.14 * 10000 * 0.00001) = 1.6 Hz, which is fine for audio. The OLED’s SPI bus can be shared with other devices if you use separate CS lines, but the sound sensor doesn’t use SPI, so no conflict. The code for the Arduino uses the Adafruit SSD1306 library and the Adafruit GFX library. Install them via the Arduino Library Manager. The SPI pins are fixed on the Uno: 10 (CS), 9 (DC), 8 (RES), 11 (MOSI), 13 (SCK). On the ESP32, you can use any pins, but common choices are: CS=5, DC=4, RES=2, MOSI=23, SCK=18. The sound sensor’s analog pin is GPIO 36 on the ESP32 (ADC1 channel 0). The sample code initializes the OLED with a 128x64 size, sets the SPI speed to 8 MHz, and clears the display. Then in the loop, you read the analog value, map it to a bar height, and draw a rectangle from the bottom of the screen to the height. The bar width is 10 pixels, with a 2-pixel gap between bars, so you can fit 10 bars on the 128-pixel width. The bar height is 64 pixels, so you scale the ADC value to 0-64. The ADC value from the MAX4466 is 0-4095 (12-bit), but the noise floor is about 200, so you subtract 200 and map the range 200-4000 to 0-64. The bar is drawn using the fillRect function, which is faster than drawing lines. To avoid flicker, you clear only the area of the bar that changed, not the entire screen. You store the previous bar height and only redraw if the new height differs. The OLED’s update rate is 60 Hz, so you can sample the sound sensor at 60 Hz, which is enough for a visualizer. But for a waveform display, you need to sample at 1 kHz and draw a scrolling line. The OLED’s memory is 1024 bytes, but you can store a 128-pixel waveform in 128 bytes (each pixel is 1 bit). You shift the waveform left by one pixel each sample, and draw the new point. The SPI speed of 8 MHz means you can transfer 128 bytes in 0.128 ms, so you can update the waveform at 7.8 kHz, but the ADC limits you to 9.6 kHz on the Uno. On the ESP32, you can sample at 20 kHz and update the waveform at 10 kHz, but the OLED’s frame rate is 60 Hz, so you only see 60 updates per second. The waveform appears as a continuous line because the pixels are small. The sound sensor’s frequency response is flat from 20 Hz to 20 kHz, so the waveform is accurate. The OLED’s contrast is set to 0xCF for maximum brightness, but you can adjust it in the code. The display’s viewing angle is 160 degrees, so you can see it from the side. The module’s dimensions are 36 mm x 26 mm x 1.5 mm, so it fits on a breadboard. The sound sensor module is 15 mm x 20 mm, so the whole setup fits on a 5 cm x 7 cm breadboard. The power consumption is 100 mA for the Arduino, 20 mA for the OLED, and 0.5 mA for the sensor, so a 9V battery with a 5V regulator lasts 2 hours. Use a USB power bank for longer operation. The code can be uploaded via the Arduino IDE, and you need to select the correct board and port. For the ESP32, you need to install the ESP32 board support in the IDE. The libraries are available on GitHub. The OLED’s driver is the SSD1306, which is supported by the Adafruit library. The sound sensor’s output is analog, so you don’t need a library. The code is simple: read analog, map to bar height, draw bar. You can add a peak hold feature that keeps the highest bar for 1 second before decaying. The peak is drawn as a dot at the top of the bar. The decay rate is 1 pixel per 100 ms, so the peak falls slowly. The sound sensor’s sensitivity can be adjusted with the potentiometer on the LM393 module, but the MAX4466 has a fixed gain. If the signal is too low, you can add an external amplifier with a gain of 10 using an LM358. The amplifier circuit is non-inverting with a gain of 1 + (R2 / R1). Use R1 = 10k, R2 = 100k for a gain of 11. The bandwidth is 10 kHz, limited by the op-amp’s slew rate. The output is biased at 2.5V using a voltage divider from 5V. The capacitor at the input blocks DC. The output goes to the Arduino’s analog pin. The OLED’s SPI bus can be extended to 2 meters using twisted-pair wires, but the signal may degrade due to capacitance. Use shielded cable for the analog signal from the sound sensor to avoid noise. The ground plane is important: connect all grounds to a single point. The OLED’s VCC pin should be decoupled with a 10 uF capacitor near the module. The sound sensor’s VCC should also have a 0.1 uF capacitor. The Arduino’s 5V pin can supply up to 500 mA, so it’s safe. The code can be optimized by using direct port manipulation for the SPI pins on the Uno, but the library is fast enough. The frame rate is 60 Hz, so the bar graph updates every 16.7 ms. The human eye perceives motion at 24 Hz, so it’s smooth. The sound sensor’s response time is 1 ms, so it’s fast enough. The OLED’s pixel response time is 10 ms, so there’s no ghosting. The display can show text too, using the GFX library’s print function. You can display the dB value as text on the top of the screen. The font is 5x7 pixels, so you can fit 21 characters per line. The text is drawn using the drawChar function, which is slow, so only update the text every 100 ms. The sound sensor’s analog value can be averaged over 10 samples to reduce noise. The averaging filter is a moving average with a window of 10 samples. The code uses a circular buffer to store the last 10 samples, and the average is computed each loop. The noise reduction is 10 dB. The bar graph is smoother. The peak hold function uses a separate variable that is updated only when the current bar is higher. The peak decays by 1 pixel every 100 ms, so you need a timer. Use the millis() function to check if 100 ms has passed. The decay is linear, so the peak falls gradually. The OLED’s brightness can be adjusted with the contrast command. The default is 0x7F, but you can set it to 0xFF for maximum. The power consumption increases by 10 mA at full brightness. The display’s lifetime is 50,000 hours at 50% brightness. The sound sensor’s microphone is omnidirectional, so it picks up sound from all directions. The sensitivity is -44 dBV, so it can detect a whisper at 30 dB SPL. The output is 0.1 mV for a whisper, which is below the noise floor, so you need the amplifier. The MAX4466 has a gain of 60 dB, so the output is 100 mV for a whisper, which is measurable. The ADC’s resolution is 10 bits, so the smallest change is 5 mV, so you can detect 0.5 dB changes. The OLED’s resolution is 128x64, so the bar graph has 64 levels, which is 6 bits, so the ADC’s 10 bits are overkill. You can downsample the ADC to 6 bits to save memory. The code uses a map function to convert 10-bit to 6-bit. The bar graph is drawn with 64 levels, so each pixel represents 1.5 dB. The dynamic range is 96 dB, but the sound sensor’s range is 60 dB, so you only use 40 levels. The bar graph can show a 40 dB range from 30 dB to 90 dB SPL. The threshold is adjustable in the code by subtracting a constant from the ADC value. The constant is the noise floor, which you measure by reading the ADC when no sound is present. The noise floor is typically 200 for a 12-bit ADC, so you subtract 200. The code can be calibrated by pressing a button to record the noise floor. The