Home Ứng dụng ESP8266 Giao tiếp màn hình OLED SSD1306 0.96 với ESP8266 NodeMCU theo chuẩn I2C

Giao tiếp màn hình OLED SSD1306 0.96 với ESP8266 NodeMCU theo chuẩn I2C

0
Giao tiếp màn hình OLED SSD1306 0.96 với ESP8266 NodeMCU theo chuẩn I2C

Trong bài viết này tôi sẽ hướng dẫn các bạn cách giao tiếp màn hình OLED SSD1306 0.96 inch với module ESP8266 NodeMCU dùng Arduino IDE. Tôi sẽ hướng dẫn cho bạn cách hiển thị văn bản, thiết lập các phông chữ khác nhau, vẽ hình dạng và hiển thị hình ảnh bitmap lên màn hình OLED.

Xem thêm: Hướng dẫn lập trình ESP8266 NodeMCU dùng Arduino IDE

Giới thiệu màn hình OLED SSD1306 0.96 inch

Màn hình OLED mà tôi sẽ sử dụng trong bài hướng dẫn này là màn hình OLED SSD1306: màn hình đơn sắc, 0,96 inch với kích thước 128×64 pixel như hình dưới đây.

Màn hình OLED SSD1306 0.96 inch

Màn hình OLED không yêu cầu đèn nền, mang lại độ tương phản rất đẹp trong môi trường tối. Ngoài ra, các pixel của nó chỉ tiêu thụ năng lượng khi hoạt động, vì vậy màn hình OLED tiêu thụ ít năng lượng hơn so với các màn hình khác.

Màn hình OLED SSD1306 có bốn chân và giao tiếp với bất kỳ vi điều khiển nào bằng giao thức truyền thông I2C. Với một phiên bản khác có thêm một chân RESET hoặc giao tiếp bằng giao thức truyền thông SPI.

Sơ đồ kết nối mạch

Do màn hình OLED sử dụng giao thức truyền thông I2C nên việc đi dây rất đơn giản. Bạn xem bảng bên dưới để kết nối mạch cho đúng.

ChânModule ESP8266
Vin3.3V
GNDGND
SCLGPIO 5 (D1)
SDAGPIO 4 (D2)

Bạn cũng có thể làm theo sơ đồ dưới đây để nối ESP8266 NodeMCU với màn hình OLED.

Sơ đồ kết nối màn hình OLED SSD1306 0.96 inch với ESP8266 NodeMCU

Trong ví dụ này, tôi sử dụng giao thức truyền thông I2C. Các chân được sử dụng cho giao tiếp I2C trong ESP8266 là GPIO 5 (SCL) và GPIO 4 (SDA).

Nếu bạn đang sử dụng màn hình OLED với giao thức truyền thông SPI, hãy sử dụng các chân GPIO sau:

  • GPIO 14: CLK
  • GPIO 12: MISO
  • GPIO 13: MOSI
  • GPIO 15: CS

Cài đặt thư viện

Để có thể giao tiếp màn hình OLED SSD1306 0.96 inch với module ESP8266 NodeMCU bạn cần cài đặt hai thư viện:  Adafruit_SSD1306 library và Adafruit_GFX library.

Bạn hãy làm theo các bước hướng dẫn sau đây để cài đặt hai thư viện này.

1. Đầu tiên hãy mở chương trình Arduino IDE, sau đó chọn menu Sketch Include Library > Manage Libraries. Cửa sổ Library Manager sẽ mở ra.

2. Nhập “SSD1306” vào ô tìm kiếm và chọn Install để cài đặt thư viện SSD1306 của Adafruit.

Cài đặt thư viện ssd1306

3. Sau khi cài đặt xong thư viện SSD1306, bạn hãy nhập “GFX” vào hộp tìm kiếm và tiếp tục cài đặt thư viện này.

Cài đặt thư viện gfx

4. Sau khi cài đặt xong các thư viện, hãy khởi động lại chương trình Arduino IDE.

