กลับไปหน้ารวมไฟล์
pixel-art-on-oled-display-b8c88b.md

ชอบ Pixel Art มาก และพอมาใช้กับจอ OLED แล้วยิ่งเจ๋งเข้าไปใหญ่!

อาจจะดูยากตอนเริ่มวาดบนจอแบบนี้ แต่ฝึกไปซักพักน้องก็ทำโลโก้เองได้ แถมทำอนิเมชั่นได้ด้วยนะ!

เลือกภาพที่จะใช้

เราจะใช้ จอขนาด 128x32 (จอ 128x64 ก็มีเยอะนะ ถ้าอยากได้พื้นที่เยอะๆ) เพราะฉะนั้นเลือกภาพที่ เล็กๆหน่อย มาลองดีกว่า

สไปรท์จากเกมบอยนี่แหละใช้ง่ายสุด แต่จำไว้ว่าเราจำกัดแค่ ขาว-ดำ ในขณะที่เกมบอยแสดง สีเทาได้ 4 ระดับ

Super Mario Land Sprites

แปลงภาพเป็น Pixel Art

ตัวอย่างแรก เราจะใช้โลโก้ของ hackster.io

Hackster.io logo

สิ่งที่ต้องทำคือ:

  • ปรับขนาดภาพเป็น 128x32
  • วาดภาพใหม่ให้ใช้แค่สองสี
  • แปลงเป็นไฟล์ XBM (โค้ด Arduino)

จะใช้โปรแกรมอะไรวาดก็ได้ แต่พี่แนะนำ Krita

  • ปรับขนาดภาพให้ใกล้เคียงกับหน้าจอ (128x32) : Image --> Scale to New Size
  • ปรับขนาดแคนวาส : Image --> Resize Canvas

Delicious pixels !

ใน brush presets เลือก Pixel Art

Pixel Art brush preset

แล้วก็ลงมือวาดภาพใหม่เลย:

สีขาวจะหมายถึงไม่มีสี (ดำ) และ สีดำจะหมายถึงมีสี (น้ำเงิน/ขาว ขึ้นอยู่กับจอ)

กด X เพื่อสลับสีใน Krita

Redrawn pixel art logo มันถึงได้ชื่อว่า Pixel Art ไงล่ะ น้องอาจต้องลองผิดลองถูกซักพักกว่าจะได้ผลลัพธ์ที่พอใจ!

แปลง Pixel Art เป็นโค้ด Arduino

  • บันทึกภาพเป็น XBM
  • บันทึกเป็น PNG ด้วย เพราะไฟล์ XBM แก้ไขไม่ได้!

เปิดไฟล์ xbm ด้วยโปรแกรมแก้ไขข้อความ แล้วเปลี่ยนชื่อตัวแปร

ไฟล์ต้นฉบับ

#define _width 128
#define _height 32
static char _bits[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00... };

ไฟล์ที่พร้อมใช้

#define logo_width 128
#define logo_height 32
static const unsigned char logo[] U8X8_PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00... };

แสดงโลโก้บนจอ

แนะนำให้ เก็บโลโก้ไว้ในไฟล์ .h อีกไฟล์ จะได้อ่านโค้ดหลักง่ายๆ

เราจะใช้ ไลบรารี่ U8g2 (รองรับจอได้เพียบ)

#include <Wire.h> //I2C
#include <U8g2lib.h>
#include "logo.h"
//I2C SSD1306 128x32 (search U8g2 examples for other display)
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
void setup() {
 u8g2.begin(); //Start Screen
 drawLogo();
}
void loop() {
}
void drawLogo() {
 u8g2.firstPage();
 do {
   u8g2.drawXBMP(0, 0, logo_width, logo_height, logo); 
 } while ( u8g2.nextPage() );
}

พี่เก็บไฟล์ png กับ xbm ไว้ในโฟลเดอร์ res/ ในโปรเจคด้วย จะได้จำได้ว่าไฟล์ logo.h นี่วาดรูปอะไรไว้

