10. Output Devices¶
This week I added an OLED display to my devboard from the Electronics Production week.
My goal was to connect the display via I2C (more on this protocol in next week’s assignment) and let it show a constantly changing diagram that will later represent the measured values in my final project.
I once again teamed up with Anna, Kerstin and Lars, the other students from HRW FabLab, to measure power consumption of a NeoPixel LED strip.
You can find our page right here.
Preparing the hardware¶
For the display I am using this 1.5 inch OLED module from Waveshare.
It features a resolution of 128x128 pixels, can be controlled via SPI or I2C and works with 3.3V or 5V.
To put the display into I2C mode I had to reposition the two resistors on the back and connect BS1 and BS2 to 1.
I also bridged BS3 to 1 so the device address is set to 0x3D.
Since this display supports two different communication protocols some pins have multiple functions so I connected the display’s DIN
and CLK
pins to my devboard’s SDA
and SCL
pins respectively, as well as VCC to 3.3V and GND to GND.
Documenting my failures¶
Waveshare provides an archive with demo file folders for their display modules via this link.
By default the demo for my display is set to use SPI to communicate so I had to open the DEV_Config.h
file and replace
#define USE_SPI_4W 1
#define USE_IIC 0
with
#define USE_SPI_4W 0
#define USE_IIC 1
I selected my board and hit the Verify
button only to be greeted by a billion error messages linked to the chinese fonts.
Fortunately I don’t really need those fonts so I simply removed them from the file directory.
After verifying again I was left with only two more error messages.
The first one was kind of self-explanatory, a function of the type void
cannot return anything of a different type.
I simply changed the code in line 19 of the OLED_1in5.ino file from
return -1;
to
return;
The second error was also easily fixed by changing line 630 in the GUI_Paint.cpp file from
void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font, UWORD Color_Background, UWORD Color_Foreground)
to
void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const unsigned char * pString, cFONT* font, UWORD Color_Background, UWORD Color_Foreground)
Looks like there are some undefined references to the removed fonts, so I guess I’ll just remove the references as well?
Verifying worked without any errors this time.
The Upload had no problems either.
And the result is… nothing…
A completely blank screen is not what I wanted to see, so I grabbed myself a multimeter and got to measuring.
Input voltage and all the signals seemed to arrive but for some reason were not actually displayed.
In fact the screen didn’t even turn on.
I tried
-
swapping out my board with an Arduino Uno… nothing changed.
-
using an entirely different library for OLED displays with both boards… nothing changed.
-
using a different display as a sanity check… Hello World! (Flipped upside down)
Guess who managed to fry his OLED display while swapping the position of two resistors and bridging two contacts.
This guy 👇
Just to be sure, I checked the same code with my original devboard.
Actually programming some stuff¶
This display unfortunately only has half the screen realestate of the old one, but it’ll work for now.
Anyways, I was talking about a different display library.
The one I used is called U8g2_Arduino by olikraus.
I simply downloaded the master.zip
archive from their github repo and imported it into a new Arduino sketch via Sketch
-> Include Library
-> Add .ZIP Library
.
I then added the following code which I found in a section about OLED displays in the seeed studio wiki.
#include <Arduino.h>
#include <U8x8lib.h>
#include <Wire.h>
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // OLEDs without Reset of the Display
void setup(void) {
u8x8.begin();
u8x8.setFlipMode(1); // set number from 1 to 3, the screen word will rotary 180
}
void loop(void) {
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.setCursor(0, 0);
u8x8.print("Hello World!");
}
The only thing I had to change was the display’s constructor reference, which I took from olikraus’ documentation.
U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // OLEDs without Reset of the Display
After reading further into the documentation I found out that I had to switch from U8X8 to U8G2, a newer library with more features.
U8X8 only works with rows and columns consisting of 8x8 pixel chunks, while with U8G2 you can control everything on a per pixel basis.
Luckily this was already included in the .zip file I downloaded.
My new code looked like this:
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_1_HW_I2C U8G2(U8G2_R0, 5, 4, U8X8_PIN_NONE);
void setup(void) {
U8G2.begin();
}
void loop(void) {
U8G2.firstPage();
do {
U8G2.setFont(u8g2_font_ncenB14_tr);
U8G2.drawStr(0,15,"Hello World!");
} while ( U8G2.nextPage() );
delay(1000);
}
and produced this:
Looking good so far, let’s try building a simple graph.
I replaced the code in the loop section with the following:
void loop(void) {
U8G2.firstPage();
do {
U8G2.drawVLine(0, 0, 64);
U8G2.drawHLine(0, 63, 128);
} while ( U8G2.nextPage() );
delay(1000);
}
This essentially draws two lines, one vertical, one horizontal, to produce the x and y axes of a basic graph.
Now I needed some values to display.
The first thing I tried was to just visualize random numbers, so I set up an array with the size of my display’s width and called the integrated random function to fill it with values.
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_1_HW_I2C U8G2(U8G2_R0, 5, 4, U8X8_PIN_NONE);
int values[128];
void setup(void) {
U8G2.begin();
}
void loop(void) {
U8G2.firstPage();
do {
U8G2.drawVLine(0, 0, 64);
U8G2.drawHLine(0, 63, 128);
for(int i=0; i<128; i++){
values[i] = random(0,64);
U8G2.drawPixel(i, values[i]);
}
} while ( U8G2.nextPage() );
}
You can find my graph_random.ino file in the Download section at the bottom of this page.
That was a little too much TV static for my taste so I went with a sine wave.
The basics stayed the same but I added two arrays.
One to store the Y-axis values of a sine function, the other one to hold them in a buffer in order to loop the first array.
Part of the code to generate said sine wave was taken from this post from stievenart in the arduino forum.
All the stored values are eventually drawn as individual pixels.
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_1_HW_I2C U8G2(U8G2_R0, 5, 4, U8X8_PIN_NONE);
int values[120]; // array for sine values
int buffer[120]; // buffer array used to shift sine values
void initArrays(void){
// fill arrays
for (int i = 0; i < 120; i++){
float angle = i * 3; // 3 * 120 = 360
values[i] = (32 + (sin(angle * (M_PI / 180)) * 30)); // values shifted upwards by 32 for easier visualization
buffer[i] = values[i]; // initialize buffer with the same values
}
}
void setup(void) {
U8G2.begin(); // initialize and clear display
initArrays(); // initialize arrays
}
void loop(void) {
// constantly repeating code
U8G2.firstPage();
do {
U8G2.drawVLine(0, 0, 64); // draw Y-axis
U8G2.drawHLine(0, 63, 128); // draw X-axis
for (int j = 0 ; j < 120; j++){
values[j] = buffer[j]; // transfer values from buffer to output array
U8G2.drawPixel(j+4, values[j]); // draw the sine wave with values from the array shifted 4 pixels to the right
}
for (int x = 0 ; x < 120; x++){ // loop array via buffer
if(x-1 < 0){
buffer[x] = values[119];
} else {
buffer[x] = values[x-1];
}
}
}
while ( U8G2.nextPage() );
delay(25); // control scroll speed of the sine wave
}
The sine_wave.ino file is also linked down below, feel free to test it yourself.