Tiếp theo, chúng ta sẽ lập trình cho module ESP8266 bằng Arduino IDE, vì vậy bạn phải cài đặt tiện ích bổ sung (add-on) ESP8266 vào chương trình Arduino IDE. Nếu chưa, hãy xem bài viết hướng dẫn tại đây.

Kiểm tra màn hình OLED với ESP8266

Sau khi kết nối màn hình OLED với ESP8266 NodeMCU và cài đặt tất cả các thư viện cần thiết, bạn có thể sử dụng một chương trình mẫu từ thư viện để xem mọi thứ có hoạt động bình thường hay không.

Trong Arduino IDE, chọn File > Examples Adafruit SSD1306, chọn chương trình ví dụ cho màn hình OLED mà bạn đang sử dụng.

Chương trình ví dụ giao tiếp màn màn hình OLED SSD1306 0.96 với ESP8266 NodeMCU

Chương trình giao tiếp màn màn hình OLED SSD1306 0.96 với ESP8266 NodeMCU:


#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)
// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
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[] =
{ 0b00000000, 0b11000000,
  0b00000001, 0b11000000,
  0b00000001, 0b11000000,
  0b00000011, 0b11100000,
  0b11110011, 0b11100000,
  0b11111110, 0b11111000,
  0b01111110, 0b11111111,
  0b00110011, 0b10011111,
  0b00011111, 0b11111100,
  0b00001101, 0b01110000,
  0b00011011, 0b10100000,
  0b00111111, 0b11100000,
  0b00111111, 0b11110000,
  0b01111100, 0b11110000,
  0b01110000, 0b01110000,
  0b00000000, 0b00110000 };

void setup() {
  Serial.begin(9600);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    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, SSD1306_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, SSD1306_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, SSD1306_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, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_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, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_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, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_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, SSD1306_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, SSD1306_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, SSD1306_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, SSD1306_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, SSD1306_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, SSD1306_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, SSD1306_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, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawchar(void) {
  display.clearDisplay();

  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_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(SSD1306_WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F("Hello, world!"));

  display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
  display.println(3.141592);

  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(SSD1306_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(SSD1306_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, SSD1306_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);
      }
    }
  }
}

Nếu màn hình OLED của bạn không có chân RESET, bạn nên set biến OLED_RESET bằng -1.

#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)

Nạp chương trình trên vào module ESP8266 NodeMCU. Đừng quên chọn đúng board mạch và cổng COM trên menu Tools.

Bạn sẽ thấy được một loạt hoạt ảnh khác nhau được hiển thị trên màn hình OLED SSD1306 0.96 như trong video ngắn dưới đây.

Nếu màn hình OLED của bạn không hiển thị bất kỳ hình ảnh nào cả thì hãy:

  • Kiểm tra xem màn hình OLED có được kết nối với module ESP8266 đúng cách hay không.
  • Kiểm tra kỹ địa chỉ I2C của màn hình OLED: kết nối OLED với ESP8266, nạp chương trình bên dưới và xem địa chỉ I2C trong màn hình nối tiếp (Serial Monitor).

Chương trình xác định địa chỉ I2C của màn hình OLED:

#include <Wire.h>
 
void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("\nI2C Scanner");
}
 
void loop() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("Da tim thay thiet bi I2C tai dia chi 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
      nDevices++;
    }
    else if (error==4) {
      Serial.print("Loi khong xac dinh tai dia chi 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0) {
    Serial.println("Khong tim thay thiet bi I2C\n");
  }
  else {
    Serial.println("Xong\n");
  }
  delay(5000);          
}

