My Fifth Project [Sistem Embedded]: Output — Display ESP32: OLED
Hello!! Welcome back again guys!! All right, jadi kali ini aku akan menceritakan experience selama aku mengerjakan Project 5. Sesuai dengan judul blognya, di Project #5 ini kita akan membahas tentang Display pada ESP32.
— Introduction; —
Terdapat dua jenis display, yaitu LCD dan OLED. Pada percobaan kali ini, aku akan menggunakan jenis display OLED SSD1306. Menurut randomnerdtutorial, OLED merupakan display yang tidak memerlukan backlight sehingga menghasilkan kontras yang bagus meskipun dalam kondisi gelap. Selain itu, hanya akan menyala ketika dinyalakan sehingga dapat lebih menghemat energi.
Nah, pada percobaan kali ini, aku akan menampilkan beberapa tampilan pada display OLED, yaitu menampilkan text, gambar, dan animasi.
— Tools; —
Untuk mengerjakan project ini dibutuhkan beberapa tambahan dari Project sebelumnya. Tools yang harus disiapkan, diantaranya:
- ESP 32 DOIT DEVKIT V1
- Breadboard
- Kabel USB (bisa menggunakan kabel yang digunakan pada project sebelumnya).
- Kabel jumper Male to Male (minimum 4 buah). Karena kita akan membuat rangkaian pada breadboard, maka kita memerlukan kabel jumper tipe male to male.
- Display OLED SSD1306, 128×64 pixels.
- Laptop/ Komputer yang memiliki Arduino IDE. Jangan lupa install dan setting Ardunio IDE-mu sama seperti pada project sebelumnya ya guys! [My First ESP32 Project: LED Blink]
— Steps; —
First Steps: Installation
Installation yang perlu dilakukan adalah Library untuk Displaynya terlebih dahulu. Library yang perlu diinstall adalah, Adafruit SSD 1306. Nah, untuk mendukung libraries ini, kita perlu menginstall beberapa libraries pendukung lagi, yaitu Adafruit GFX Library dan Adafruit BusIO. Yuk ikuti langkah-langkah sebagai berikut:
- Buka dan jalankan aplikasi Arduino IDE. Kemudian klik Sketch > Include Library > Manage Libraries
- Lakukan search kemudian ketik “Adafruit SSD 1306” di kolom searching. Lalu, klik Install All.
- Lakukan step yang sama untuk melakukan installation Adafruit GFX Library dan Adafruit BusIO.
- Jika ketiganya sudah berhasil di install, maka library untuk Project #5 sudah berhasil diinstall !!
Next Steps:
- Assemble the Circuit
- Langkah berikutnya yang harus kita lakukan sebelum menjalankan program adalah merakit rangkaiannya terlebih dahulu. Berikut kurang lebih ilustrasi dari tampilan rangkaian kita nanti.
Pin-pin yang ada pada gambar tersebut dihubungkan berdasarkan kabel sesuai dengan tabel berikut:
2. The Code;
Berikut adalah block program yang aku gunakan dalam Project #5 ini:
— Code Program untuk menampilkan text
// Memanggil library yang diperlukan
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>#define SCREEN_WIDTH 128 // Lebar OLED dalam pixels
#define SCREEN_HEIGHT 64 // Tinggi OLED dalam pixels
#define OLED_RESET -1 // Reset pin
#define SCREEN_ADDRESS 0x3C // Alamat I2C yang terhubung dengan display// Deklarasi SSD1306 display yang terhubung dengan I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);void setup() {
Serial.begin(115200); //Inisiasi serial monitorif(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Tidak melakukan apa apa
}
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 10); // Display static text
display.println("Hello, Van!");
display.println("Sistem");
display.println("Embedded");
display.display();
display.startscrollleft(0, 0x0f); //Untuk animasi text scroll ke kiri
}
— Code Program untuk menampilkan animasi Lingkaran
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // Lebar OLED dalam pixels
#define SCREEN_HEIGHT 64 // Tinggi OLED dalam pixels
#define OLED_RESET -1 // Reset pin
#define SCREEN_ADDRESS 0x3C // Alamat I2C yang terhubung dengan display
// Deklarasi SSD1306 display yang terhubung dengan I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
} else {
Serial.println ("tes"); // untuk keperluan debuging
}
delay(2000);
display.clearDisplay();
Serial.println ("acc"); // untuk keperluan debuging
}
void loop() {
display.clearDisplay();
for (;;) {
for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
display.display();
delay(1);
}
display.clearDisplay();
}
delay(2000);
}
— Code Program untuk menampilkan animasi versi RandomTutorials
/*********
Complete project details at https://randomnerdtutorials.com
This is an example for our Monochrome OLEDs based on SSD1306 drivers. Pick one up today in the adafruit shop! ------> http://www.adafruit.com/category/63_98
This example is for a 128x32 pixel display using I2C to communicate 3 pins are required to interface (two I2C and one reset).
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries, with contributions from the open source community. BSD license, check license.txt for more information All text above, and the splash screen below must be included in any redistribution.
*********/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
static const unsigned char PROGMEM logo_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 };
void setup() {
Serial.begin(115200);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
// Draw a single pixel in white
display.drawPixel(10, 10, WHITE);
// Show the display buffer on the screen. You MUST call display() after
// drawing commands to make them visible on screen!
display.display();
delay(2000);
// display.display() is NOT necessary after every single drawing command,
// unless that's what you want...rather, you can batch up a bunch of
// drawing operations and then update the screen all at once by calling
// display.display(). These examples demonstrate both approaches...
testdrawline(); // Draw many lines
testdrawrect(); // Draw rectangles (outlines)
testfillrect(); // Draw rectangles (filled)
testdrawcircle(); // Draw circles (outlines)
testfillcircle(); // Draw circles (filled)
testdrawroundrect(); // Draw rounded rectangles (outlines)
testfillroundrect(); // Draw rounded rectangles (filled)
testdrawtriangle(); // Draw triangles (outlines)
testfilltriangle(); // Draw triangles (filled)
testdrawchar(); // Draw characters of the default font
testdrawstyles(); // Draw 'stylized' characters
testscrolltext(); // Draw scrolling text
testdrawbitmap(); // Draw a small bitmap image
// Invert and restore display, pausing in-between
display.invertDisplay(true);
delay(1000);
display.invertDisplay(false);
delay(1000);
testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
}
void loop() {
}
void testdrawline() {
int16_t i;
display.clearDisplay(); // Clear display buffer
for(i=0; i<display.width(); i+=4) {
display.drawLine(0, 0, i, display.height()-1, WHITE);
display.display(); // Update screen with each newly-drawn line
delay(1);
}
for(i=0; i<display.height(); i+=4) {
display.drawLine(0, 0, display.width()-1, i, WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for(i=0; i<display.width(); i+=4) {
display.drawLine(0, display.height()-1, i, 0, WHITE);
display.display();
delay(1);
}
for(i=display.height()-1; i>=0; i-=4) {
display.drawLine(0, display.height()-1, display.width()-1, i, WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for(i=display.width()-1; i>=0; i-=4) {
display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE);
display.display();
delay(1);
}
for(i=display.height()-1; i>=0; i-=4) {
display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for(i=0; i<display.height(); i+=4) {
display.drawLine(display.width()-1, 0, 0, i, WHITE);
display.display();
delay(1);
}
for(i=0; i<display.width(); i+=4) {
display.drawLine(display.width()-1, 0, i, display.height()-1, WHITE);
display.display();
delay(1);
}
delay(2000); // Pause for 2 seconds
}
void testdrawrect(void) {
display.clearDisplay();
for(int16_t i=0; i<display.height()/2; i+=2) {
display.drawRect(i, i, display.width()-2*i, display.height()-2*i, WHITE);
display.display(); // Update screen with each newly-drawn rectangle
delay(1);
}
delay(2000);
}
void testfillrect(void) {
display.clearDisplay();
for(int16_t i=0; i<display.height()/2; i+=3) {
// The INVERSE color is used so rectangles alternate white/black
display.fillRect(i, i, display.width()-i*2, display.height()-i*2, INVERSE);
display.display(); // Update screen with each newly-drawn rectangle
delay(1);
}
delay(2000);
}
void testdrawcircle(void) {
display.clearDisplay();
for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
display.drawCircle(display.width()/2, display.height()/2, i, WHITE);
display.display();
delay(1);
}
delay(2000);
}
void testfillcircle(void) {
display.clearDisplay();
for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
// The INVERSE color is used so circles alternate white/black
display.fillCircle(display.width() / 2, display.height() / 2, i, INVERSE);
display.display(); // Update screen with each newly-drawn circle
delay(1);
}
delay(2000);
}
void testdrawroundrect(void) {
display.clearDisplay();
for(int16_t i=0; i<display.height()/2-2; i+=2) {
display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
display.height()/4, WHITE);
display.display();
delay(1);
}
delay(2000);
}
void testfillroundrect(void) {
display.clearDisplay();
for(int16_t i=0; i<display.height()/2-2; i+=2) {
// The INVERSE color is used so round-rects alternate white/black
display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
display.height()/4, INVERSE);
display.display();
delay(1);
}
delay(2000);
}
void testdrawtriangle(void) {
display.clearDisplay();
for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
display.drawTriangle(
display.width()/2 , display.height()/2-i,
display.width()/2-i, display.height()/2+i,
display.width()/2+i, display.height()/2+i, WHITE);
display.display();
delay(1);
}
delay(2000);
}
void testfilltriangle(void) {
display.clearDisplay();
for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
// The INVERSE color is used so triangles alternate white/black
display.fillTriangle(
display.width()/2 , display.height()/2-i,
display.width()/2-i, display.height()/2+i,
display.width()/2+i, display.height()/2+i, INVERSE);
display.display();
delay(1);
}
delay(2000);
}
void testdrawchar(void) {
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.cp437(true); // Use full 256 char 'Code Page 437' font
// Not all the characters will fit on the display. This is normal.
// Library will draw what it can and the rest will be clipped.
for(int16_t i=0; i<256; i++) {
if(i == '\n') display.write(' ');
else display.write(i);
}
display.display();
delay(2000);
}
void testdrawstyles(void) {
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
display.println(F("Hello, world!"));
display.setTextColor(BLACK, WHITE); // Draw 'inverse' text
display.println(3.141592);
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(WHITE);
display.print(F("0x")); display.println(0xDEADBEEF, HEX);
display.display();
delay(2000);
}
void testscrolltext(void) {
display.clearDisplay();
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(WHITE);
display.setCursor(10, 0);
display.println(F("scroll"));
display.display(); // Show initial text
delay(100);
// Scroll in various directions, pausing in-between:
display.startscrollright(0x00, 0x0F);
delay(2000);
display.stopscroll();
delay(1000);
display.startscrollleft(0x00, 0x0F);
delay(2000);
display.stopscroll();
delay(1000);
display.startscrolldiagright(0x00, 0x07);
delay(2000);
display.startscrolldiagleft(0x00, 0x07);
delay(2000);
display.stopscroll();
delay(1000);
}
void testdrawbitmap(void) {
display.clearDisplay();
display.drawBitmap(
(display.width() - LOGO_WIDTH ) / 2,
(display.height() - LOGO_HEIGHT) / 2,
logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
display.display();
delay(1000);
}
#define XPOS 0 // Indexes into the 'icons' array in function below
#define YPOS 1
#define DELTAY 2
void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
int8_t f, icons[NUMFLAKES][3];
// Initialize 'snowflake' positions
for(f=0; f< NUMFLAKES; f++) {
icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width());
icons[f][YPOS] = -LOGO_HEIGHT;
icons[f][DELTAY] = random(1, 6);
Serial.print(F("x: "));
Serial.print(icons[f][XPOS], DEC);
Serial.print(F(" y: "));
Serial.print(icons[f][YPOS], DEC);
Serial.print(F(" dy: "));
Serial.println(icons[f][DELTAY], DEC);
}
for(;;) { // Loop forever...
display.clearDisplay(); // Clear the display buffer
// Draw each snowflake:
for(f=0; f< NUMFLAKES; f++) {
display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE);
}
display.display(); // Show the display buffer on the screen
delay(200); // Pause for 1/10 second
// Then update coordinates of each flake...
for(f=0; f< NUMFLAKES; f++) {
icons[f][YPOS] += icons[f][DELTAY];
// If snowflake is off the bottom of the screen...
if (icons[f][YPOS] >= display.height()) {
// Reinitialize to a random position, just off the top
icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width());
icons[f][YPOS] = -LOGO_HEIGHT;
icons[f][DELTAY] = random(1, 6);
}
}
}
}
— The Result; —
— Display Text
— Display Animasi Lingkaran
— Display Example RandomTutorial
— Analysis; —
Pada project ini, ada beberapa library yang harus diinclude dan harus mendefine beberapa variable serta fungsi seperti pada block kode berikut:
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Lalu, ada if conditional untuk mengecek apakah OLED sudah terhubung dengan benar atau belum.
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
Lalu, ada juga beberapa fungsi yang digunakan pada program, sebagai berikut:
display.display()
= menampilkan sesuatu ke display OLEDdisplay.clearDisplay()
= membersihkan display OLEDdisplay.println(string)
= menampilkan tulisandisplay.drawPixel(x, y, color)
= menggambar sebuah titikdisplay.drawLine(x1, y1, x2, y2, color)
= menggambar sebuah garisdisplay.drawRect(x, y, width, height, color)
= menggambar sebuah persegi panjang, namun hanya garis luarnya sajadisplay.drawRoundRect(x, y, width, height, radius, color)
= menggambar sebuah persegi panjang yang ujungnya tumpul, namun hanya garis luarnya sajadisplay.drawTriangle(x, y, width, height, color)
= menggambar sebuah segitiga, namun hanya garis luarnya sajadisplay.fillRect(x, y, width, height, color)
= menggambar sebuah persegi panjang, berwarnadisplay.fillRoundRect(x, y, width, height, radius, color)
= menggambar sebuah persegi panjang yang ujungnya tumpul, berwarnadisplay.setTextSize(int)
= untuk menentukan ukurandisplay.setTextColor(color)
= untuk menentukan warna. Namun, karena OLED yang digunakan monocolor, jadi perubahan warnanya tidak berpengaruhdisplay.setCursor(x, y)
= untuk mengatur posisi awal tulisan. Defaultnya adalah (0,0) yaitu pojok kanan atasdisplay.starscrollleft(x, y)
= menampilkan text dengan animasi scroll text to left
— The Problem; —
- Error Compiling for board ESP32 DEVKIT V1. Pada process compiling kemarin, aku menemukan kegagalan dalam proses compile dengan error message seperti pada gambar dibawah ini. Aku mengira bahwa board atau ESP32 punyaku mengalami kerusakan. Akan tetapi, setelah aku coba menggunakan ESP32 temanku masih sama aja error compiling. Kemudian aku mengganti kabel USBnya. Ternyata masih sama saja error. Lalu, aku mencoba melakukan installation ulang Software Arduino IDE, mengingat ternyata punyaku belum versi terbaru. Setelah melakukan installation latest version dari Arduino IDE, akhirnya compiling succes. Yeayyy!!
— Additional Project: PWM; —
Jadi, setelah mengerjakan dan mengumpulkan blog ini, ada opsional project yang dapat dikerjakan. Karena aku penasaran dengan project ini maka aku mencoba mengerjakannya. Sesuai dengan namanya, project PWM (Pulse Width Modulation). PWM ini diartikan sebagai sebuah metode untuk memanipulasi tebal sinyal dengan nilai amplitudo dan frekuensi yang tetap. Pada project ini, tebal sinyal yang akan dimanipulasi adalah terang dari LED. Biasanya pada project ini akan melibatkan motor/dimmer sebagai pengubah tebal sinyal. Namun, pada project ini, aku hanya akan menggunakan LED saja sehingga nantinya terang redupnya LED akan berlangsung secara terus menerus.
— Tools—
- ESP 32 DOIT DEVKIT V1
- Breadboard
- Kabel USB (bisa menggunakan kabel yang digunakan pada project sebelumnya).
- Kabel jumper Male to Male (minimum 4 buah). Karena kita akan membuat rangkaian pada breadboard, maka kita memerlukan kabel jumper tipe male to male.
- Lampu LED 3 buah
- Resistor 330 ohm 3 buah
- Laptop/ Komputer yang memiliki Arduino IDE. Jangan lupa install dan setting Ardunio IDE-mu sama seperti pada project sebelumnya ya guys! [My First ESP32 Project: LED Blink]
— Steps—
Untuk mengerjakan project ini aku memakai sumber dari randomnerdtutorials. Untuk informasi lebih lengkap bisa klik link yang tercantum.
Selanjutnya, kita akan merakit rangkaian untuk project ini. Berikut adalah kurang lebih ilustrasi rangkaiannya.
— The Code —
// the number of the LED pin
const int ledPin = 16; // 16 corresponds to GPIO16
const int ledPin2 = 17; // 17 corresponds to GPIO17
const int ledPin3 = 5; // 5 corresponds to GPIO5
// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;
void setup(){
// configure LED PWM functionalitites
ledcSetup(ledChannel, freq, resolution);
// attach the channel to the GPIO to be controlled
ledcAttachPin(ledPin, ledChannel);
ledcAttachPin(ledPin2, ledChannel);
ledcAttachPin(ledPin3, ledChannel);
}
void loop(){
// increase the LED brightness
for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){
// changing the LED brightness with PWM
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
// decrease the LED brightness
for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
// changing the LED brightness with PWM
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
}
— The Result —
— Closing; —
Yeayy! Project keempat sudah selesai.. Itulah beberapa percobaan mengenai Display ESP32 with OLED dan Project PWM. Semoga pengalaman yang aku sharing ini bisa bermanfaat bagi teman-teman yang membaca. Masih banyak lagi project-project lain yang gak kalah seru dan menarik. So staytune and see you !!