Raspberry Pi + OLED แสดง IP และสถานะเครือข่ายแบบ Headless

Raspberry Pi ที่ทำงานแบบ Headless อาจรันระบบอยู่ได้ทั้งวันโดยไม่ต้องต่อ Monitor แต่ปัญหาคือเวลาเครื่องเปลี่ยน IP จาก DHCP หรือหลุดจาก Network เราอาจต้องเข้า Router หรือเริ่มไล่เช็กก่อน ถึงจะรู้ว่าเครื่องอยู่ที่ไหน

โปรเจกต์นี้แก้ Pain Point แบบตรงไปตรงมา: เพิ่ม OLED I²C ขนาด 0.91 นิ้ว ให้ Raspberry Pi 3B แล้วใช้ Python แสดงสถานะ ONLINE / OFFLINE, IP Address และ Local Time ไว้บนตัวเครื่องตลอดเวลา

จุดเด่นไม่ใช่แค่มีจอ: เมื่อ Script ถูกตั้งให้เริ่มผ่าน systemd Raspberry Pi สามารถ Boot แล้วเปิด Network Status Display ได้เองโดยไม่ต้อง Login หรือเปิดโปรแกรมด้วยมือทุกครั้ง

Raspberry Pi ในชุดทดลอง OLED network status display
Raspberry Pi ใน Test Setup ที่เพิ่ม OLED ขนาดเล็ก เพื่อดู Network Status โดยไม่ต้องต่อ Monitor ตลอดเวลา

Key Takeaways

  • Source ใช้ Raspberry Pi 3B เป็น Test Platform
  • Display เป็น OLED I²C ขนาด 0.91 นิ้ว ความละเอียด 128×32 pixels
  • จอเป็น White Monochrome
  • OLED เชื่อมกับ Raspberry Pi ด้วย 4 เส้น
  • Power ใช้ Pin 1 — 3.3V และ Pin 6 — GND
  • SDA ใช้ Pin 3 / GPIO2
  • SCL ใช้ Pin 5 / GPIO3
  • Display ใน Setup นี้ใช้ I²C Address 0x3C
  • ตรวจ Address ด้วย i2cdetect -y 1
  • Python Code ใช้ luma.oled + Pillow
  • Device Class ที่ Script ใช้คือ ssd1306
  • หน้าจอที่หนึ่งแสดง ONLINE หรือ OFFLINE
  • หน้าจอที่สองแสดง IP Address และ Local Time
  • สองหน้าสลับทุก 2 วินาที
  • Network Check ใช้ Timeout 1.5 วินาที
  • สถานะ ONLINE ใน Code มาจากการเชื่อมไป 8.8.8.8:53 สำเร็จ
  • ถ้าหา IP ไม่ได้ จอจะแสดง IP: ---
  • Script รองรับ SIGTERM / SIGINT และ Clear Display ก่อนจบ
  • systemd Service ใช้ Restart=always
  • Service Example ระบุ User เป็น raspberry

ทำไม Headless Raspberry Pi ควรมีจอสถานะเล็ก ๆ?

Raspberry Pi สามารถทำงานได้โดยไม่ต้องมี Monitor, Keyboard และ Mouse ต่ออยู่ตลอดเวลา

ในงาน IoT, Prototype หรือระบบที่วางทิ้งไว้บน Network วิธีนี้ช่วยลดอุปกรณ์รอบตัว และทำให้ Pi ทำหน้าที่เหมือน Device หนึ่งตัว มากกว่าคอมพิวเตอร์ Desktop

แต่เมื่อ Network ใช้ DHCP IP Address สามารถเปลี่ยนได้ และถ้าต้อง SSH เข้าเครื่อง คำถามแรกมักกลายเป็น:

“ตอนนี้ Pi ได้ IP อะไร?”

OLED เล็ก ๆ จึงทำหน้าที่เหมือน Local Status Panel: ไม่ต้อง Login ก่อน ก็เห็นได้ว่า Script มองว่าเครื่อง Online หรือ Offline และ IP ที่ตรวจพบตอนนั้นคืออะไร