Nếu cần, bạn thay đổi lại địa chỉ OLED trong dòng lệnh sau. Màn hình OLED mà tôi sử dụng có địa chỉ là 0x3C.

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {

Hiển thị chữ trên màn hình OLED

Thư viện Adafruit dành cho màn hình OLED đi kèm với một số chức năng để hiển thị chữ (văn bản). Trong phần này, bạn sẽ học cách hiển thị và cuộn văn bản bằng các hàm của thư viện này.

Chương trình hiển thị chữ “Hello, World!” lên màn hình OLED

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // Chiều rộng màn hình OLED, tính bằng pixel
#define SCREEN_HEIGHT 64 // Chiều cao màn hình OLED, tính bằng pixel

// Khai báo màn hình SSD1306 được kết nối theo chuẩn I2C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Địa chỉ 0x3D cho 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000);
  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 10);
  // Hiển thị văn bản tĩnh
  display.println("Hello, world!");
  display.display(); 
}

void loop() {
  
}

Sau khi nạp chương trình vào ESP8266, bạn sẽ thấy chữ “Hello, Word!” hiển trị trên màn hình OLED như hình bên dưới.

Giải thích chương trình:

Import các thư viện

Trước tiên, bạn cần import các thư viện cần thiết. Thư viện Wire để sử dụng chuẩn giao tiếp I2C và hai thư viện Adafruit để hiển thị chữ ra màn hình: Adafruit_GFX và Adafruit_SSD1306.

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

Khởi tạo màn hình OLED

Sau đó, bạn khai báo chiều rộng và chiều cao của màn hình OLED. Trong ví dụ này, tôi đang sử dụng màn hình OLED 128 × 64. Nếu bạn đang sử dụng các kích thước khác, bạn có thể thay đổi kích thước thông qua các biến SCREEN_WIDTH và SCREEN_HEIGHT.

#define SCREEN_WIDTH 128 // Chiều rộng màn hình OLED tính bằng pixel

#define SCREEN_HEIGHT 64 // Chiều cao màn hình OLED tính bằng pixel

Sau đó, khởi tạo một đối tượng hiển thị với chiều rộng và chiều cao được xác định trước đó bằng giao thức truyền thông I2C (&Wire).

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

Tham số (-1) có nghĩa là màn hình OLED của bạn không có chân RESET. Nếu màn hình OLED của bạn có chân RESET, nó sẽ được kết nối với chân GPIO. Trong trường hợp đó, bạn nên chuyển số chân GPIO làm tham số.

Trong phần setup(), hãy khởi tạo màn hình nối tiếp (Serial Monitor) ở tốc độ truyền 115200 cho mục đích gỡ lỗi.

Serial.begin(115200);

Khởi tạo màn hình OLED với phương thức begin() như sau:

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))

{ Serial.println(“SSD1306 allocation failed”);

for(;;);

}

Đoạn chương trình này cũng hiển thị một thông báo trên Serial Monitor, trong trường hợp chúng ta không thể kết nối với màn hình.

Serial.println(“SSD1306 allocation failed”);

