Arduino: OLED displej 128×64 (SSD1306)

Lekcija 6 od 28 · Arduino i ESP32

Serial Monitor je odličan dok je Arduino vezan za računar. Ali termometar, brojač ili igrica treba da rade i bez računara – za to služi mali OLED displej. Ima 128 × 64 tačke (piksela), dobro se vidi i u mraku, a sa Arduinom se povezuje sa samo četiri žice preko veze I2C.

Šta je OLED displej

  • Najčešći je displej dijagonale 0,96 inča sa čipom SSD1306. Ima 128 tačaka u širinu i 64 u visinu.
  • Svaka tačka sama svetli (kao mala LED dioda), pa displej nema pozadinsko osvetljenje i troši malo struje.
  • Postoje jednobojni (beli, plavi) i dvobojni (gornjih 16 redova žuto, ostalo plavo) – programiraju se isto.
  • Komunicira preko I2C veze: dve žice (SDA za podatke i SCL za takt) i svaki uređaj ima svoju adresu. Adresa ovog displeja je najčešće 0x3C.

Povezivanje

Pin displejaArduino UnoESP32
VCC5V (ili 3.3V)3V3
GNDGNDGND
SDAA4GPIO21
SCLA5GPIO22

Kod nekih modula redosled pinova je GND, VCC, SCL, SDA – uvek pročitaj natpise na pločici pre povezivanja.

U Arduino IDE-u instaliraj biblioteke Adafruit SSD1306 i Adafruit GFX Library (Sketch → Include Library → Manage Libraries). Kada te IDE pita da instalira i biblioteke koje su potrebne, izaberi „Install all“.

Provera: I2C skener

Pre prvog programa proveri da li Arduino „vidi“ displej. Ovaj kratki program ispisuje adrese svih uređaja na I2C vezi:

// I2C skener – ispisuje adrese svih uređaja na I2C vezi (radi i na Arduinu i na ESP32)
#include <Wire.h>

void setup() {
  Serial.begin(9600);
  Wire.begin();                 // na ESP32 možeš napisati Wire.begin(21, 22);
  Serial.println("Trazim I2C uredjaje...");
  int pronadjeno = 0;
  for (int adresa = 1; adresa < 127; adresa++) {
    Wire.beginTransmission(adresa);
    if (Wire.endTransmission() == 0) {      // 0 = uređaj se javio
      Serial.print("Uredjaj na adresi 0x");
      Serial.println(adresa, HEX);
      pronadjeno++;
    }
  }
  if (pronadjeno == 0) {
    Serial.println("Nema uredjaja - proveri zice SDA, SCL, VCC i GND.");
  }
}

void loop() {
}

Ako se ispiše 0x3C (ili 0x3D), sve je ispravno povezano. Ako nema uređaja, proveri žice SDA i SCL – najčešće su zamenjene.

Program 1: prvi tekst

// Arduino – OLED displej 128x64 (SSD1306, I2C): prvi tekst
// Biblioteke: „Adafruit SSD1306“ i „Adafruit GFX Library“ (Library Manager)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const int SIRINA = 128;
const int VISINA = 64;
// -1: displej nema poseban RESET pin
Adafruit_SSD1306 ekran(SIRINA, VISINA, &Wire, -1);

void setup() {
  Serial.begin(9600);
  // 0x3C je najčešća I2C adresa ovih displeja (ponekad 0x3D)
  if (!ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Displej nije pronadjen!");
    while (true) {
    }                           // zaustavi program
  }
  ekran.clearDisplay();         // obriši sliku u memoriji
  ekran.setTextColor(SSD1306_WHITE);
  ekran.setTextSize(1);         // mala slova: 21 znak u redu
  ekran.setCursor(0, 0);        // gornji levi ugao
  ekran.println("Zdravo!");
  ekran.setTextSize(2);         // dvostruko veća slova
  ekran.setCursor(0, 20);
  ekran.println("Arduino");
  ekran.display();              // tek sada se slika prenosi na displej
}

void loop() {
}

