Skip to content
CSRD-Ready Sustainability Platform · Berlin / SF

How to display a line chart on a 1.54 inch 128x64 OLED?

By admin

How to Display a Line Chart on a 1.54 Inch 128x64 OLED

To display a line chart on a 1.54 inch 128x64 oled display, you need to drive the OLED via SPI or I2C, buffer pixel data in RAM, and render the chart using a microcontroller like an ESP32 or STM32. The key is understanding that this OLED is monochrome, with a resolution of 128 columns by 64 rows, and each pixel is either on or off. You cannot display gradients or anti-aliasing natively, so line charts must be drawn with single-pixel width lines, or you can use dithering for thicker lines. The display controller is typically the SSD1309 or SH1106, both of which support page addressing mode and horizontal addressing mode. For line charts, horizontal addressing mode is more efficient because you can write sequential bytes to the display buffer without recalculating page boundaries. The SPI interface runs at up to 10 MHz, which allows you to update the entire frame in about 1.6 milliseconds (128*64/8 = 1024 bytes, at 10 MHz = 0.8 microseconds per byte, plus overhead). This is fast enough for real-time data plotting, but you need to manage the buffer carefully to avoid flicker.

First, choose a microcontroller with enough RAM. The display buffer requires 1024 bytes (128 columns * 64 rows / 8 bits per byte). An Arduino Uno with 2 KB RAM can handle it, but you’ll have little room for other data. ESP32 with 520 KB RAM is a better choice, especially if you’re plotting data from sensors or a web server. For the wiring, connect the OLED’s CS (chip select) to a GPIO pin, DC (data/command) to another GPIO, RESET to a third, SCLK to SPI clock, and MOSI to SPI data. If you’re using I2C, the speed is limited to 400 kHz, which increases frame update time to about 20 milliseconds (1024 bytes at 400 kHz = 2.56 microseconds per byte, plus address overhead). For line charts, SPI is recommended because you can push data faster.

Now, the core of the line chart: you need to map your data points (x, y) to pixel coordinates. The x-axis runs from 0 to 127, and the y-axis from 0 to 63, with (0,0) at the top-left corner. To make the chart readable, you’ll want to leave margins: typically 10 pixels on the left for y-axis labels, 10 pixels on the right, 10 pixels on the top, and 10 pixels on the bottom. This gives you a plotting area of 108 pixels wide by 44 pixels high. If your data has a range of 0 to 100, you scale the y-value: pixel_y = 54 - (value / 100) * 44 (assuming 0 is at the bottom). The x-axis is scaled similarly: pixel_x = 10 + (index / total_points) * 108. For a line chart, you connect consecutive points with straight lines. The Bresenham line algorithm is the standard for drawing lines on a pixel grid, and it’s computationally cheap. You can implement it in C or Python (for microPython). Here’s a rough breakdown: for each pair of points (x1, y1) and (x2, y2), calculate the delta x and delta y, determine the step direction, and iterate through pixels, setting the corresponding bit in the buffer. The buffer is a 2D array of bytes, where each byte represents 8 vertical pixels. For example, column 0, row 0-7 is stored in byte 0, column 0, row 8-15 in byte 1, and so on. To set a pixel at (x, y), you compute the byte index: byte_index = x + (y / 8) * 128, and the bit position: bit = y % 8. Then set that bit to 1.

For a real-world example, consider plotting temperature data from a DS18B20 sensor. The sensor outputs 12-bit values, but you can map them to the OLED’s 44-pixel height. If the temperature ranges from 20°C to 30°C, you can scale it: pixel_y = 54 - ((temp - 20) / 10) * 44. The x-axis increments every 10 seconds, so after 108 data points (about 18 minutes at 10-second intervals), the chart scrolls. To implement scrolling, you shift the buffer left by one column and add the new data point to the rightmost column. This requires shifting 1024 bytes, which on an ESP32 at 240 MHz takes about 0.1 milliseconds. But on an Arduino Uno, shifting 1024 bytes takes about 2 milliseconds, which is still acceptable for a 10-second update rate. However, if you’re updating faster, you might want to use a circular buffer and only redraw the changed pixels.

Another approach is to use a library like Adafruit_SSD1306 or U8g2, which handle the low-level SPI communication and buffer management. Adafruit’s library uses a 1024-byte buffer and provides functions like drawLine() and drawPixel(). But these libraries are not optimized for line charts: they redraw the entire buffer on every update, which can cause flicker if you’re updating frequently. To avoid flicker, you can use double buffering: allocate two buffers, draw to one, and then swap it with the display buffer. This doubles the RAM requirement to 2048 bytes, but it’s smooth. On an ESP32, you can use the SPIFFS or PSRAM for extra memory if needed.