Project folder structure

อัพโหลดโค้ด แล้วก็จัดไปวัยรุ่น!

Final logo on OLED screen

ข้อมูลเทคนิคแบบจัดเต็ม: การแปลงภาพมาตรฐานให้ Arduino ใช้ได้

จอ OLED แบบ I2C (อย่าง SSD1306) น่ะ มีความละเอียด 128x64 พิกเซล เท่ากับมี LED เล็กๆ ถึง 8,192 ดวงที่เราต้องสั่งมันให้ทำงานเป๊ะๆ Arduino น่ะมันทางคณิตศาสตร์แล้วส่งไฟล์ภาพตรงๆ ไม่ได้หรอก มันส่งได้แค่ "อาร์เรย์เลขฐานสิบหก (hex array)" เท่านั้น

กระบวนการก็คือการแปลงงานกราฟิกของเราให้กลายเป็นภาษาที่ไมโครคอนโทรลเลอร์เข้าใจได้:

  1. วาดโลโก้หรือรูปของเราบนพื้นหลังขาวดำสนิท ขนาดเท่ากับจอเป๊ะๆ (เช่น 128x32 หรือ 128x64)
  2. บันทึกภาพเป็นไฟล์ .BMP หรือ .XBM
  3. ใช้เครื่องมือแปลงภาพ (เช่น LCD Assistant, Image2CPP ออนไลน์ หรือการ Export เป็น XBM ใน Krita ตามที่เห็นด้านบน) เพื่อให้มันวิเคราะห์รูปแล้วสร้างอาร์เรย์ยักษ์ของข้อมูลไบต์เลขฐานสิบหกออกมา
  4. อาร์เรย์นี้จะถูกเก็บไว้ในหน่วยความจำ Flash ของ Arduino โดยใช้ PROGMEM (หรือ U8X8_PROGMEM ถ้าใช้ U8g2) เพื่อประหยัด RAM อันมีค่าของเรา

การส่งข้อมูลไปยังบัฟเฟอร์ของจอ

เราจะโหลดอาร์เรย์นี้เข้าไปเก็บใน Flash ของ Arduino

  1. ไลบรารีกราฟิก (เช่น <U8g2lib.h> หรือ <Adafruit_SSD1306.h>) จะเริ่มต้นการทำงานของจอบนขา I2C (SDA/SCL)
  2. ฟังก์ชันอย่าง u8g2.firstPage() จะเริ่มลูปการวาด หรือ display.clearDisplay(); จะลบกราฟิกเก่าทิ้ง
  3. คำสั่งอย่าง u8g2.drawXBMP(x, y, width, height, bitmap); จะบอกไลบรารีให้วาดข้อมูลภาพที่เราเก็บไว้ที่ตำแหน่งไหน
  4. Arduino จะดึงข้อมูลจาก Flash memory ส่งผ่านสาย I2C และจอก็จะแสดงผลงานศิลป์ของเราออกมาได้อย่างคมชัด แม่นยำทางคณิตศาสตร์เป๊ะเวอร์!
  5. u8g2.nextPage() หรือ display.display(); จะเป็นขั้นตอนสุดท้าย บังคับให้จออัพเดทบัฟเฟอร์ภายในและแสดงภาพใหม่ออกมา

อนิเมชั่น - ตำแหน่ง

เราสามารถทำให้พิกเซลอาร์ตของเราเคลื่อนไหวได้สองวิธีหลักๆ:

  • เปลี่ยนตำแหน่งมัน
  • ใช้หลายๆ ภาพ

ถ้าเราอยากให้สไปรท์ของเราเดินจากซ้ายไปขวา เราก็แค่เปลี่ยน ค่า x position ไปทางขวา ใช่มั้ย?

แผนภาพตำแหน่งอนิเมชั่น