Kako se crta na displeju

  • Tačka (0, 0) je u gornjem levom uglu. Broj x raste udesno (do 127), a y nadole (do 63).
  • Sve naredbe crtaju u memoriju Arduina. Slika se prenosi na displej tek naredbom display() – ako je zaboraviš, ekran ostaje prazan.
  • clearDisplay() briše sliku u memoriji; pozivamo je pre crtanja novog sadržaja.
setTextSizeVisina slovaZnakova u reduRedova na ekranu
18 tačaka218
216 tačaka104
324 tačke72

Program 2: brojač i oblici

// Arduino – OLED: vreme od uključenja i traka koja se puni
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 ekran(128, 64, &Wire, -1);

void setup() {
  ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ekran.setTextColor(SSD1306_WHITE);
}

void loop() {
  unsigned long sekunde = millis() / 1000;

  ekran.clearDisplay();
  ekran.setTextSize(1);
  ekran.setCursor(0, 0);
  ekran.print("Vreme od ukljucenja:");
  ekran.setTextSize(3);
  ekran.setCursor(0, 16);
  ekran.print(sekunde);
  ekran.print(" s");

  int duzina = map(sekunde % 10, 0, 9, 0, 124);   // traka se puni svakih 10 s
  ekran.drawRect(0, 52, 128, 12, SSD1306_WHITE);  // okvir trake
  ekran.fillRect(2, 54, duzina, 8, SSD1306_WHITE);
  ekran.display();
  delay(200);
}
NaredbaŠta crta
drawLine(x1, y1, x2, y2, boja)liniju
drawRect(x, y, širina, visina, boja)okvir pravougaonika
fillRect(x, y, širina, visina, boja)popunjen pravougaonik
drawCircle(x, y, r, boja)krug
fillCircle(x, y, r, boja)popunjen krug
drawPixel(x, y, boja)jednu tačku

Boja je SSD1306_WHITE (tačka svetli) ili SSD1306_BLACK (tačka ne svetli).

Program 3: termometar sa DHT11

Spajamo displej sa senzorom iz lekcije Senzor temperature: DATA pin senzora DHT11 je na pinu 3, a displej je i dalje na A4 i A5.

// Arduino – OLED termometar: temperatura i vlažnost sa DHT11 (DATA na pinu 3)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>

#define DHTPIN 3
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
Adafruit_SSD1306 ekran(128, 64, &Wire, -1);

void setup() {
  dht.begin();
  ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ekran.setTextColor(SSD1306_WHITE);
}

void loop() {
  float t = dht.readTemperature();
  float h = dht.readHumidity();

  ekran.clearDisplay();
  ekran.setTextSize(1);
  ekran.setCursor(0, 0);
  ekran.print("Sobni termometar");
  ekran.drawLine(0, 10, 127, 10, SSD1306_WHITE);   // linija ispod naslova

  if (isnan(t) || isnan(h)) {
    ekran.setCursor(0, 28);
    ekran.print("Greska senzora!");
  } else {
    ekran.setTextSize(2);
    ekran.setCursor(0, 18);
    ekran.print(t, 1);
    ekran.print(" C");
    ekran.setCursor(0, 42);
    ekran.print(h, 0);
    ekran.print(" %");
  }
  ekran.display();
  delay(2000);                  // DHT11 merimo na 2 sekunde
}

Ovo je prvi pravi uređaj koji radi bez računara: napajaj Arduino iz punjača za telefon ili baterije od 9 V i postavi ga bilo gde u sobi.

OLED displej na ESP32

Isti displej i iste biblioteke rade i na ESP32. Razlike su samo dve: displej se napaja sa 3V3, a I2C veza se pokreće naredbom Wire.begin(21, 22).

// ESP32 – isti OLED displej; menja se samo pokretanje I2C veze
// (SDA = GPIO21, SCL = GPIO22)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 ekran(128, 64, &Wire, -1);

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);           // SDA, SCL – podrazumevani pinovi na ESP32
  if (!ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Displej nije pronadjen!");
    while (true) {
      delay(100);
    }
  }
  ekran.clearDisplay();
  ekran.setTextColor(SSD1306_WHITE);
  ekran.setTextSize(2);
  ekran.setCursor(0, 0);
  ekran.println("ESP32");
  ekran.setTextSize(1);
  ekran.println("OLED radi na 3,3 V");
  ekran.display();
}

void loop() {
}