Trong trường hợp bạn đang sử dụng một màn hình OLED khác, bạn có thể cần phải thay đổi địa chỉ OLED. Màn hình mà tôi đang dùng có địa chỉ là 0x3C.

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {

Nếu địa chỉ này không hoạt động, bạn có thể chạy chương trình để tìm địa chỉ của màn hình OLED. Bạn có thể tìm thấy chương trình này tại đây.

Sau khi khởi tạo màn hình, hãy thêm thời gian trễ (delay) hai giây để OLED có đủ thời gian khởi tạo trước khi hiển thị văn bản:

delay(2000);

Xóa màn hình, đặt cỡ chữ, màu sắc và hiển thị văn bản

Sau khi khởi tạo màn hình, hãy xóa bộ đệm hiển thị bằng lệnh clearDisplay():

display.clearDisplay();

Trước khi hiển thị văn bản, bạn cần chọn kích thước văn bản, màu sắc và vị trí văn bản sẽ được hiển thị trên OLED.

Đặt kích thước văn bản bằng lệnh setTextSize():

display.setTextSize(1);

Chọn màu cho văn bản bằng lệnh setTextColor():

display.setTextColor(WHITE);

WHITE đặt phông chữ màu trắng và nền đen.

Xác định vị trí bắt đầu văn bản bằng lệnh setCursor(x,y). Trong chương trình này, chúng ta đặt văn bản bắt đầu ở tọa độ (0,10).

display.setCursor(0,10);

Cuối cùng, cho hiển thị văn bản lên màn hình bằng lệnh println() như sau:

display.println(“Hello, world!”);

Sau đó, bạn cần dùng lệnh display() để thực sự hiển thị văn bản trên màn hình.

display.display();

Cuộn văn bản

Thư viện OLED của Adafruit cung cấp các phương pháp hữu ích để dễ dàng cuộn văn bản.

startscrollright(0x00, 0x0F): cuộn văn bản từ trái sang phải

startscrollleft(0x00, 0x0F): cuộn văn bản từ phải sang trái

startscrolldiagright(0x00, 0x07): cuộn văn bản từ góc dưới bên trái sang góc trên bên phải

startscrolldiagleft(0x00, 0x07): cuộn văn bản từ góc dưới bên phải sang góc trên bên trái

Chương trình sau đây thực hiện các kiểu cuộn văn bản trên.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 
#define SCREEN_HEIGHT 64 

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000);
  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("Scrolling Hello");
  display.display(); 
  delay(100);
 
}

void loop() {
  // Cuộn văn bản theo các hướng khác nhau, tạm dừng ở giữa:
  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);
}

Thay đổi phông chữ

Thư viện Adafruit GFX cho phép chúng ta sử dụng một số phông chữ thay thế bên cạnh các phông chữ sẵn có. Nó cho phép bạn chọn các phông chữ Serif, Sans và Mono. Mỗi phông chữ có sẵn ở dạng đậm, nghiêng và với các kích cỡ khác nhau.

Kích thước được chọn theo phông chữ thực tế. Vì vậy, lệnh setTextSize() không hoạt động với các phông chữ này. Các phông chữ này có kích cỡ 9, 12, 18 và 24 point và cũng chứa các ký tự 7 bit (mã ASCII) (được mô tả là 7b trong tên phông chữ).

Bạn có thể chọn từ danh sách các phông chữ sau đây:

FreeMono12pt7b.h		FreeSansBoldOblique12pt7b.h
FreeMono18pt7b.h		FreeSansBoldOblique18pt7b.h
FreeMono24pt7b.h		FreeSansBoldOblique24pt7b.h
FreeMono9pt7b.h			FreeSansBoldOblique9pt7b.h
FreeMonoBold12pt7b.h		FreeSansOblique12pt7b.h
FreeMonoBold18pt7b.h		FreeSansOblique18pt7b.h
FreeMonoBold24pt7b.h		FreeSansOblique24pt7b.h
FreeMonoBold9pt7b.h		FreeSansOblique9pt7b.h
FreeMonoBoldOblique12pt7b.h	FreeSerif12pt7b.h
FreeMonoBoldOblique18pt7b.h	FreeSerif18pt7b.h
FreeMonoBoldOblique24pt7b.h	FreeSerif24pt7b.h
FreeMonoBoldOblique9pt7b.h	FreeSerif9pt7b.h
FreeMonoOblique12pt7b.h		FreeSerifBold12pt7b.h
FreeMonoOblique18pt7b.h		FreeSerifBold18pt7b.h
FreeMonoOblique24pt7b.h		FreeSerifBold24pt7b.h
FreeMonoOblique9pt7b.h		FreeSerifBold9pt7b.h
FreeSans12pt7b.h		FreeSerifBoldItalic12pt7b.h
FreeSans18pt7b.h		FreeSerifBoldItalic18pt7b.h
FreeSans24pt7b.h		FreeSerifBoldItalic24pt7b.h
FreeSans9pt7b.h			FreeSerifBoldItalic9pt7b.h
FreeSansBold12pt7b.h		FreeSerifItalic12pt7b.h
FreeSansBold18pt7b.h		FreeSerifItalic18pt7b.h
FreeSansBold24pt7b.h		FreeSerifItalic24pt7b.h
FreeSansBold9pt7b.h		FreeSerifItalic9pt7b.h

