How to display a progress bar on a 0.96 inch OLED?
How to Display a Progress Bar on a 0.96 inch OLED
To display a progress bar on a 0.96 inch OLED, you need to write code that draws a filled rectangle whose width scales with the percentage of completion. The most common driver for these displays is the SSD1306, which uses a 128x64 pixel resolution. The progress bar typically occupies a horizontal strip, say 10 pixels tall and 100 pixels wide, centered on the screen. You calculate the fill width as (percentage / 100) * total bar width, then draw a filled rectangle from the left edge to that calculated width. For example, at 50% with a 100-pixel bar, you fill from x=0 to x=50. This is done using the display.fillRect() or display.drawRect() functions in libraries like Adafruit_SSD1306 or u8g2. The key is updating the display in a loop, clearing the old bar before drawing the new one to avoid ghosting. You can also add a percentage text label below the bar for clarity. The 0.96 inch 128x64 i2c oled display is a common choice for this task because it supports I2C communication, which requires only two wires (SDA and SCL) and works well with microcontrollers like Arduino, ESP32, or Raspberry Pi.
The hardware setup is straightforward. The SSD1306 OLED with I2C interface typically operates at 3.3V or 5V, with a default I2C address of 0x3C (or 0x3D on some variants). The display has a resolution of 128 columns and 64 rows, each pixel individually addressable. For a progress bar, you need to decide on the bar dimensions. A common choice is a bar that is 100 pixels wide and 10 pixels tall, positioned at the center of the screen. This leaves 14 pixels on each side horizontally and 27 pixels above and below vertically. You can also add a border around the bar for visual clarity. The border is drawn as a rectangle outline using display.drawRect() with a 1-pixel width. The fill area inside the border is then updated based on the progress. The refresh rate of the SSD1306 is about 30-60 frames per second, depending on the I2C clock speed (standard 100 kHz or fast mode 400 kHz). Updating the entire screen at 400 kHz takes about 1.5 ms for a full frame, but for a progress bar, you only need to update the bar region, which is faster. You can use partial updates by calling display.display() only after drawing the bar, not the whole screen. This reduces flicker and improves responsiveness.
From a software perspective, the most common approach is to use the Adafruit SSD1306 library for Arduino. The core functions are: display.clearDisplay() to clear the buffer, display.drawRect() for the border, display.fillRect() for the fill, and display.display() to send the buffer to the OLED. For a progress bar that updates every 100 milliseconds, you can use a loop with a variable that increments from 0 to 100. At each step, you clear the previous fill area, draw the new fill, and display it. To avoid flicker, you can use double buffering: draw everything in a buffer, then send the buffer to the display in one go. The Adafruit library does this automatically. The buffer size is 1024 bytes (128 * 64 / 8), which fits easily in the SRAM of most microcontrollers. For ESP32, you have 520 KB of SRAM, so no issue. For Arduino Uno, you have 2 KB, which is tight but still works if you optimize. The progress bar itself uses only a small portion of the buffer, so memory is not a bottleneck.
Data-wise, the SSD1306 datasheet specifies that the display can handle up to 100 Hz refresh rate, but in practice, I2C limits this. At 400 kHz, a full frame update takes about 1.5 ms, so you can theoretically update at 666 Hz, but the display's internal timing and the microcontroller's processing speed reduce this. For a progress bar, you don't need high refresh rates; 10-20 updates per second is sufficient for smooth animation. The bar width is calculated as: fillWidth = (progress / 100) * barWidth. For example, with barWidth = 100, at progress = 50, fillWidth = 50. You then draw a filled rectangle from x = barLeft to x = barLeft + fillWidth - 1. The barLeft is typically (128 - barWidth) / 2 = 14. So the filled rectangle is from x=14 to x=63 at 50%. The height is 10 pixels, so y from 27 to 36. The border is drawn from x=14 to x=113, y=27 to y=36. This gives a clear visual representation.
You can also add a percentage label below the bar. For example, at y=40, you can display the number using display.setCursor() and display.print(). The font size 1 (5x7 pixels) is standard, but you can use larger fonts for readability. The Adafruit library includes a 5x7 font by default, and you can add custom fonts like 12x16 for larger text. The label updates with each progress step. To avoid text flicker, clear the text area before printing the new number. For example, draw a filled rectangle over the text area (e.g., y=40 to y=47, x=0 to x=127) in black, then print the new number. This is more efficient than clearing the entire screen.
For advanced implementations, you can use a vertical progress bar instead of horizontal. The same logic applies: scale the height instead of width. For a vertical bar, you set barHeight = 50, barTop = 7, and fillHeight = (progress / 100) * barHeight. You draw a filled rectangle from y = barTop + barHeight - fillHeight to y = barTop + barHeight - 1. This is useful for battery level indicators or loading screens. You can also combine both horizontal and vertical bars for multi-segment progress, like a file download with multiple parts. Each segment can have its own color (if using a dual-color OLED, but most 0.96 inch OLEDs are monochrome). For monochrome, you can use different patterns like dithering or inverted colors to distinguish segments. The SSD1306 supports inverse display mode via display.invertDisplay(true), which flips all pixels. You can use this to highlight the active segment.
Power consumption is another factor. The SSD1306 draws about 20 mA when all pixels are on, but for a progress bar, only a fraction of pixels are lit. A typical progress bar with 100x10 pixels lit uses about 1000 pixels out of 8192, which is about 12% of the display. This translates to roughly 2.4 mA for the display, plus the microcontroller's power. For battery-powered projects, this is efficient. You can further reduce power by using sleep mode between updates. The SSD1306 has a sleep command (0xAE) that drops current to less than 10 µA. After each progress update, you can send the display to sleep for 100 ms, then wake it up (0xAF) for the next update. This reduces average power consumption significantly. For example, if you update every 100 ms and the update takes 5 ms, the display is on for 5% of the time, so average current is about 0.12 mA plus sleep current.
I2C speed also affects performance. At 100 kHz, a full frame update takes about 6 ms, but at 400 kHz, it's 1.5 ms. For a progress bar, the difference is negligible because you only update a small region. However, if you are updating multiple displays or other I2C devices, speed matters. The I2C bus can handle up to 400 kHz for most devices, but some microcontrollers support 1 MHz. The SSD1306 datasheet specifies a maximum clock frequency of 400 kHz for I2C, so stick to that. You can set the I2C clock in Arduino using Wire.setClock(400000). For ESP32, use Wire.begin(SDA, SCL, 400000). This ensures fast updates without errors.
From a coding perspective, here is a typical Arduino sketch structure. First, include the libraries: #include
Error handling is important. If the I2C communication fails, the display may not show anything. You can check the return value of display.begin() which returns true if successful. If false, you can blink an LED or print an error message to serial. For noisy environments, add pull-up resistors on the I2C lines (4.7 kΩ to 10 kΩ) to improve signal integrity. The OLED module usually has built-in pull-ups, but if you are using long wires (more than 20 cm), add external ones. Also, avoid running I2C lines near high-current wires to prevent interference. The SSD1306 is sensitive to voltage spikes, so use a stable power supply. A 100 µF capacitor on the power line can smooth out fluctuations.
For multiple displays, you can use I2C multiplexers like the TCA9548A, which allows up to 8 displays on the same bus. Each display has a unique I2C address, but the 0.96 inch OLED typically uses 0x3C, so you need to change the address by soldering the address select pins on the module. Some modules have a resistor jumper to set the address to 0x3D. If you need multiple displays with the same address, use a multiplexer. The progress bar can be shown on one display while another shows a different status. This is useful for dashboards or multi-parameter monitoring.
In terms of visual design, you can add a gradient effect to the progress bar by using different fill patterns. For example, at 0-25%, use a sparse pattern (every other pixel), at 25-50%, use a denser pattern, and at 50-100%, use solid fill. The SSD1306 supports bitmaps, so you can predefine patterns and draw them using display.drawBitmap(). This adds visual interest without extra computation. The pattern can be stored in PROGMEM to save SRAM. For example, a 10x10 bitmap of a checkerboard pattern uses 100 bits, which is 12.5 bytes. You can cycle through patterns as progress increases. This is more engaging than a solid bar.
Another technique is to use a circular progress bar, which is more complex but visually appealing. You draw an arc using trigonometric functions. For each pixel on the arc, you calculate the angle based on progress, then draw a line from the center to the arc. The SSD1306 has no hardware acceleration for this, so you need to compute the pixels manually. The radius can be 30 pixels, centered at (64, 32). The arc length is 2 * PI * radius * (progress / 100). You draw a line from the center to each point on the arc. This requires more CPU time, but for a 100 MHz ESP32, it's fine. For an 8 MHz Arduino, it may be slow, so use a lower resolution arc (e.g., every 5 degrees) to reduce pixel count. The circular bar can be combined with a percentage text in the center for a professional look.
For web-based projects, you can use an ESP32 to serve a web page that shows the progress bar on the OLED. The ESP32 runs a web server, and when a user clicks a button, it sends a request to update the progress. The ESP32 then updates the OLED via I2C. This is useful for remote monitoring. The web page can use AJAX to poll the progress every second. The ESP32 code uses the AsyncWebServer library to handle requests. The progress variable is stored in RAM and updated by a separate task. The OLED update is done in the main loop. This approach combines IoT with local display, giving you both remote and local feedback.
In industrial applications, the progress bar can indicate the status of a machine cycle. For example, a 3D printer uses a 0.96 inch OLED to show print progress. The bar updates every few seconds based on the G-code commands. The display is mounted on the printer's control board. The I2C connection is reliable over short distances (less than 30 cm). The bar can be combined with temperature and speed data. The SSD1306's 128x64 resolution is enough to show two lines of text and a progress bar simultaneously. For example, the top line shows "Printing: 45%", the middle shows the bar, and the bottom shows "Temp: 210°C". This is a compact but informative display.
Battery life is critical for portable devices. With a 200 mAh battery, the OLED consumes about 2.4 mA for the bar, plus the microcontroller (e.g., ESP32 at 80 mA in active mode). To extend battery life, use deep sleep mode. The ESP32 can wake up every 100 ms, update the display, then go back to sleep. The OLED is also put to sleep between updates. This reduces average current to about 10 mA for the ESP32 and 0.12 mA for the OLED, giving about 20 hours of operation from a 200 mAh battery. If you use an Arduino Pro Mini at 8 MHz (4 mA active), the total is about 4.12 mA, giving 48 hours. For longer life, use a lower update rate (e.g., every 1 second) and a smaller bar (e.g., 50 pixels wide) to reduce pixel count.
Finally, testing the progress bar is straightforward. Use a serial monitor to simulate progress values. For example, send "50" via serial, and the Arduino reads it and updates the bar. This is useful for debugging. You can also use a potentiometer connected to an analog pin to control the progress. The analog value (0-1023) is mapped to 0-100, and the bar updates in real time. This gives a tactile feedback loop. The code is simple: int analogValue = analogRead(A0); int progress = map(analogValue, 0, 1023, 0, 100); display.updateBar(progress); delay(50);. This is a common demo project for beginners.