ถ้าใช้ Arduino Uno มันก็จะทำงานนะ แต่ถ้าใช้บอร์ดที่แรงกว่านี้ (เช่น Arduino MKR1010) อนิเมชั่นของน้อง จะเร็วเกินไปจนมองไม่เห็นเลย!

เราต้องจำกัดอัตราการรีเฟรชของอนิเมชั่น แน่นอนว่าเราใช้ delay ง่ายๆ ก็ได้ แต่มันจะทำให้เราทำงานอื่นพร้อมกันไม่ได้

ดังนั้นเราจะใช้ไลบรารี AsyncDelay เพื่อจัดการเรื่องนี้อย่างมีประสิทธิภาพ

#include <AsyncDelay.h>
const int FPS = 1000 / 30; //30 FPS
AsyncDelay tick;
void setup(){
  tick.start(FPS, AsyncDelay::MILLIS);
}
void loop(){
  if(tick.isExpired()){
    //ANIMATION HERE
    tick.repeat();
  }
}

สุดท้ายแล้ว เราอยากให้ภาพของเราออกไปนอกจอทางขวา แล้วค่อยๆ โผล่มาจากทางซ้าย

เราก็แค่รีเซ็ตตำแหน่งเมื่อมันเกินความกว้างของ OLED (128) ไปเท่ากับความกว้างของภาพ (128+36)

แผนภาพรีเซ็ตตำแหน่งอนิเมชั่น

และตั้งค่าเริ่มต้นตำแหน่งเป็น -36 เพื่อให้ภาพดูเหมือนโผล่มาจากทางซ้าย

แผนภาพตำแหน่งเริ่มต้นอนิเมชั่น

void loop() {
 if (tick.isExpired()) {
   drawAnimation();
   pos_x = pos_x + X;
   if (pos_x > OLED_WIDTH + cannon_width) {
     pos_x = -cannon_width;
   }
   tick.repeat();
 }
}

และนี่คือผลลัพธ์! ดูคลิปเลย

Animation - ใช้หลายรูปภาพ

อีกวิธีทำอนิเมชั่นคือใช้หลายรูปภาพแทน

แทนที่จะเปลี่ยนตำแหน่ง X เราใช้ switch condition และเพิ่มค่า animation_state แทน

void drawAnimation() {
 u8g2.firstPage();
 do {
   switch (animation_state) {
     case 0:
       u8g2.drawXBMP(0, 0, logo_width, logo_height, logo_1);
       break;
     case 1:
       u8g2.drawXBMP(0, 0, logo_width, logo_height, logo_2);
       break;
     case 2:
       u8g2.drawXBMP(0, 0, logo_width, logo_height, logo_3);
       break;
     case 3:
       u8g2.drawXBMP(0, 0, logo_width, logo_height, logo_4);
       break;
     case 4:
       u8g2.drawXBMP(0, 0, logo_width, logo_height, logo_5);
       break;
     case 5:
       u8g2.drawXBMP(0, 0, logo_width, logo_height, logo_6);
       break;
   }
 } while ( u8g2.nextPage() );
}

แต่พี่อยากให้อนิเมชั่นมันย้อนกลับได้ด้วย เลยใช้ boolean (animation_direction) มาควบคุมให้มันเดินหน้า-ถอยหลังได้

drawAnimation();
   if (animation_direction) {
     animation_state--;
   } else {
     animation_state++;
   }
   if (animation_state == 5) {
     animation_direction = true;
   }
   if (animation_state == 0) {
     animation_direction = false;
   }

ผลลัพธ์เป็นยังไง ดูเอาเลย:

ถ้าน้องเอาเทคนิคสองอันนี้มาผสมกันล่ะก็ อนิเมชั่นเทพๆ ก็ทำได้แล้วจ้า:

