OLED Smoothing - Page 1
Forum section: electronics / general

author: Thane of Cawdor
author_rank: Regular Contributor
timestamp: August 01, 2014, 03:17:44 pm
content: Hi, I am trying to make a really low bandwidth 'oscilloscope' with an I2C 128x64 OLED display and an Arduino (for a graphical voltmeter). I managed to get a line scrolling along the x-axis and the analog signal scaled on the y-axis using two Adafruit libraries (Adafruit-GFX.h and Adafruit_SSD1306.h). The only problem I am faced with is that when the signal is jumping around irregularly or too quickly, the pixels on the display do not join up and this gets very messy.Basically, I need to get those pixels to join up with straight lines somehow. I am not sure whether I'd need to store the previous analog value and then call another display.drawLine command to connect them.Test Code:Code:#include <SPI.h>#include <Wire.h>#include <Adafruit_GFX.h>#include <Adafruit_SSD1306.h>#define OLED_RESET 4Adafruit_SSD1306 display(OLED_RESET);#define NUMFLAKES 10#define XPOS 0#define YPOS 1#define DELTAY 2#define LOGO16_GLCD_HEIGHT 16#define LOGO16_GLCD_WIDTH 16static const unsigned char PROGMEM logo16_glcd_bmp[] ={B00000000, B11000000,B00000001, B11000000,B00000001, B11000000,B00000011, B11100000,B11110011, B11100000,B11111110, B11111000,B01111110, B11111111,B00110011, B10011111,B00011111, B11111100,B00001101, B01110000,B00011011, B10100000,B00111111, B11100000,B00111111, B11110000,B01111100, B11110000,B01110000, B01110000,B00000000, B00110000 };#if (SSD1306_LCDHEIGHT != 64)#error("Height incorrect, please fix Adafruit_SSD1306.h!");#endifint x = 0;int y = 0;void setup() {Serial.begin(9600);// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)display.begin(SSD1306_SWITCHCAPVCC, 0x3c); // initialize with the I2C addr 0x3D (for the 128x64)// init done// Show image buffer on the display hardware.// Since the buffer is intialized with an Adafruit splashscreen// internally, this will display the splashscreen.display.display();delay(500);}void loop() {while (x<=128) {x++;y = (analogRead(A0));display.drawLine(x, y, x, y);display.display();}while (x >128) {display.clearDisplay();x = 0;}}

author: zapta
author_rank: Super Contributor
timestamp: August 01, 2014, 04:32:16 pm
content: I implemented something similar (scroll down the page)https://github.com/zapta/power-monitors/tree/master/pmon_3v8The graph is drawn using lines, not pixels. It uses SPI ssd1306 display (faster than I2C) and uses ug8lib to draw the pages. The units does 10 samples a second and the graph scrolls smoothly from right to left. The vertical axis is logarithmic to have better dynamic resolution. When a new data point is added, it is mapped to a pixel Y value and stored in a ring buffer such that the mapping from value to pixel is done only once per data point.u8glib is a great graphic library. The only down side is that it redraws the entire screen each time.You can find the core display code here. It supports multiple pages that are toggled by the button.https://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/display.cpp

author: andyturk
author_rank: Frequent Contributor
timestamp: August 01, 2014, 05:28:31 pm
content: What zapta said. You need to draw lines, not just dots. UG8 looks like a really nice library for this kind of thing. I haven't used it personally, but I borrowed some of its implementation tricks for my own graphic library. Here's an oscilloscope-like view drawn on a TFT LCD via SPI. The sample data is pretty noisy to begin with, but by drawing lines between the samples, it's easier to see what's going on:Here's come code for drawing lines between two arbitrary points usingBresenham's Algorithm:Code:void Canvas::draw_line(Point p0, Point p1, pixel value) {if (!clip.clip(p0, p1)) return;if (p0.x == p1.x) { // vertical linecoord ymax = std::max(p0.y, p1.y);for (coord y=std::min(p0.y, p1.y); y <= ymax; ++y) set_pixel(Point(p0.x, y), value);} else if (p0.y == p1.y) {coord xmax = std::max(p0.x, p1.x);for (coord x=std::min(p0.x, p1.x); x <= xmax; ++x) set_pixel(Point(x, p0.y), value);} else {bool steep = std::abs(p1.y - p0.y) > std::abs(p1.x - p0.x);if (steep) {std::swap(p0.x, p0.y);std::swap(p1.x, p1.y);}if (p0.x > p1.x) {std::swap(p0, p1);}coord dx = p1.x - p0.x;coord dy = std::abs(p1.y - p0.y);coord error = dx/2;coord y = p0.y;coord ystep = (p0.y < p1.y) ? 1 : -1;for (coord x=p0.x; x <= p1.x; ++x) {if (steep) {set_pixel(Point(y, x), value);} else {set_pixel(Point(x, y), value);}if ((error -= dy) < 0) {y += ystep;error += dx;}}}}

author: dannyf
author_rank: Super Contributor
timestamp: August 01, 2014, 05:34:25 pm
content: QuoteJim Lynch wrote a set of really nice routines to drive a Nokia 6100 LCD. Google will help find it for you. The code is written in a very nice hierarchical way.QuoteHaving a ram buffer allows you to have lots more flexibility - particularly on overlaying objects on a display that doesn't support read-back. However, depending on your choice of mcu, it may not be feasible or as important.

author: zapta
author_rank: Super Contributor
timestamp: August 01, 2014, 06:04:35 pm
content: Quote from: dannyf on August 01, 2014, 05:34:25 pmug8lib allows to overlay objects without requiring a whole display buffer. You provide a drawing function that is called for each of N subsections of the display. This way its need a buffer of size 1/N of the whole display. With the 1.3" 128x64 display I use the options are N=4 or N=8.https://code.google.com/p/u8glib/wiki/tpictureloop

author: Thane of Cawdor
author_rank: Regular Contributor
timestamp: August 02, 2014, 04:01:49 am
content: Thanks for the responses!QuoteQuoteI thought the display.drawLine(x, y, x, y, WHITE) part of the code did just that. Does it only display dots because the start and end values are the same (x, y, x, y)?Code:drawing lines between two arbitrary points using Bresenham's AlgorithmThat looks really neat! Would I just need to change the names of some of the functions in order for it to be compatible with the Adafruit library?

author: andyturk
author_rank: Frequent Contributor
timestamp: August 02, 2014, 04:25:59 am
content: Quote from: Thane of Cawdor on August 02, 2014, 04:01:49 amSure looks that way.QuoteI know squat about Adafruit's code, but if all you have is a primitive to turn individual pixels on, then Bresenham's Algorithm is the way to get lines that look reasonable to the eye without doing too many calculations. In fact, the same algorithm is probably behind the Adafruit code you're already calling.