Các phông chữ hoạt động tốt hơn với màn hình OLED với kích thước 9 và 12 point.

Để sử dụng một trong những phông chữ trên, trước tiên bạn cần đưa nó vào chương trình của mình, ví dụ:

#include <Fonts/FreeSerif12pt7b.h>

Tiếp theo, bạn chỉ cần sử dụng lệnh setFont(), đối số chính là phông chữ đã chọn.

display.setFont(&FreeSerif12pt7b);

Sau khi chọn phông chữ, tất cả các lệnh để hiển thị văn bản cũng sử dụng được cho phông chữ đó. Để quay lại sử dụng phông chữ ban đầu, bạn chỉ cần gọi lệnh setFont() mà không có đối số:

display.setFont();

Nạp chương trình dưới đây vào module ESP8266.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/FreeSerif9pt7b.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println("SSD1306 allocation failed");
    for(;;);
  }
  delay(2000);

  display.setFont(&FreeSerif9pt7b);
  display.clearDisplay();
  display.setTextSize(1);             
  display.setTextColor(WHITE);        
  display.setCursor(0,20);             
  display.println("Dien Tu Viet");
  display.display();
  delay(2000); 
}
void loop() {
  
}

Bây giờ, trên màn hình sẽ hiển thị dòng chữ “Dien Tu Viet” với phông chữ FreeSerif.

Hiển thị Điện Tử Việt lên màn hình OLED

Vẽ hình dạng trên màn hình OLED

Thư viện OLED của Adafruit cung cấp các phương pháp hữu ích để vẽ pixel, đường thẳng và các hình dạng khác nhau. Hãy xem nhanh cách thực hiện các bạn nhé.

Vẽ một pixel

Để vẽ một pixel trên màn hình OLED, bạn có thể sử dụng lệnh drawPixel(x, y, color) với đối số là tọa độ x và y là nơi pixel xuất hiện và màu sắc. Ví dụ:

display.drawPixel(64, 32, WHITE);

Hiển thị 1 pixel lên OLED

Vẽ đường thẳng

Sử dụng lệnh drawLine(x1, y1, x2, y2, color) để tạo một đường thẳng. Tọa độ (x1, y1) cho biết điểm bắt đầu của dòng và tọa độ (x2, y2) cho biết vị trí kết thúc của đường thẳng. Ví dụ:

display.drawLine(0, 0, 127, 20, WHITE);

Vẽ đường thẳng trên OLED

Vẽ hình chữ nhật

Lệnh drawRect(x, y, width, height, color) cho phép dễ dàng vẽ một hình chữ nhật. Tọa độ (x, y) biểu thị góc trên cùng bên trái của hình chữ nhật. Sau đó, bạn cần chỉ định chiều rộng, chiều cao và màu sắc:

display.drawRect(10, 10, 50, 30, WHITE);

Bạn có thể sử dụng fillRect(x, y, width, height, color) để vẽ một hình chữ nhật tô đầy. Lệnh này có các đối số giống như drawRect().

Hình chữ nhật đầy

Thư viện cũng cung cấp các lệnh để hiển thị hình chữ nhật có góc bo tròn: drawRoundRect() và fillRoundRect(). Các lệnh này có các đối số giống như các lệnh trước cộng với bán kính của góc. Ví dụ:

display.drawRoundRect(10, 10, 30, 50, 2, WHITE);

Hình chữ nhật bo tròn

Hoặc một hình chữ nhật góc tròn, tô đầy:

Hoặc một hình chữ nhật tròn đầy

Vẽ hình tròn