Hardware ที่ใช้ใน Project

รายการ ข้อมูลที่ Source ระบุ
Single-board Computer Raspberry Pi 3B
Power Supply micro-USB Power Supply 2.5A
Display 0.91" I²C OLED
Resolution 128×32 pixels
Display Color White Monochrome
Jumper Wires Female-Female Dupont ×4
Remote Terminal PuTTY SSH Client

Source ระบุ Power Supply เป็น 2.5A แต่ไม่ได้ให้รายละเอียด Power ทั้งชุดเพิ่มเติมในบทความ: บทความนี้จึงไม่สร้างค่า Power Consumption, OLED Current หรือกำลังไฟรวมของ Project เพิ่มเอง

เพิ่ม OLED 0.91 นิ้วให้ Raspberry Pi

OLED 0.91 นิ้วที่ใช้แสดง network status บน Raspberry Pi
OLED I²C 128×32 ถูกเพิ่มเข้า Raspberry Pi เพื่อแสดงสถานะเครือข่าย IP Address และเวลา

Display ขนาด 128×32 มีพื้นที่ไม่มาก ดังนั้น Creator ไม่พยายามยัดข้อมูลทั้งหมด ลงหน้าเดียว

แต่ใช้แนวคิด 2 Pages แล้วสลับกัน

Page 1
ONLINE / OFFLINE

↓ ทุก 2 วินาที ↓

Page 2
IP Address + Local Time
ภาพเพิ่มเติมของ Raspberry Pi และ OLED network status display
ภาพเพิ่มเติมจาก Test Setup ของ Raspberry Pi และ OLED Network Status Display

ต่อ OLED ผ่าน I²C เพียง 4 เส้น

Raspberry Pi pin configuration สำหรับต่อ OLED ผ่าน I2C
Pin Mapping ที่ Source ใช้: Power 3.3V, GND, SDA GPIO2 และ SCL GPIO3
หน้าที่ Raspberry Pi Pin GPIO / Signal
OLED Power Pin 1 3.3V
Ground Pin 6 GND
I²C Data Pin 3 GPIO2 / SDA
I²C Clock Pin 5 GPIO3 / SCL

บทความต้นฉบับใช้ OLED Power ที่ 3.3V: Source ไม่ได้ให้ Wiring แบบ 5V, Pull-up Resistor, Level Shifter หรือ I²C Bus Speed จึงไม่มีการเพิ่มวงจรเหล่านั้นเอง

ตรวจว่า OLED ถูกพบที่ Address 0x3C

หลังต่อสาย Creator ใช้:

ตรวจ I²C Address
i2cdetect -y 1

Display ใน Setup ของ Creator ถูกพบที่:

0x3C

0x3C เป็น Address ของ Display ใน Project นี้: ไม่ควรสมมติว่า OLED I²C ทุก Module จะใช้ Address เดียวกัน

Source ไม่ได้ให้ขั้นตอนเปิด I²C หรือ Install i2c-tools: ดังนั้นบทความนี้ไม่สร้างคำสั่ง raspi-config, apt หรือขั้นตอน Package Installation เพิ่มเอง

Python Stack: luma.oled + Pillow + SSD1306

Creator ใช้ luma.oled เป็น Python Interface สำหรับคุยกับ OLED และใช้ Pillow ในการสร้าง Image, วาด Text และจัด Layout ก่อนส่งขึ้น Display

Import ที่อยู่ใน Source Code คือ:

Libraries ที่ Script ใช้
from PIL import Image, ImageDraw, ImageFont
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306

Library รองรับหลาย Display ไม่ได้หมายความว่า Script นี้รองรับทุกตัวโดยไม่แก้: Source Code instantiate ssd1306 โดยตรง ดังนั้น Current Script ถูกเขียนสำหรับ SSD1306 ตาม Source

