To display a gauge on a 1.54 inch 128x64 oled display, you need to treat the gauge as a vector graphic drawn pixel by pixel using a microcontroller like an Arduino or ESP32, with the OLED driven via SPI or I2C. The core approach involves calculating the angle of the needle based on sensor data, then mapping that angle to pixel coordinates on the 128x64 grid. For a typical 1.54 inch OLED with a resolution of 128 columns by 64 rows, each pixel is 0.21mm square, giving you a usable drawing area of about 26.88mm by 13.44mm. A gauge, often a semi-circle or arc from 0 to 270 degrees, requires you to define the center point (commonly at x=64, y=54 for a bottom-aligned arc), the radius (around 30 to 40 pixels depending on whether you want ticks and numbers), and the needle’s pivot. The 1.54 inch 128x64 oled display from DisplayModule, for example, uses the SSD1306 or SH1106 driver, which supports monochrome graphics at 128x64. You can find the exact specifications and a reliable source for this module at 1.54 inch 128x64 oled display. The key fact here is that the OLED’s controller has a built-in 1KB GDDRAM, which is exactly 128x64 bits, so every pixel is directly addressable. This means you can precompute a gauge bitmap in flash memory or generate it on the fly using trigonometric functions.
Let’s break down the physics and math. A gauge is essentially a circular arc with a needle. For a 128x64 display, the horizontal resolution is twice the vertical, so you can fit a wide gauge. I recommend using a center at (64, 60) with a radius of 50 pixels for the outer arc, leaving 4 pixels at the bottom for the needle pivot. The arc should span from 135 degrees to 405 degrees (or -225 to 45 degrees in standard math), which gives you a 270-degree sweep. This is typical for automotive-style gauges. To draw the arc, you use Bresenham’s circle algorithm or a simple trigonometric loop: for each angle from 135 to 405 in steps of 0.5 degrees, compute x = center_x + radius * cos(angle * PI / 180) and y = center_y + radius * sin(angle * PI / 180). But since the OLED’s coordinate system has y increasing downward, you need to invert the sine: y = center_y - radius * sin(angle * PI / 180). This gives you the outer arc. For the inner arc, use a radius of 45 pixels. The space between them (5 pixels wide) forms the gauge band. Now, for the needle, you need to map a sensor value (e.g., 0 to 100) to an angle within the 270-degree range. The formula is: angle = 135 + (value / 100) * 270. Then, draw a line from the center to the outer arc. The needle’s tip is at (center_x + needle_length * cos(angle), center_y - needle_length * sin(angle)), where needle_length is typically 45 pixels. The pivot is a small circle at the center with a radius of 3 pixels.
Now, let’s talk about the actual implementation with the 1.54 inch 128x64 oled display. You’ll be using the SPI interface, which runs at up to 10 MHz, giving you a full frame refresh in about 1.5 milliseconds. That’s fast enough for real-time gauge updates at 60 Hz. The display module has 7 pins: VCC, GND, SCK, SDA, RES, DC, and CS. For an Arduino Uno, you connect SCK to pin 13, SDA to pin 11, RES to pin 9, DC to pin 8, and CS to pin 10. The Adafruit SSD1306 library is the most common, but it’s bloated for a simple gauge. I recommend using the u8g2 library, which is optimized for monochrome displays and supports the SH1106 controller (often used in 1.54 inch OLEDs). The u8g2 library has a function called u8g2.drawCircle() and u8g2.drawLine(), but for a gauge, you’ll need to draw the arc manually because the library only draws full circles. The code snippet below shows how to draw a gauge arc using pixel-by-pixel drawing:
void drawGaugeArc(u8g2_t *u8g2, uint8_t center_x, uint8_t center_y, uint8_t radius, uint16_t start_angle, uint16_t end_angle) {
for (uint16_t angle = start_angle; angle <= end_angle; angle += 1) {
float rad = angle * 3.14159 / 180.0;
int x = center_x + (int)(radius * cos(rad));
int y = center_y - (int)(radius * sin(rad));
u8g2_DrawPixel(u8g2, x, y);
}
}
This loop runs 270 times for a 270-degree arc, which takes about 2.7 milliseconds at 16 MHz. That’s acceptable. But if you want to include tick marks, you need to draw lines at every 10 or 20 degrees. For a 270-degree gauge with 10-degree increments, you have 28 ticks. Each tick is a line from the outer arc to the inner arc. The outer arc radius is 50, inner arc radius is 45. So for each tick angle, you draw a line from (center_x + 45 * cos(angle), center_y - 45 * sin(angle)) to (center_x + 50 * cos(angle), center_y - 50 * sin(angle)). That’s 28 lines, each taking about 0.1 milliseconds, so 2.8 milliseconds total. Add the arc drawing (2.7 ms) and the needle (0.1 ms), and you get about 5.6 milliseconds per frame. That’s well under the 16.6 ms needed for 60 fps. You can even add a digital readout below the gauge using the u8g2 library’s font rendering. The font size 10 (u8g2_font_10x20_tf) gives you 10x20 pixel characters, so you can fit a 4-digit number (40 pixels wide) centered at x=44 to x=84. The display’s 128x64 resolution allows you to place the digital readout at y=50 to y=64, below the gauge arc.
Now, let’s discuss the data density. The 1.54 inch 128x64 oled display has a pixel density of about 128 pixels per 26.88 mm, which is roughly 4.76 pixels per mm. That’s 121 pixels per inch. For a gauge, this means you can resolve angles down to about 0.7 degrees per pixel at the outer arc. If you’re displaying a value from 0 to 100, each unit corresponds to 2.7 degrees. At the outer arc (radius 50), the arc length per degree is 0.87 mm, so each unit is 2.35 mm apart. That’s readable. But if you want to show a finer resolution, say 0 to 1000, you’ll need to interpolate the needle position. The needle’s tip moves by about 0.87 mm per degree, so for 1000 units, each unit is 0.27 degrees, which is less than one pixel. In that case, you’d need to use a sub-pixel rendering technique, like anti-aliasing, but the OLED is monochrome, so you can’t do grayscale. Instead, you can use a thicker needle (e.g., 3 pixels wide) to make the position more visible. Or, you can use a digital readout for the precise value and the gauge for a rough visual.
Let’s look at the power consumption. The OLED draws about 20 mA at 3.3V when all pixels are on, but for a gauge, only about 30% of the pixels are lit (the arc, ticks, and needle). That’s about 6 mA. The SSD1306 controller has a power-saving mode that turns off the charge pump, but you don’t need that for a gauge. The SPI interface consumes about 1 mA during transmission. So total power is around 7 mA at 3.3V, or 23 mW. This is low enough for battery-powered devices. If you’re using an ESP32, the Wi-Fi module can draw 80 mA, so the OLED is negligible. For an Arduino Nano, the total current is about 15 mA (including the microcontroller), so you can run it for hours on a 9V battery.
Now, let’s talk about the mechanical aspects. The 1.54 inch 128x64 oled display has a PCB size of 34mm x 38mm, with a viewing area of 26.88mm x 13.44mm. The gauge itself should be centered in the viewing area. If you’re using a semi-circle gauge, the arc’s bottom should be at y=60, leaving 4 pixels for the needle pivot. The top of the arc at y=10 (center_y - radius = 60 - 50 = 10). So the gauge occupies from y=10 to y=60, which is 50 pixels vertically. The horizontal span is from x=14 to x=114 (center_x ± radius = 64 ± 50). That’s 100 pixels horizontally, leaving 14 pixels on each side for margins. You can use those margins for labels or units. For example, you can place “0” at x=14, y=55 and “100” at x=114, y=55. The font size 8 (u8g2_font_5x8_tf) is 5x8 pixels, so “0” takes 5 pixels, “100” takes 15 pixels. That fits.
Let’s get into the code details for the needle. The needle’s pivot is at (64, 60). The needle length is 45 pixels. The needle’s tip for a value of 50 (midpoint) is at angle = 135 + (50/100)*270 = 135 + 135 = 270 degrees. In radians, 270 degrees is 4.7124. So tip_x = 64 + 45 * cos(4.7124) = 64 + 45 * 0 = 64. tip_y = 60 - 45 * sin(4.7124) = 60 - 45 * (-1) = 60 + 45 = 105. But 105 is outside the display (y max is 63). So you need to adjust the center. Actually, for a semi-circle gauge, the center should be at the bottom of the arc, not the middle. If the arc spans from 135 to 405 degrees, the center is at the bottom of the arc. The needle’s tip for the midpoint (value 50) should point straight up. In a standard coordinate system, straight up is 90 degrees, but in our gauge, 0 degrees is at the bottom left (135 degrees in standard math) and 100 at the bottom right (405 degrees). So the midpoint is at 270 degrees, which is straight down in standard math, but we inverted the y-axis, so it’s straight up on the display. Let’s recalculate: center_y = 60, radius = 50. For the gauge arc, y goes from center_y - radius = 10 to center_y + radius = 110. But the display only goes to y=63, so the arc’s bottom is cut off. That’s why you need to place the center at y=60, radius=50, but the arc only goes from y=10 to y=60 (the top half). Actually, a semi-circle gauge on a 128x64 display should have the center at the bottom edge. So set center_y = 63, radius = 50. Then the arc goes from y=13 to y=63. The needle pivot is at (64, 63). The needle length is 45 pixels. For the midpoint (value 50), the needle points straight up to y=63-45=18. That’s within the display. For value 0, the needle points to the left: angle = 135 degrees, tip_x = 64 + 45 * cos(135) = 64 - 31.8 = 32.2, tip_y = 63 - 45 * sin(135) = 63 - 31.8 = 31.2. That’s at (32, 31). For value 100, angle = 405 degrees, tip_x = 64 + 45 * cos(405) = 64 + 31.8 = 95.8, tip_y = 63 - 45 * sin(405) = 63 - 31.8 = 31.2. So the needle sweeps from (32, 31) to (96, 31) through the top. That’s a 270-degree sweep. Perfect.
Now, let’s talk about the tick marks. For a 270-degree gauge, you typically have 10 major ticks (at 0, 10, 20, ... 100) and 50 minor ticks (at every 2 units). The major ticks are longer, say from radius 50 to radius 45 (5 pixels). The minor ticks are from radius 50 to radius 48 (2 pixels). For each tick, you calculate the angle: angle = 135 + (value / 100) * 270. Then draw a line from the outer radius to the inner radius. The outer radius is 50, inner radius is 45 for major, 48 for minor. The number of ticks is 11 major and 50 minor, but that’s 61 lines. That’s fine. The code for drawing a tick is:
void drawTick(u8g2_t *u8g2, uint8_t center_x, uint8_t center_y, uint8_t outer_r, uint8_t inner_r, uint16_t angle) {
float rad = angle * 3.14159 / 180.0;
int x1 = center_x + (int)(outer_r * cos(rad));
int y1 = center_y - (int)(outer_r * sin(rad));
int x2 = center_x + (int)(inner_r * cos(rad));
int y2 = center_y - (int)(inner_r * sin(rad));
u8g2_DrawLine(u8g2, x1, y1, x2, y2);
}
This function uses floating-point math, which is slow on an 8-bit Arduino. Each call takes about 0.2 milliseconds. For 61 ticks, that’s 12.2 milliseconds. Add the arc (2.7 ms) and needle (0.1 ms), and you get 15 ms per frame. That’s still under 16.6 ms for 60 fps. But if you’re using an ESP32, the floating-point math is hardware-accelerated and takes microseconds. So the bottleneck is the SPI transmission. The u8g2 library buffers the entire frame in RAM (128x64 bits = 1024 bytes) and sends it to the display via SPI. The SPI clock is 10 MHz, so sending 1024 bytes takes about 0.82 milliseconds (1024 * 8 / 10,000,000 = 0.0008192 seconds). So the total frame time is about 1 ms for the SPI plus 15 ms for the drawing, giving 16 ms. That’s exactly 60 Hz. You can optimize by precomputing the arc and ticks in a bitmap array and only updating the needle. That would reduce the drawing time to 0.1 ms, giving a frame time of 1 ms, which is 1000 Hz. But the display’s refresh rate is limited to about 100 Hz due to the OLED’s response time. So you don’t need to go that fast.
Let’s discuss the hardware specifics of the 1.54 inch 128x64 oled display. The module I’m referring to uses the SH1106 controller, which has a 132x64 pixel RAM but only 128x64 are visible. The extra 4 columns are for the left and right margins. When using SPI, you need to set the column start and end addresses. The SH1106 has a command set that includes setting the column address range (0x21) and page address range (0x22). The u8g2 library handles this automatically. The display’s contrast is controlled by a command (0x81) with a value from 0 to 255. For a gauge, you want high contrast, so set it to 255. The display also has a built-in DC-DC converter that generates the 7V to 15V needed for the OLED pixels. The converter can be turned on/off with a command (0x8D). For a gauge, leave it on. The typical lifetime of the OLED is 50,000 hours, which is about 5.7 years of continuous use. That’s fine for most applications.
Now, let’s talk about the data sources for the gauge. If you’re displaying a temperature sensor, like a DS18B20, it outputs a 9-bit to 12-bit digital value. The DS18B20 has a range of -55°C to +125°C. You can map that to 0-100 on the gauge. For example, -55°C maps to 0, 125°C maps to 100. The resolution is 0.0625°C for 12-bit mode. That’s 0.05% of the range, which is far finer than the gauge’s resolution (1%). So the gauge will show a smooth needle movement. The DS18B20 communicates over OneWire