Để vẽ hình tròn, sử dụng lệnh drawCircle(x, y,radius,color). Tọa độ (x,y) chỉ tâm của đường tròn. Bạn cũng nên chuyển bán kính làm đối số. Ví dụ:

display.drawCircle(64, 32, 10, WHITE);

Theo cách tương tự, để tạo một vòng tròn được lấp đầy, hãy sử dụng lệnh fillCircle() với cùng các đối số:

display.fillCircle(64, 32, 10, WHITE);

Vẽ hình tam giác

Sử dụng lệnh drawTriangle(x1, y1, x2, y2, x3, y3, color) để tạo một hình tam giác. Phương thức này có các đối số tọa độ của từng góc và màu sắc.

display.drawTriangle(10, 10, 55, 20, 5, 40, WHITE);

Sử dụng lệnh fillTriangle() để hiển thị một hình tam giác đầy.

display.fillTriangle(10, 10, 55, 20, 5, 40, WHITE);

Đảo ngược

Thư viện cung cấp một lệnh bổ sung mà bạn có thể sử dụng với hình dạng hoặc văn bản: phương thức invertDisplay(). Truyền true làm đối số để đảo ngược màu của màn hình hoặc false để quay lại màu gốc.

Nếu bạn gọi lệnh sau sau khi vẽ hình tam giác:

display.invertDisplay(true);

Bạn sẽ được một hình tam giác có màu ngược với màu nền như sau:

Hình tam giác đảo ngược

Chương trình vẽ hình dạng

Nạp chương trình sau đây để thực thi từng đoạn code mà tôi đã đề cập trước đó và xem qua tất cả các hình dạng.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000); // Tạm dừng 2 giây

  // Xóa bộ đệm
  display.clearDisplay();

  // Vẽ một pixel màu trắng
  display.drawPixel(64, 32, WHITE);
  display.display();
  delay(3000);

  // Vẽ một đường thẳng
  display.clearDisplay();
  display.drawLine(0, 0, 127, 20, WHITE);
  display.display();
  delay(3000);
  
  // Vẽ hình chữ nhật
  display.clearDisplay();
  display.drawRect(30, 10, 50, 30, WHITE);
  display.display();
  delay(3000);
  // Hình chữ nhật tô đầy
  display.fillRect(30, 10, 50, 30, WHITE);
  display.display();
  delay(3000);

  // Vẽ hình chữ nhật có góc bo tròn
  display.clearDisplay();
  display.drawRoundRect(10, 10, 30, 50, 2, WHITE);
  display.display();
  delay(3000);
  // Hình chữ nhật có góc bo tròn, tô đầy
  display.clearDisplay();
  display.fillRoundRect(10, 10, 30, 50, 2, WHITE);
  display.display();
  delay(3000);
  
  // Vẽ hình tròn
  display.clearDisplay();
  display.drawCircle(64, 32, 10, WHITE);
  display.display();
  delay(3000);
  // Hình tròn tô đầy
  display.fillCircle(64, 32, 10, WHITE);
  display.display();
  delay(3000);
  
  // Vẽ hình tam giác
  display.clearDisplay();
  display.drawTriangle(10, 10, 55, 20, 5, 40, WHITE);
  display.display();
  delay(3000);
  // Tam giác đầy
  display.fillTriangle(10, 10, 55, 20, 5, 40, WHITE);
  display.display();
  delay(3000);

  // Đảo ngược và khôi phục hiển thị
  display.invertDisplay(true);
  delay(3000);
  display.invertDisplay(false);
  delay(3000);
}

void loop() {
  
}

Hiển thị hình ảnh bitmap trên OLED

Bạn có thể hiển thị hình ảnh đơn sắc 128×64 bitmap trên màn hình OLED.

Trước tiên, hãy sử dụng một chương trình để thay đổi kích thước hình ảnh và lưu nó dưới dạng bitmap đơn sắc. Nếu bạn sử dụng máy tính Window, bạn có thể dùng ứng dụng Paint trong Window.