Source ไม่ได้ให้ Python Version, Raspberry Pi OS Version, luma.oled Version, Pillow Version หรือ Package Installation Commands บทความนี้จึงไม่สร้าง Version และคำสั่งติดตั้งขึ้นเอง

ONLINE / OFFLINE ใน Script ถูกตรวจอย่างไร?

จุดนี้ควรอ่านจาก Code มากกว่าดูคำว่า “Network Status” อย่างเดียว

Function:

get_ip_and_online(timeout=1.5)

แยกงานออกเป็นสองส่วน

1. หา IP Address ที่ระบบกำลังใช้

Script สร้าง UDP Socket และเรียก:

หา Local Source IP
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]

IP ที่นำไปแสดง คือ Source Address ที่ Socket ได้จาก Route ไปยังปลายทางดังกล่าว

2. ตรวจสถานะ Online

Code ทดลองเปิด Connection ไป:

8.8.8.8:53

Online Check
socket.create_connection(
    ("8.8.8.8", 53),
    timeout=timeout
).close()

online = True

Default Timeout คือ:

1.5 วินาที

คำว่า ONLINE ใน Project นี้มี Definition เฉพาะ: มันหมายถึง Script สามารถเปิด Connection ไปยัง 8.8.8.8:53 ภายใน Timeout ได้ ไม่ใช่การพิสูจน์ว่า Internet Service ทุกปลายทาง หรือ Network ทุกส่วนทำงานปกติ

OLED แสดงอะไรบ้าง?

หน้า 1 — ONLINE / OFFLINE

หน้าแรกใช้ Font ขนาดใหญ่ และจัด Text ไว้กึ่งกลางจอ

Script เลือกข้อความ:

"ONLINE" if online else "OFFLINE"

หน้า 2 — IP + Local Time

หน้าที่สองแสดงสองบรรทัด:

IP: xxx.xxx.xxx.xxx
HH:MM:SS

ถ้า Script ไม่สามารถหา IP ได้ จะแสดง:

IP: ---

Page Timing

Source กำหนด:

PAGE_TIME = 2.0

และใช้:

page ^= 1

เพื่อสลับระหว่าง Page 0 และ Page 1 ทุก 2 วินาที

Font

Script พยายามโหลด:

/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf

โดยใช้:

  • Font ใหญ่: 18
  • Font เล็ก: 12

ถ้าโหลดไม่ได้ จะ Fallback ไป:

ImageFont.load_default()

Python Code ฉบับเต็มจาก Source

Script รวมทั้ง:

  • I²C Initialization
  • SSD1306 Device
  • Network Check
  • IP Detection
  • Font Loading
  • Page Switching
  • SIGTERM / SIGINT Handling
  • Display Clear ตอนจบ
net_oled.py
#!/usr/bin/env python3
"""
Netwerkstatus OLED-display voor Raspberry Pi
Geschikt voor SSD1306 128×32 (0x3C)
Toont ONLINE/OFFLINE en IP-adres met tijd
"""

import time, socket, signal, sys
from datetime import datetime
from PIL import Image, ImageDraw, ImageFont
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306


I2C_ADDR = 0x3C          # jouw displayadres
PAGE_TIME = 2.0          # seconden per pagina-wissel


def get_ip_and_online(timeout=1.5):
    """Geef (ip, online_bool)."""
    ip, online = None, False
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.settimeout(timeout)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
    except Exception:
        pass
    try:
        socket.create_connection(("8.8.8.8", 53), timeout=timeout).close()
        online = True
    except Exception:
        pass
    return ip, online


def load_font(size):
    try:
        return ImageFont.truetype(
            "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", size
        )
    except Exception:
        return ImageFont.load_default()