อุปกรณ์ที่ต้องใช้ (จัดไปวัยรุ่น)

  • Arduino Uno/Nano (ใช้ I2C นะ)
  • จอ OLED 0.96” หรือ 1.3” แบบ SSD1306 / SH1106 I2C
  • คอมสักเครื่องไว้รันโปรแกรมแปลงรูป (เช่น Krita หรือ LCD Assistant)

หวังว่าบทเรียนนี้จะมีประโยชน์ แล้วน้องจะสนุกกับการเล่นกับจอ OLED เหมือนพี่นะ สู้งานนะน้อง!

ถ้าน้องวาด pixel art อะไรเจ๋งๆ ออกมา อย่าลืมเอามาอวดกันบ้างล่ะ

ข้อมูล Frontmatter ดั้งเดิม

apps:
  - "1x Arduino Web Editor"
  - "1x Arduino IDE"
  - "1x Krita"
author: "usini"
category: "Screens & Displays"
components:
  - "1x Monochrome 128x32 I2C OLED graphic display"
  - "1x Arduino UNO"
description: "มาสร้างภาพ Pixel Art สไตล์เรโทรสุดชิคบนจอ OLED กัน! เราจะสอนให้วาดภาพเองได้ แล้วยังทำให้มันขยับได้แบบอนิเมชั่นเทพๆ อีกด้วย จัดไป!"
difficulty: "Intermediate"
documentationLinks: []
downloadableFiles:
  - "https://create.arduino.cc/editor/madnerd/f616433a-3f8a-483c-8ca2-6447dac0314c/preview"
  - "https://create.arduino.cc/editor/madnerd/7deed5b1-6f4d-4cd6-b662-0e2a88a93974/preview"
  - "https://create.arduino.cc/editor/madnerd/b4c53d85-5579-4953-89d4-29e0a3b4228c/preview"
  - "https://create.arduino.cc/editor/madnerd/0a94bf35-ada8-4695-9f56-d62ea2ed4cff/preview"
  - "https://create.arduino.cc/editor/madnerd/f616433a-3f8a-483c-8ca2-6447dac0314c/preview"
  - "https://create.arduino.cc/editor/madnerd/7deed5b1-6f4d-4cd6-b662-0e2a88a93974/preview"
  - "https://create.arduino.cc/editor/madnerd/b4c53d85-5579-4953-89d4-29e0a3b4228c/preview"
  - "https://create.arduino.cc/editor/madnerd/0a94bf35-ada8-4695-9f56-d62ea2ed4cff/preview"
  - "https://github.com/maditnerd/oled_xbm"
encryptedPayload: "U2FsdGVkX194HbNVvGPaiuJCr/kwYWSm/AhRPpPtizTE1fTHkeAZ0c+qU5/ksy+rzbTfiJYvbcPAnE2kX3LmyAnfZ8GSIMGdBpwWEknbo+qcXqauH/y1fFu4p/NkCC9NH1WFlDUQqSLgo61NjuyfNQ=="
heroImage: "https://cdn.jsdelivr.net/gh/bigboxthailand/arduino-assets@main/images/projects/pixel-art-on-oled-display-b8c88b_cover.jpg"
lang: "en"
likes: 14
passwordHash: "c383dfb0f80a38b12a06bd2a4a05ad5d9836b239d0abc1ff3c1817c66c5f4192"
price: 299
seoDescription: "Learn how to create and animate Pixel Art on an OLED Display for a retro look using Arduino. Step-by-step guide for makers."
tags:
  - "display"
  - "oled"
  - "image"
  - "pixelart"
  - "xbm"
  - "animation"
title: "วาด Pixel Art บนจอ OLED งานง่ายแต่หล่อ ดูตึงๆ วัยรุ่น!"
tools: []
videoLinks:
  - "https://www.youtube.com/embed/JfFld6lq_7o"
  - "https://www.youtube.com/embed/1zds44isPXk"
  - "https://www.youtube.com/embed/DALdShW_gdQ"
  - "https://www.youtube.com/embed/iB52FujIK-A"
views: 56985