Sau đó, sử dụng một ứng dụng để chuyển đổi hình ảnh thành một mảng (array). Tôi đã sử dụng phần mềm LCD Image Converter để làm việc này.

Chạy chương trình và bắt đầu với một hình ảnh mới. Vào menu Image > Import và chọn ảnh bitmap mà bạn đã tạo trước đó.

 Phần mềm LCD Image Converter

Vào menu Options > Conversion và trong tab Prepare, chọn các tùy chọn như sau:

  • Type: Monochrome, Threshold Dither
  • Main Scan Direction: Top to Bottom
  • Line Scan Direction: Forward
Tùy chọn 1

Chuyển đến tab Image và chọn các tùy chọn như sau:

  • Split to rows
  • Block size: 8 bit
  • Byte order: Little-Endian
Tùy chọn 2

Then, click OK. Finally, in the main menu, go to File > Convert. A new file with .c extension should be saved. That file contains the C array for the image. Open that file with a text editor, and copy the array.

Sau đó, bấm OK. Cuối cùng, trong menu chính, vào File > Convert. Một tập tin mới có phần mở rộng .c sẽ được lưu. Tệp đó chứa mảng C cho hình ảnh. Mở tệp đó bằng trình soạn thảo văn bản và sao chép mảng.

Dưới đây là mảng mà chúng ta nhận được:

static const uint8_t image_data_Saraarray[1024] = {
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x14, 0x9e, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x6d, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0xfb, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x03, 0xd7, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x07, 0xef, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x0f, 0xdf, 0xff, 0x90, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x0f, 0xbf, 0xff, 0xd0, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x1d, 0x7f, 0xff, 0xd0, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x01, 0x1b, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x02, 0xa7, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xff, 0x80, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0x07, 0xff, 0xf8, 0xf8, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0e, 0x01, 0xff, 0xc0, 0x38, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0x46, 0xff, 0xb1, 0x18, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0x97, 0xff, 0xc0, 0x7a, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xbf, 0xff, 0xff, 0xff, 0xfe, 0x81, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xfc, 0x81, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xfd, 0x83, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0xbf, 0xff, 0xfe, 0xff, 0xfd, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xfb, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x3f, 0xff, 0xdc, 0xff, 0xfa, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xd8, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xd0, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x90, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x02, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xb0, 0x00, 0x0f, 0xf5, 0xff, 0xd7, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xb0, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x5f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xa0, 0x00, 0x0f, 0xfb, 0xff, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x0f, 0xfd, 0xff, 0xdf, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x07, 0xff, 0xff, 0xbf, 0xf0, 0x00, 0x0f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x87, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x43, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x60, 0x00, 0x01, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x73, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfe, 0xe0, 0x00, 0x00, 0xff, 0xff, 0xff, 0x80, 0x00, 0x7b, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfd, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x00, 0x33, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfd, 0xe0, 0x00, 0x00, 0x3f, 0xff, 0xf8, 0x00, 0x00, 0x27, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x67, 0xff, 0xe0, 0x00, 0x00, 0x1b, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfd, 0x40, 0x00, 0x00, 0xf3, 0xff, 0xc4, 0x00, 0x00, 0x0b, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfe, 0x80, 0x00, 0x00, 0xfc, 0xff, 0x8c, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x7f, 0x3c, 0x3c, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x3f, 0xc0, 0x7c, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff
};

Copy mảng này vào chương trình. Để hiển thị mảng, hãy sử dụng lệnh drawBitmap() với các đối số sau (x, y, mảng hình ảnh, chiều rộng hình ảnh, chiều cao hình ảnh, xoay). Các tọa độ (x, y) xác định nơi hình ảnh bắt đầu được hiển thị.

Copy chương trình bên dưới để hiển thị hình ảnh bitmap lên OLED.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