def draw_centered(draw, text, font, w, h):
    tw, th = draw.textbbox((0, 0), text, font=font)[2:]
    draw.text(((w - tw) // 2, (h - th) // 2), text, font=font, fill=255)


def main():
    serial = i2c(port=1, address=I2C_ADDR)
    device = ssd1306(serial)
    W, H = device.width, device.height
    font_big = load_font(18)
    font_small = load_font(12)

    stop = {"flag": False}
    signal.signal(signal.SIGTERM, lambda *_: stop.update(flag=True))
    signal.signal(signal.SIGINT, lambda *_: stop.update(flag=True))

    page = 0
    while not stop["flag"]:
        ip, online = get_ip_and_online()
        img = Image.new("1", (W, H), 0)
        d = ImageDraw.Draw(img)

        if page == 0:
            draw_centered(d, "ONLINE" if online else "OFFLINE", font_big, W, H)
        else:
            d.text((0, 2), f"IP: {ip or '---'}", font=font_small, fill=255)
            d.text((0, 18), datetime.now().strftime("%H:%M:%S"), font=font_small, fill=255)

        device.display(img)
        time.sleep(PAGE_TIME)
        page ^= 1

    device.clear()


if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"net_oled error: {e}", file=sys.stderr)
        try:
            ssd1306(i2c(port=1, address=I2C_ADDR)).clear()
        except Exception:
            pass
        sys.exit(1)

Graceful Shutdown

Script ฟัง:

  • SIGTERM
  • SIGINT

เมื่อได้รับ Signal จะออกจาก Loop แล้วเรียก:

device.clear()

ถ้าเกิด Exception ที่ระดับ Main Code ก็พยายามสร้าง SSD1306 Device และ Clear จออีกครั้งก่อน Exit

ทำให้ OLED Status Script เริ่มเองหลัง Boot ด้วย systemd

Raspberry Pi OLED network status display ในช่วงตั้งค่า auto-start
ช่วงท้ายของ Project Script ถูกตั้งให้ทำงานแบบ Autonomous ผ่าน systemd เพื่อให้ OLED กลับมาทำงานหลัง Reboot

Source เก็บ Python Script ไว้ที่:

/usr/local/bin/net_oled.py

แล้วทำให้ไฟล์ Executable ด้วย:

ทำ Script ให้ Executable
sudo chmod +x /usr/local/bin/net_oled.py

Service File อยู่ที่:

/etc/systemd/system/net-oled.service

net-oled.service
[Unit]
Description=OLED Netwerkstatus Display
After=network-online.target

[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/net_oled.py
Restart=always
User=raspberry
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target

Service File ทำอะไร?

Setting ค่าจาก Source
Start Order After=network-online.target
Python Command /usr/bin/python3 /usr/local/bin/net_oled.py
Restart Policy Restart=always
User raspberry
Environment PYTHONUNBUFFERED=1
Install Target multi-user.target

Service Example ผูกกับ User ชื่อ raspberry: ถ้า Raspberry Pi ใช้ Username อื่น Source ไม่ได้ให้ Service File สำหรับ Username นั้นโดยตรง จึงควรตรวจค่าก่อนนำ Example ไปใช้

Source กล่าวถึง daemon-reload และ enable --now หลังสร้าง Service แต่ข้อความต้นฉบับที่ใช้บทความนี้ ไม่ได้ให้ Shell Commands แบบเต็ม จึงไม่มีการสร้าง Command เพิ่มเอง

ภาพเพิ่มเติมของ Raspberry Pi OLED network status setup
ภาพเพิ่มเติมจากช่วง Final Setup ของ OLED Network Status Project
ภาพผลลัพธ์เพิ่มเติมของ Raspberry Pi OLED network status display
ภาพผลลัพธ์เพิ่มเติม หลังจัดให้ Script ทำงานบน Raspberry Pi แบบ Autonomous

ดู OLED Network Status ทำงานจริง

หลังตั้งค่า Script และ systemd จอจะสลับข้อมูล Network Status, IP Address และ Local Time ตาม Logic ใน Python Code

สิ่งที่ Source ยังไม่ได้ระบุ

Source ระบุ Source ไม่ได้ระบุ
Raspberry Pi 3B Compatibility Test กับ Pi 4 / Pi 5 / Pi Zero
OLED 0.91" 128×32 ผู้ผลิต / Part Number ของ Module
I²C Address 0x3C ใน Test Setup Address ของ OLED ทุก Module
SSD1306 Device Class ใน Python Code สำหรับ SH1106
Python + luma.oled + Pillow Version ของ Software Packages
i2cdetect -y 1 ขั้นตอน Install i2c-tools
I²C Wiring Bus Speed / Pull-up Values / Cable Length
2.5A Power Supply System Power Consumption
ONLINE Check ผ่าน 8.8.8.8:53 Multi-endpoint Network Health Check
IP จาก Route ที่ Script ใช้ การแสดง IP ของทุก Network Interface
User=raspberry ใน systemd Service Config สำหรับ Username อื่น

Maker เรียนรู้อะไรจาก Project นี้?

1. Headless ไม่ได้แปลว่าต้องมองไม่เห็นสถานะ

Raspberry Pi ไม่จำเป็นต้องต่อ Monitor ตลอด แต่ข้อมูลสำคัญอย่าง IP สามารถถูกดึงออกมาแสดงบน Local OLED ได้โดยตรง

2. จอเล็กควรเลือกข้อมูลให้เหมาะกับพื้นที่

128×32 pixels ไม่ได้มีพื้นที่มาก Creator จึงใช้สองหน้า แทนการย่อทุกอย่าง ให้ลงจอเดียว

3. นิยามคำว่า ONLINE ให้ชัด

ใน UI เราเห็นคำว่า ONLINE แต่สิ่งสำคัญสำหรับคนทำระบบคือ ต้องรู้ว่า Code ใช้วิธีไหนตัดสินสถานะนั้น

Project นี้นิยามจาก Connection ไป 8.8.8.8:53 ไม่ใช่ Network Diagnostic ทุกประเภท

4. systemd ทำให้ Prototype เริ่มกลายเป็น Appliance

เมื่อ Script ไม่ต้องรอให้ผู้ใช้ SSH เข้าไป Run ทุกครั้งหลัง Boot Raspberry Pi ก็เข้าใกล้การทำงานแบบ Standalone Device มากขึ้น

เหมาะกับ Project แบบไหน? แนวคิดเดียวกันสามารถเป็น Inspiration สำหรับ Raspberry Pi ที่ต้องวางทำงานแบบ Headless และต้องการ Local Status Display แต่ Feature เพิ่มเติม เช่น Sensor, CPU Temperature, Storage หรือ Service Health ไม่ได้อยู่ใน Source นี้ จึงไม่มีการสร้าง Code เพิ่มเองในบทความ

โปรเจกต์นี้มีหลายขั้น แนะนำอ่านต้นฉบับควบคู่กัน

ถ้าจะ Build ตาม แนะนำอ่าน Main Source เพื่อดูรูป Wiring, Test Setup และ Context ของ Python Script ควบคู่กับ Code ในบทความนี้

FAQ: Raspberry Pi OLED Network Status

Project นี้ใช้ Raspberry Pi รุ่นอะไร?

Source ใช้ Raspberry Pi 3B เป็น Test Platform

OLED มีขนาดเท่าไร?

0.91 นิ้ว ตาม Hardware List ของ Source

OLED Resolution เท่าไร?

128×32 pixels และเป็น White Monochrome Display

OLED ใช้ I²C หรือ SPI?

Project นี้ใช้ I²C

OLED ต่อ Pin ไหนบน Raspberry Pi?

Source ใช้ Pin 1 สำหรับ 3.3V, Pin 6 สำหรับ GND, Pin 3 / GPIO2 สำหรับ SDA และ Pin 5 / GPIO3 สำหรับ SCL

OLED ทุกตัวใช้ Address 0x3C หรือไม่?

Source ไม่ได้ระบุเช่นนั้น Display ที่ใช้ใน Project นี้ ถูกตรวจพบที่ 0x3C

ตรวจ I²C Address อย่างไร?

Source ใช้ i2cdetect -y 1 แล้วตรวจว่า Display ปรากฏที่ 0x3C

Python ใช้ Library อะไร?

ใช้ luma.oled สำหรับ OLED Interface และ Pillow สำหรับสร้าง Image, Text และ Graphic Buffer

Code นี้ใช้ SSD1306 หรือ SH1106?

Current Code import และ instantiate ssd1306 โดยตรง Source ไม่ได้ให้ Code สำหรับเปลี่ยนไปใช้ SH1106

OLED แสดงข้อมูลอะไร?

หน้าแรกแสดง ONLINE หรือ OFFLINE ส่วนหน้าที่สองแสดง IP Address และ Local Time

หน้าจอสลับทุกกี่วินาที?

ทุก 2 วินาที ตามค่า PAGE_TIME = 2.0

ONLINE ถูกตรวจอย่างไร?

Script ทดลองเปิด TCP Connection ไปยัง 8.8.8.8:53 ภายใน Timeout 1.5 วินาที ถ้าสำเร็จจะตั้ง online = True

ถ้าหา IP ไม่ได้ จอแสดงอะไร?

Code ใช้ ip or '---' ดังนั้นหน้าจอจะแสดง IP: ---

Script เริ่มเองหลัง Reboot ได้ไหม?

ได้ตาม Project นี้ โดย Source สร้าง systemd Service สำหรับเรียก /usr/local/bin/net_oled.py หลัง Boot

ต้องใช้ User ชื่อ raspberry หรือไม่?

Service Example ใน Source ระบุ User=raspberry Source ไม่ได้ให้ Config สำหรับ Username อื่น จึงควรตรวจค่าให้ตรงกับระบบจริง

ใช้กับ Raspberry Pi 4 หรือ Raspberry Pi 5 ได้ไหม?

Source ทดสอบและระบุ Raspberry Pi 3B เท่านั้น ไม่ได้ให้ Compatibility Test กับ Pi รุ่นอื่น

สรุป: OLED เล็ก ๆ ช่วยให้ Headless Raspberry Pi ดูแลง่ายขึ้น

Project นี้ไม่ได้พยายามทำ Dashboard ใหญ่หรือซับซ้อน

แต่เลือกข้อมูลสามอย่าง ที่มีประโยชน์มากสำหรับ Headless System:

  • Online / Offline
  • IP Address
  • Local Time

แล้วเชื่อม Flow ตั้งแต่ Network Check ไปจนถึง OLED ด้วย Python เพียง Script เดียว

Network State

Python socket

IP + ONLINE/OFFLINE

Pillow

luma.oled

I²C

SSD1306 OLED

ส่วน systemd ทำให้จอไม่ต้องพึ่งการ Login หรือการ Run Script ด้วยมือหลัง Reboot

สำหรับ Maker นี่จึงเป็นตัวอย่างที่ดีว่า การเพิ่ม Feedback เล็ก ๆ บน Hardware สามารถทำให้ Headless Device ใช้งานจริงสะดวกขึ้นมาก โดยไม่จำเป็นต้องเพิ่ม Monitor เข้าไปทั้งชุด

References / แหล่งข้อมูลต้นฉบับ

กำลังทำ Raspberry Pi หรือ IoT Project แบบ Headless?

การเพิ่ม Local Display เล็ก ๆ ช่วยให้ Device บอกสถานะของตัวเองได้ โดยไม่ต้องต่อ Monitor หรือ Login เข้าเครื่องก่อนทุกครั้ง

ถ้ากำลังทำ Raspberry Pi, IoT, OLED Display, Sensor หรือ Maker Project สามารถสอบถามทีม Globalbyte เรื่อง Development Board, Display, Jumper Wire, Module และอุปกรณ์ที่เหมาะกับ Project ได้

บทความนี้ไม่ได้ยืนยันว่า Raspberry Pi 3B หรือ OLED รุ่นเดียวกับต้นฉบับ มี Stock อยู่ที่ Globalbyte กรุณาสอบถามรุ่นและ Availability ก่อนสั่งซื้อ

 


Blog posts