Data density is critical: the OLED’s 128x64 resolution means you can show up to 108 data points on the x-axis (with margins), but the y-axis resolution is only 44 pixels. That’s fine for a general trend, but if you need to show fine details, you’ll need to zoom in or use a larger display. For example, if your data has a range of 0.1°C, the 44-pixel height can only represent 44 distinct values, so the chart will look quantized. To improve this, you can implement a moving average or decimate the data. Alternatively, you can use the OLED’s full 64-pixel height by removing the top and bottom margins, but then you lose the axis labels. You can also add a grid: draw horizontal lines every 10 pixels, and vertical lines every 20 pixels. This uses about 200 additional pixels, which is negligible in terms of buffer updates.

Let’s talk about the 1.54 inch 128x64 oled display itself. It’s a 1.54-inch diagonal, which is about 35 mm by 17.5 mm active area. The pixel pitch is 0.28 mm, so each pixel is about 0.28 mm square. This is small enough that you can’t read text smaller than 5 pixels tall, but for line charts, single-pixel lines are visible. The contrast ratio is typically 2000:1, and the brightness is about 100 cd/m², which is readable indoors but not in direct sunlight. The display consumes about 20 mA with all pixels on, but for a line chart, most pixels are off, so the current draw is around 5-10 mA. This makes it suitable for battery-powered projects, like a portable temperature logger.

For the software side, you need to handle the initialization sequence. The SSD1309 requires a specific sequence of commands: set display off, set multiplex ratio to 63 (for 64 rows), set display offset to 0, set start line to 0, set segment re-map to column 127 (mirror), set COM pins hardware configuration, set contrast to 0x7F, set charge pump enable, set display on. This sequence is standard and can be found in the datasheet. If you’re using a library, it handles this automatically. But if you’re writing your own driver, you need to send these commands via SPI. For example, to set the contrast, you send command 0x81 followed by the contrast value (0x00 to 0xFF). The default contrast is 0x7F, but you can adjust it for different lighting conditions.

Now, let’s get into the specifics of drawing a line chart with multiple data series. Suppose you have two sensors: temperature and humidity. You can draw them in different line styles: solid for temperature, dashed for humidity. A dashed line is drawn by skipping every other pixel. For example, for a line from (x1, y1) to (x2, y2), you draw pixels only when (x % 2 == 0). This is easy to implement by modifying the Bresenham algorithm. Alternatively, you can use different line thicknesses: draw two parallel lines for a thicker line. But on a 64-pixel height, a 2-pixel thick line reduces the dynamic range. Another option is to invert the pixels: draw the temperature line in white (pixels on) and the humidity line in black (pixels off) on a gray background. But the OLED is monochrome, so you can’t have a gray background unless you use a dithering pattern. A 50% dithering pattern (checkerboard) takes about 512 bytes of buffer space, but it can be used as a background grid.

For performance, you should avoid redrawing the entire chart every time. Instead, update only the new data point. For example, if you have a scrolling chart, you only need to shift the buffer left by one column and draw the new line segment. But shifting the buffer is not trivial: you need to move 128 bytes per row (since each row is 128 columns, and there are 8 rows of bytes). On a 64-row display, that’s 8 rows of bytes. Shifting left by one column means moving each byte’s bits to the left, but since the bytes are column-major, it’s easier to shift the entire buffer by one byte per column. Actually, the buffer is stored as 128 columns, each column is 8 bytes (for 64 rows). So shifting left by one column means you move the entire column’s 8 bytes to the left by one column. This is a memcpy operation of 8 bytes, repeated 127 times, for a total of 1016 bytes moved. On an ESP32, this takes about 0.5 milliseconds. On an Arduino Uno, it takes about 5 milliseconds. That’s acceptable for a 1-second update rate.

Let’s look at a concrete example with code-like pseudocode. Assume you have an array of 108 data points, each 16-bit integer. The chart is drawn with the y-axis inverted (0 at bottom). The scaling factor is: y_pixel = 54 - (data_point / max_value) * 44. The x_pixel = 10 + index. For each new data point, you draw a line from the previous point to the new point. The Bresenham algorithm in C for a line from (x0, y0) to (x1, y1) is:

dx = abs(x1 - x0); dy = abs(y1 - y0); sx = (x0 < x1) ? 1 : -1; sy = (y0 < y1) ? 1 : -1; err = dx - dy; while (1) { setPixel(x0, y0); if (x0 == x1 && y0 == y1) break; e2 = 2 * err; if (e2 > -dy) { err -= dy; x0 += sx; } if (e2 < dx) { err += dx; y0 += sy; } }

This algorithm works on any microcontroller and is efficient. The setPixel function writes to the buffer. After drawing all lines, you call a function to send the buffer to the OLED. For the 1.54 inch 128x64 oled display, you send the buffer via SPI using the following sequence: set column address range (0x21, 0x00, 0x7F), set page address range (0x22, 0x00, 0x07), then send 1024 bytes of data. This is the fastest way to update the entire display. If you only need to update a portion, you can set the column and page range to a smaller rectangle, but for line charts, it’s usually easier to update the whole buffer.