Displej ćemo koristiti i u završnom projektu pametna učionica.

Česti problemi

  • Ekran je potpuno crn: proveri adresu skenerom (0x3C ili 0x3D) i da li si pozvao display().
  • Program staje na „Displej nije pronađen“: zamenjene su žice SDA i SCL ili displej nema napajanje.
  • Arduino Uno radi čudno kada dodaš još koda: slika ekrana zauzima 1024 bajta, a Uno ima samo 2048 bajtova radne memorije. Koristi kraće tekstove i manje promenljivih (ili ESP32, koji ima mnogo više memorije).
  • Tekst se preklapa: zaboravljen je clearDisplay() pre novog crtanja.

Zadaci za vežbu

Pokušaj prvo sam da rešiš zadatak, pa tek onda otvori rešenje.

  1. Prikaži na displeju koliko je puta pritisnut taster na pinu 2, velikim brojevima (veličina slova 4).
  2. Nacrtaj grafik osvetljenja: LDR na A0, nova tačka svakih 100 ms, a stari deo grafika se pomera ulevo.
  3. Napravi animaciju: loptica poluprečnika 4 tačke kreće se po ekranu i odbija se od ivica.
Rešenje 1: brojač pritisaka na displeju
// Zadatak – brojač pritisaka tastera (pin 2) na OLED displeju, velikim brojevima
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 ekran(128, 64, &Wire, -1);
const int TASTER = 2;
int broj = 0;
int prethodno = HIGH;

void prikazi() {
  ekran.clearDisplay();
  ekran.setTextSize(1);
  ekran.setCursor(0, 0);
  ekran.print("Pritisaka:");
  ekran.setTextSize(4);
  ekran.setCursor(0, 20);
  ekran.print(broj);
  ekran.display();
}

void setup() {
  pinMode(TASTER, INPUT_PULLUP);
  ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ekran.setTextColor(SSD1306_WHITE);
  prikazi();
}

void loop() {
  int sada = digitalRead(TASTER);
  if (prethodno == HIGH && sada == LOW) {
    broj++;
    prikazi();
    delay(30);
  }
  prethodno = sada;
}
Rešenje 2: grafik osvetljenja
// Zadatak – grafik osvetljenja: LDR na A0, nova tačka svakih 100 ms,
// a grafik se pomera ulevo
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 ekran(128, 64, &Wire, -1);
byte visine[128];               // pamtimo poslednjih 128 merenja (jedno po koloni)

void setup() {
  ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  for (int x = 0; x < 128; x++) {
    visine[x] = 63;
  }
}

void loop() {
  for (int x = 0; x < 127; x++) {          // pomeri sve tačke ulevo
    visine[x] = visine[x + 1];
  }
  visine[127] = map(analogRead(A0), 0, 1023, 63, 0);   // veća vrednost = viša tačka
  ekran.clearDisplay();
  for (int x = 1; x < 128; x++) {
    ekran.drawLine(x - 1, visine[x - 1], x, visine[x], SSD1306_WHITE);
  }
  ekran.display();
  delay(100);
}
Rešenje 3: loptica koja se odbija
// Zadatak – loptica koja se odbija od ivica ekrana
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 ekran(128, 64, &Wire, -1);
int x = 10, y = 10;             // položaj loptice
int dx = 2, dy = 1;             // pomeraj u svakom koraku
const int R = 4;                // poluprečnik

void setup() {
  ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C);
}

void loop() {
  x += dx;
  y += dy;
  if (x - R <= 0 || x + R >= 127) dx = -dx;   // udarac u levu ili desnu ivicu
  if (y - R <= 0 || y + R >= 63) dy = -dy;    // udarac u gornju ili donju ivicu
  ekran.clearDisplay();
  ekran.drawRect(0, 0, 128, 64, SSD1306_WHITE);
  ekran.fillCircle(x, y, R, SSD1306_WHITE);
  ekran.display();
  delay(20);
}

Proveri znanje

Otvori kviz

Kviz: OLED displej

1. Na koje pinove Arduina Uno se vezuju SDA i SCL?




2. Gde je tačka (0, 0) na displeju?




3. Šta se desi ako zaboraviš display()?




4. Koja je najčešća I2C adresa displeja SSD1306?