static const uint8_t image_data_Saraarray[1024] = {
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x14, 0x9e, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x6d, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0xfb, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x03, 0xd7, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x07, 0xef, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x0f, 0xdf, 0xff, 0x90, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x0f, 0xbf, 0xff, 0xd0, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x1d, 0x7f, 0xff, 0xd0, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x01, 0x1b, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x02, 0xa7, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xff, 0x80, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0x07, 0xff, 0xf8, 0xf8, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0e, 0x01, 0xff, 0xc0, 0x38, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0x46, 0xff, 0xb1, 0x18, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0x97, 0xff, 0xc0, 0x7a, 0x07, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xbf, 0xff, 0xff, 0xff, 0xfe, 0x81, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xfc, 0x81, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xfd, 0x83, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0xbf, 0xff, 0xfe, 0xff, 0xfd, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xfb, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x3f, 0xff, 0xdc, 0xff, 0xfa, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xd8, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x03, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xd0, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x90, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x02, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xb0, 0x00, 0x0f, 0xf5, 0xff, 0xd7, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xb0, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x5f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xa0, 0x00, 0x0f, 0xfb, 0xff, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x0f, 0xfd, 0xff, 0xdf, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x07, 0xff, 0xff, 0xbf, 0xf0, 0x00, 0x0f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x87, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x43, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x60, 0x00, 0x01, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x73, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfe, 0xe0, 0x00, 0x00, 0xff, 0xff, 0xff, 0x80, 0x00, 0x7b, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfd, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x00, 0x33, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfd, 0xe0, 0x00, 0x00, 0x3f, 0xff, 0xf8, 0x00, 0x00, 0x27, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x67, 0xff, 0xe0, 0x00, 0x00, 0x1b, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfd, 0x40, 0x00, 0x00, 0xf3, 0xff, 0xc4, 0x00, 0x00, 0x0b, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfe, 0x80, 0x00, 0x00, 0xfc, 0xff, 0x8c, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x7f, 0x3c, 0x3c, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x3f, 0xc0, 0x7c, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 
    0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff
};
 
void setup() {
  Serial.begin(115200);
 
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000); 
 
  display.clearDisplay();
  
  // Hiển thị bitmap lên OLED
  display.drawBitmap(0, 0, image_data_Saraarray, 128, 64, 1);
  display.display();
}
 
void loop() {
  
}

Sau khi nạp chương trình này vào ESP8266, đây là hình ảnh bitmap chúng ta thấy được trên màn hình.

Hình bitmap hiển thị trên OLED

Xử lý sự cố

Nếu bạn gặp lỗi “SSD1306 allocation failed” hoặc nếu OLED không hiển thị bất cứ thứ gì trên màn hình, thì đó có thể là một trong những sự cố sau:

Sai địa chỉ I2C

Địa chỉ I2C của màn hình OLED mà tôi dùng trong bài viết này là 0x3C. Tuy nhiên, màn hình OLED của bạn có thể có địa chỉ khác. Vì vậy, hãy đảm bảo bạn xác định đúng địa chỉ I2C của màn hình OLED.

SDA và SCL không được kết nối đúng cách

Hãy kiểm tra lại và đảm bảo rằng bạn đã nối dây đúng các chân SDA và SCL của màn hình OLED. Trong trường hợp sử dụng ESP8266 NodeMCU, hãy kết nối chân SDA với GPIO 4 (D2) và chân SCL với GPIO 5 (D1).

Lời kết

Qua bài viết này, tôi đã hướng dẫn cho các bạn cách giao tiếp giữa màn hình OLED SSD1306 0.96 inch với ESP8266 NodeMCU, làm thế nào để hiển thị văn bản, thiết lập các phông chữ khác nhau, vẽ hình dạng và hiển thị hình ảnh bitmap lên màn hình OLED. Nếu bạn gặp khó khăn trong quá trình tìm hiểu thì vui lòng để lại tin nhắn ở phần bình luận. Chúc các bạn thành công!

LEAVE A REPLY

Please enter your comment!
Please enter your name here