Now, consider the power consumption. The OLED’s charge pump requires a capacitor (typically 1 µF) between VCC and GND. If you’re using a battery, you can put the OLED in sleep mode by sending command 0xAE. This reduces current to about 1 µA. When you need to update the chart, wake it up with 0xAF, which takes about 100 ms to stabilize. For a chart that updates every 10 seconds, you can keep the display on, but for a chart that updates every minute, you can sleep between updates. The trade-off is that waking up takes time, so you might miss a data point if the sensor reads faster.

For the chart labels, you need to render text. The OLED has no built-in font, so you need to use a bitmap font. A 5x7 font (5 pixels wide, 7 pixels tall) is common. You can store the font as a 96-byte array per character (for ASCII 32-127). For example, the character ‘0’ is stored as 5 bytes: 0x3E, 0x51, 0x49, 0x45, 0x3E. To display a number, you convert it to a string and then draw each character. For the y-axis, you might show values like 20, 25, 30. Each label takes about 5x3 = 15 pixels wide, so you can fit about 7 labels on the left margin. For the x-axis, you can show timestamps like “12:00”, “12:10”, etc. Each label takes about 5x5 = 25 pixels, so you can fit about 4 labels on the bottom margin. This is enough for a basic chart.

One common issue is the OLED’s ghosting or image retention. If you display the same chart for hours, the pixels might retain a faint image. To avoid this, you can invert the display every few minutes (send command 0xA7 for inverted, 0xA6 for normal). Or you can shift the chart by a few pixels periodically. This is not critical for most projects, but if you’re building a product, it’s worth considering.

Let’s talk about the physical connection. The 1.54 inch OLED typically has a 7-pin header: GND, VCC (3.3V or 5V), SCLK, MOSI, CS, DC, RESET. Some modules have a 4-pin I2C version. For SPI, you need to connect all 7 pins. The VCC can be 3.3V or 5V, but the logic level is 3.3V. If you’re using a 5V microcontroller like Arduino Uno, you need a level shifter or a voltage divider on the MOSI and SCLK lines. The OLED’s maximum SPI clock is 10 MHz, but some modules are rated for 20 MHz. For reliability, use 4 MHz. The CS pin is active low, so you pull it low to select the display. The DC pin is low for commands and high for data. The RESET pin is active low; you can tie it to VCC with a 10k resistor, but it’s better to control it from the microcontroller for a proper reset sequence.

If you’re using a Raspberry Pi, you can use the SPI interface with the spidev library. The Python code is similar: import spidev, open the device, and send commands and data. But the Raspberry Pi’s GPIO is 3.3V, so no level shifting is needed. The buffer size is 1024 bytes, which is trivial for the Pi. You can also use the Pi’s DMA to send data faster. For a line chart, you can use matplotlib to generate the chart and then convert it to a bitmap, but that’s overkill. Instead, write a simple Python script that reads sensor data, scales it, and draws lines using the same Bresenham algorithm.

For the data source, you can use an ADC to read a potentiometer, or a digital sensor like the BMP280 for pressure and temperature. The BMP280 outputs 16-bit values, and you can map them to the OLED’s range. For example, pressure ranges from 300 to 1100 hPa, so you scale it: y_pixel = 54 - ((pressure - 300) / 800) * 44. The x-axis can be time-based, using the microcontroller’s millis() function. To store the data, you can use a circular buffer of 108 elements. Each element is a 16-bit integer, so the buffer is 216 bytes. This fits in the Arduino Uno’s RAM, but you also need the display buffer (1024 bytes), so total RAM usage is about 1240 bytes, leaving about 800 bytes for other variables. That’s tight, but it works. On an ESP32, you have plenty of RAM.

Now, let’s discuss the chart’s aesthetics. The OLED’s monochrome nature means you can’t use colors, but you can use different line styles: solid, dashed, dotted, or thick. For a dashed line, draw 2 pixels on, 2 pixels off. For a dotted line, draw 1 pixel on, 3 pixels off. You can also use a crosshair cursor to show the current value. The cursor is a small cross (5x5 pixels) at the last data point. This helps the user see the current value. To draw the crosshair, you draw a horizontal line and a vertical line through the point. But be careful not to overwrite the chart lines. You can XOR the pixels: if the pixel is on, turn it off, and vice versa. This is easy to implement by reading the buffer, toggling the bit, and writing it back. But reading the

See your real Scope 1–3 baseline in 14 days.

A 20-minute live demo with a solutions engineer — no slides, no greenwashing, just your data flowing through the platform.

Book a live demo