2026年9月16日 星期三

Python TCP 分組網路實作:1 位組長收集 5 位組員的 IP、MAC、學號與姓名

大一網際網路應用|Python TCP 分組網路實作:1 位組長收集 5 位組員的 IP、MAC、學號與姓名

在「網際網路應用」課程中,我們會學到 IP Address、MAC Address、Port、Socket、TCP、Client、Server 等重要觀念。

如果只背誦這些名詞,很難真正理解一筆資料究竟如何從一台電腦傳送到另一台電腦。 因此這次我們直接使用 Python, 讓學生自己建立一個小型的 TCP Client / Server 網路

每組由一位學生擔任組長, 組長的電腦擔任 TCP Server; 另外 5 位學生擔任組員, 組員的電腦擔任 TCP Client

每位組員輸入自己的學號與姓名後, Python 自動取得本機的 IP 與 MAC Address, 將資料轉換成 JSON, 再透過 TCP 傳送到組長電腦。

本次實驗的核心問題:
如何讓 5 位組員先與組長建立 TCP 連線, 再將自己的學號、姓名、IP 與 MAC 傳送給組長?

一、學習目標

  • 理解 TCP Client / Server 架構。
  • 了解 IP Address、MAC Address 與 Port。
  • 理解 Socket 在網路程式中的角色。
  • 理解 TCP 是 Connection-oriented Protocol。
  • 了解 TCP Server 的 bind、listen、accept。
  • 了解 TCP Client 的 connect。
  • 使用 sendall() 與 recv() 傳送資料。
  • 使用 JSON 交換結構化資料。
  • 完成「1 位組長 + 5 位組員」TCP 網路實驗。

二、分組方式

每組共有 6 位學生

角色 人數 程式 網路角色
組長 1 tcp_leader.py TCP Server
組員 5 tcp_node.py TCP Client

三、網路架構

組員1 Client ─┐
組員2 Client ─┤
組員3 Client ─┤
組員4 Client ─┤── TCP ──→ 組長 Server
組員5 Client ─┘             Port 5005
                                │
                                ↓
                       建立本組節點資料表

例如組長 IP:

192.168.1.100

組員可能分別為:

組員1:192.168.1.101
組員2:192.168.1.102
組員3:192.168.1.103
組員4:192.168.1.104
組員5:192.168.1.105

這些只是範例,實際 IP 由教室的網路環境決定。


四、TCP 最大的特色:先建立連線

TCP 的全名是:

Transmission Control Protocol

TCP 是連線導向(Connection-oriented)的傳輸層協定。

因此組員不能直接把資料丟給組長,而是先:

找到組長 IP

找到 Port 5005

建立 TCP Connection

傳送 JSON 資料

組長接收

組長回覆

關閉 Connection

五、TCP 與 UDP 有什麼不同?

比較 TCP UDP
連線 需要建立連線 不需建立連線
可靠傳輸 提供可靠、有序的位元組流 本身不保證可靠送達
資料形式 Byte Stream Datagram
常見 Socket SOCK_STREAM SOCK_DGRAM

初學時可以先這樣理解:

TCP:比較像先打電話接通,再開始說話。

UDP:比較像直接寄出一張明信片。

六、TCP Socket

建立 TCP Socket:

sock = socket.socket(
    socket.AF_INET,
    socket.SOCK_STREAM
)

其中:

  • AF_INET:使用 IPv4。
  • SOCK_STREAM:建立 TCP Socket。

這裡就是與上一個 UDP 實驗非常重要的差異:

UDP
socket.SOCK_DGRAM

TCP
socket.SOCK_STREAM

七、TCP Server 的四個重要步驟

組長端有四個非常重要的 Socket 操作:

bind()

listen()

accept()

recv()

1. bind()

server.bind(("0.0.0.0", 5005))

讓 Server 使用本機的 TCP Port 5005。

2. listen()

server.listen(5)

讓 Socket 進入監聽狀態,準備接受連線。 參數代表等待連線佇列的 backlog 設定,並不是「最多只能服務 5 位學生」。

3. accept()

client_socket, address = server.accept()

等待一位組員建立 TCP 連線。

4. recv()

data = client_socket.recv(4096)

接收該組員送來的資料。


八、組長端 TCP Server 完整程式

組長儲存為:

tcp_leader.py
import socket
import json

SERVER_IP = "0.0.0.0"
SERVER_PORT = 5005

# 建立 TCP Socket
server = socket.socket(
    socket.AF_INET,
    socket.SOCK_STREAM
)

# 允許程式重新啟動後較快重新使用 Port
server.setsockopt(
    socket.SOL_SOCKET,
    socket.SO_REUSEADDR,
    1
)

# 綁定 IP 與 Port
server.bind(
    (SERVER_IP, SERVER_PORT)
)

# 開始監聽
server.listen(5)

students = {}

print("=" * 60)
print("TCP 小組節點資料收集系統")
print("本電腦角色:組長 TCP Server")
print(f"等待 TCP Port {SERVER_PORT}...")
print("=" * 60)


while len(students) < 5:

    print("\n等待組員連線...")

    # 接受 TCP Connection
    client_socket, address = server.accept()

    print(
        f"收到 TCP 連線:"
        f"{address[0]}:{address[1]}"
    )

    try:

        # 接收資料
        data = client_socket.recv(4096)

        if not data:
            client_socket.close()
            continue

        # JSON → Python Dictionary
        student = json.loads(
            data.decode("utf-8")
        )

        student_id = student["student_id"]
        name = student["name"]
        ip = student["ip"]
        mac = student["mac"]

        students[student_id] = {
            "name": name,
            "ip": ip,
            "mac": mac,
            "source_ip": address[0]
        }

        print()
        print(
            f"目前收到 "
            f"{len(students)} / 5 位組員"
        )

        print("-" * 50)

        print(f"學號:{student_id}")
        print(f"姓名:{name}")

        print(
            f"組員回報 IP:{ip}"
        )

        print(
            f"TCP 實際來源 IP:"
            f"{address[0]}"
        )

        print(f"MAC:{mac}")

        print(
            f"來源 Port:"
            f"{address[1]}"
        )

        print("-" * 50)

        # 回覆組員
        message = (
            f"組長已收到 "
            f"{student_id} {name} 的資料"
        )

        client_socket.sendall(
            message.encode("utf-8")
        )

    except Exception as e:

        print(
            "處理資料時發生錯誤:",
            e
        )

    finally:

        # 關閉這一位組員的 Connection
        client_socket.close()


print()
print("=" * 90)
print("本組 5 位組員資料收集完成")
print("=" * 90)

print(
    f"{'學號':<15}"
    f"{'姓名':<12}"
    f"{'回報IP':<18}"
    f"{'來源IP':<18}"
    f"{'MAC'}"
)

print("-" * 90)

for student_id, info in students.items():

    print(
        f"{student_id:<15}"
        f"{info['name']:<12}"
        f"{info['ip']:<18}"
        f"{info['source_ip']:<18}"
        f"{info['mac']}"
    )

server.close()

九、組員端 TCP Client 完整程式

5 位組員都使用相同程式:

tcp_node.py
import socket
import uuid
import json

# =========================================
# 修改成「組長電腦」的 IP
# =========================================

LEADER_IP = "192.168.1.100"
SERVER_PORT = 5005


# =========================================
# 取得本機 IP
# =========================================

def get_ip():

    s = socket.socket(
        socket.AF_INET,
        socket.SOCK_DGRAM
    )

    try:

        # 此處不是真的傳資料給 8.8.8.8
        # 主要用來判斷系統會選用哪個本機 IP
        s.connect(("8.8.8.8", 80))

        ip = s.getsockname()[0]

    except:

        ip = "127.0.0.1"

    finally:

        s.close()

    return ip


# =========================================
# 取得 MAC Address
# =========================================

def get_mac():

    mac = uuid.getnode()

    return ":".join(
        f"{(mac >> i) & 0xff:02X}"
        for i in range(40, -1, -8)
    )


# =========================================
# 主程式
# =========================================

print("=" * 50)
print("TCP 小組節點資料傳送程式")
print("本電腦角色:組員 TCP Client")
print("=" * 50)

student_id = input("請輸入學號:")
name = input("請輸入姓名:")

ip = get_ip()
mac = get_mac()

student = {
    "student_id": student_id,
    "name": name,
    "ip": ip,
    "mac": mac
}

print()
print("準備傳送資料")
print("-" * 40)

print("學號:", student_id)
print("姓名:", name)
print("IP:", ip)
print("MAC:", mac)

print("-" * 40)

print(
    "組長 Server:",
    LEADER_IP
)


# =========================================
# 建立 TCP Socket
# =========================================

sock = socket.socket(
    socket.AF_INET,
    socket.SOCK_STREAM
)


try:

    # =====================================
    # 建立 TCP Connection
    # =====================================

    print("\n正在連線到組長...")

    sock.connect(
        (LEADER_IP, SERVER_PORT)
    )

    print("TCP 連線成功!")


    # =====================================
    # Dictionary → JSON → Bytes
    # =====================================

    data = json.dumps(
        student,
        ensure_ascii=False
    ).encode("utf-8")


    # =====================================
    # 傳送資料
    # =====================================

    sock.sendall(data)

    print(
        "資料已透過 TCP "
        "傳送給組長!"
    )


    # =====================================
    # 等待組長回覆
    # =====================================

    reply = sock.recv(4096)

    print(
        "\n組長回覆:",
        reply.decode("utf-8")
    )


except ConnectionRefusedError:

    print(
        "\n連線失敗!"
        "請確認組長程式是否已啟動。"
    )


except Exception as e:

    print(
        "\n發生錯誤:",
        e
    )


finally:

    sock.close()

    print(
        "\nTCP Connection 已關閉"
    )

十、注意:get_ip() 裡為什麼還看到 SOCK_DGRAM?

有同學可能會發現: 明明這一課改成 TCP,為什麼 get_ip() 裡面還有:

socket.SOCK_DGRAM

這裡的 UDP Socket 不是本次學生資料傳輸使用的通訊協定。 它只是利用作業系統的路由選擇,協助程式判斷目前會使用哪個本機 IP。

真正將學生資料傳給組長的 Socket 是:

sock = socket.socket(
    socket.AF_INET,
    socket.SOCK_STREAM
)

所以本實驗的主要資料傳輸仍然是 TCP


十一、組員最重要的一行:connect()

UDP 版本沒有真正的 TCP Connection, 但 TCP Client 必須先:

sock.connect(
    (LEADER_IP, SERVER_PORT)
)

可以把它理解為:

組員知道:

組長 IP
192.168.1.100

+

TCP Port
5005

        ↓

192.168.1.100:5005

        ↓

建立 TCP Connection

十二、TCP 三向交握

在組員的 connect() 與組長的 accept() 背後,TCP 會進行建立連線的程序。 最經典的就是 Three-Way Handshake(三向交握)

組員 Client                         組長 Server
     │                                  │
     │ ----------- SYN --------------→ │
     │                                  │
     │ ←-------- SYN + ACK ------------ │
     │                                  │
     │ ----------- ACK --------------→ │
     │                                  │
     │      TCP Connection 建立         │
     │                                  │

完成連線後,應用程式才開始交換學生資料。

注意: Python 程式不需要自己寫 SYN、ACK。 這些工作由作業系統中的 TCP/IP 協定堆疊處理。 我們使用 Socket API 操作即可。

十三、sendall() 做什麼?

組員使用:

sock.sendall(data)

把 JSON 編碼後的 Bytes 寫入 TCP 連線。

組長則使用:

data = client_socket.recv(4096)

從 TCP 位元組流讀取資料。

本實驗的 JSON 很短,因此用單次 recv(4096) 即可作為入門示範。 但要特別知道: TCP 是 Byte Stream,不保留應用程式訊息邊界。 正式系統若要傳送較長或連續多筆資料,通常要自行設計長度欄位、分隔符號或其他 framing 機制。


十四、完整操作流程

① 6 位學生分組

② 選出 1 位組長

③ 組長查詢自己的 IP

④ 告訴其他 5 位組員

⑤ 組長啟動 tcp_leader.py

⑥ Server bind()

⑦ Server listen()

⑧ 組員執行 tcp_node.py

⑨ Client connect()

⑩ Server accept()

⑪ 建立 TCP Connection

⑫ 組員 sendall()

⑬ 組長 recv()

⑭ 組長回覆

⑮ 關閉該次 Connection

十五、實際執行

Step 1:組長先啟動

python tcp_leader.py

畫面:

============================================================
TCP 小組節點資料收集系統
本電腦角色:組長 TCP Server
等待 TCP Port 5005...
============================================================

等待組員連線...

Step 2:組員設定組長 IP

LEADER_IP = "192.168.1.100"

Step 3:組員執行

python tcp_node.py

例如:

請輸入學號:411001001
請輸入姓名:王小明

準備傳送資料
----------------------------------------
學號:411001001
姓名:王小明
IP:192.168.1.101
MAC:34:12:98:AA:01:10
----------------------------------------
組長 Server:192.168.1.100

正在連線到組長...
TCP 連線成功!

資料已透過 TCP 傳送給組長!

組長回覆:
組長已收到 411001001 王小明 的資料

TCP Connection 已關閉

十六、組長看到的資料

收到 TCP 連線:
192.168.1.101:52341

目前收到 1 / 5 位組員
--------------------------------------------------

學號:411001001
姓名:王小明

組員回報 IP:
192.168.1.101

TCP 實際來源 IP:
192.168.1.101

MAC:
34:12:98:AA:01:10

來源 Port:
52341

這裡同樣可以比較:

JSON 中組員自己回報的 IP

TCP Server 從 accept() 得到的實際來源 IP

十七、MAC 為什麼仍然需要組員自己回報?

雖然我們從 UDP 改成 TCP, 但這個觀念沒有改變。

組員
 │
 ├─ 學號
 ├─ 姓名
 ├─ IP
 └─ MAC
      │
      ↓
   Dictionary
      ↓
     JSON
      ↓
     TCP
      ↓
   組長 Server

Server 從 TCP 連線可以知道對端 IP 與來源 Port, 但 JSON 中的 MAC 是由組員程式自行取得後主動回報的應用資料。


十八、五位組員完成後

==========================================================================================
本組 5 位組員資料收集完成
==========================================================================================

學號           姓名        回報IP             來源IP             MAC
------------------------------------------------------------------------------------------
411001001      王小明      192.168.1.101      192.168.1.101      34:12:98:AA:01:10
411001002      李小華      192.168.1.102      192.168.1.102      50:EB:F6:22:18:21
411001003      陳大文      192.168.1.103      192.168.1.103      10:62:E5:31:21:33
411001004      林小美      192.168.1.104      192.168.1.104      A4:BB:6D:11:23:44
411001005      張志明      192.168.1.105      192.168.1.105      D8:43:AE:12:35:55

十九、從組員輸入姓名到組長收到資料

輸入學號、姓名

取得 IP、MAC

Python Dictionary

JSON

UTF-8 Bytes

TCP Socket

connect()

TCP Connection

sendall()

IP Network

組長 Port 5005

accept()

recv()

JSON Decode

建立節點資料表

組長回覆

close()

二十、如果出現 Connection refused

例如:

ConnectionRefusedError

通常代表 Client 已經找到目標主機, 但指定的 TCP Port 沒有接受連線,或連線受到系統/網路政策阻擋。

首先檢查:

  1. 組長是否先執行 tcp_leader.py
  2. 組員設定的 LEADER_IP 是否正確。
  3. Server 與 Client 是否都使用 Port 5005。
  4. Windows 防火牆是否允許 Python 網路通訊。
  5. 目前網路是否允許組員電腦與組長電腦互相連線。

二十一、UDP 與 TCP 程式的關鍵差異

學過上一個 UDP 實驗後,可以直接比較:

功能 UDP TCP
Socket SOCK_DGRAM SOCK_STREAM
Server 等待 recvfrom() listen() + accept()
Client 連線 不需 TCP 連線 connect()
傳送 sendto() sendall()
接收 recvfrom() recv()

二十二、課堂思考題

  1. 為什麼 TCP Server 要先執行?
  2. SOCK_STREAM 代表什麼?
  3. bind() 的作用是什麼?
  4. listen() 的作用是什麼?
  5. accept() 在等待什麼?
  6. connect() 做了什麼?
  7. TCP 三向交握中的 SYN、SYN-ACK、ACK 是什麼?
  8. sendall() 和 UDP 的 sendto() 有什麼不同?
  9. 為什麼 TCP Server 可以知道 Client 的來源 IP?
  10. 為什麼 MAC 還是由組員自己放進 JSON?
  11. 為什麼 TCP 不能假設一次 recv() 永遠等於一次 sendall()
  12. 組長收到 5 位組員後,為什麼程式就結束?如果要一直提供服務,應該修改哪個部分?

二十三、從 TCP 實驗看懂網際網路應用

這次看起來只是把:

「學號 + 姓名 + IP + MAC」

從 5 台組員電腦傳到組長電腦, 背後卻已經串起一整套網路概念:

Client / Server

IP Address + Port

Socket

TCP

connect()

Three-Way Handshake

accept()

JSON

sendall() / recv()

組長建立節點資料表

透過這個實驗,同學不只是知道「TCP 是可靠的、連線導向的傳輸層協定」, 而是真正讓兩台電腦建立 TCP Connection, 再把自己建立的資料送過網路。

因此,學習網際網路最重要的不只是記住協定名稱, 而是理解: 應用程式的資料,如何透過 Socket、TCP、IP 與實際網路, 從一台電腦到達另一台電腦。

Python UDP 分組網路實作:1 位組長收集 5 位組員的 IP、MAC、學號與姓名

大一網際網路應用|Python UDP 分組網路實作:1 位組長收集 5 位組員的 IP、MAC、學號與姓名

在「網際網路應用」課程中,我們會學到 IP Address、MAC Address、Port、Socket、TCP、UDP、Client 與 Server 等重要觀念。 如果只記住名詞定義,很難真正理解電腦之間到底如何交換資料。

因此這次我們直接進行一個學生分組網路實驗。 每組由一位學生擔任組長(Group Leader), 組長的電腦同時擔任 UDP Server; 另外 5 位學生擔任組員(Node)

每位組員執行 Python 程式後,只要輸入自己的學號與姓名, 程式會取得該電腦的 IP 與 MAC Address, 再透過 UDP 傳送到組長電腦。

本次實驗的核心問題:
如何讓一組學生自己建立一個小型區域網路, 由「組長 Server」收集 5 位「組員 Node」的網路與身分資料?

一、學習目標

  • 理解 Client / Server 網路架構。
  • 了解 IP Address、MAC Address 與 Port 的基本角色。
  • 了解 TCP 與 UDP 的基本差異。
  • 使用 Python Socket 建立 UDP 通訊。
  • 使用 JSON 傳送結構化資料。
  • 觀察 UDP 封包的實際來源 IP。
  • 讓學生自己完成一個小型區域網路。
  • 建立「組長 Server + 5 個組員 Node」的分組合作模式。

二、分組方式

每組共有 6 位學生

角色 人數 程式 主要任務
組長 1 udp_leader.py UDP Server
組員 5 udp_node.py UDP Node

三、網路架構

組員1 Node ─┐
組員2 Node ─┤
組員3 Node ─┤
組員4 Node ─┤── UDP ──→ 組長 Server
組員5 Node ─┘             Port 5005
                              │
                              ↓
                     建立本組節點資料表

例如組長電腦的 IP 是:

192.168.1.100

其他 5 位組員可能是:

組員1:192.168.1.101
組員2:192.168.1.102
組員3:192.168.1.103
組員4:192.168.1.104
組員5:192.168.1.105

實際 IP 由教室的 DHCP 或網路設定決定, 不一定與範例相同。


四、誰是 Server?誰是 Client?

這個實驗中,老師不再擔任 Server。

組長:擔任 UDP Server,等待 5 位組員傳送資料。

組員:擔任 UDP Client / Node,將自己的資料傳送給組長。

因此一個小組本身就是一個簡單的 Client / Server 網路:

       組員 Node
           │
       組員 Node
           │
       組員 Node
           │
       組員 Node
           │
       組員 Node
           │
           ↓
       組長 Server

這也說明一個重要觀念: Server 不一定是一台大型主機。 只要一支程式正在等待並提供某種網路服務, 一般筆記型電腦也可以暫時扮演 Server。


五、TCP 與 UDP

TCP 與 UDP 都是常見的傳輸層協定。 本次實驗選擇 UDP。

比較 TCP UDP
建立連線 需要 不需要
可靠傳輸機制 本身不保證
順序控制 本身不保證
協定負擔 較多 較少

可以先把 TCP 想成「打電話」, UDP 則像「直接寄出一張明信片」。

但我們可以在應用程式中自行加入確認機制。 所以本實驗設計成:

組員
  │
  │ UDP:我的資料
  ↓
組長
  │
  │ UDP:已收到
  ↓
組員

六、每位組員要傳什麼?

每位組員傳送四項資料:

  • 學號 student_id
  • 姓名 name
  • IP Address
  • MAC Address

例如:

{
    "student_id": "411001001",
    "name": "王小明",
    "ip": "192.168.1.101",
    "mac": "34:12:98:AA:01:10"
}

這就是 JSON 格式。

整個資料處理流程為:

Python Dictionary
        ↓
       JSON
        ↓
      UTF-8
        ↓
       Bytes
        ↓
       UDP

七、組長端完整程式

組長將程式儲存為:

udp_leader.py
import socket
import json

SERVER_IP = "0.0.0.0"
SERVER_PORT = 5005

sock = socket.socket(
    socket.AF_INET,
    socket.SOCK_DGRAM
)

sock.bind(
    (SERVER_IP, SERVER_PORT)
)

students = {}

print("=" * 60)
print("UDP 小組節點資料收集系統")
print("本電腦角色:組長 Server")
print(f"等待 UDP Port {SERVER_PORT}...")
print("=" * 60)


while len(students) < 5:

    data, address = sock.recvfrom(4096)

    try:

        student = json.loads(
            data.decode("utf-8")
        )

        student_id = student["student_id"]
        name = student["name"]
        ip = student["ip"]
        mac = student["mac"]

        students[student_id] = {
            "name": name,
            "ip": ip,
            "mac": mac,
            "source_ip": address[0]
        }

        print()
        print(f"目前收到 {len(students)} / 5 位組員")
        print("-" * 50)

        print(f"學號:{student_id}")
        print(f"姓名:{name}")
        print(f"組員回報 IP:{ip}")
        print(f"實際來源 IP:{address[0]}")
        print(f"MAC:{mac}")
        print(f"來源 Port:{address[1]}")

        print("-" * 50)

        message = (
            f"組長已收到 "
            f"{student_id} {name} 的資料"
        )

        sock.sendto(
            message.encode("utf-8"),
            address
        )

    except Exception as e:

        print("資料格式錯誤:", e)


print()
print("=" * 90)
print("本組 5 位組員資料收集完成")
print("=" * 90)

print(
    f"{'學號':<15}"
    f"{'姓名':<12}"
    f"{'回報IP':<18}"
    f"{'來源IP':<18}"
    f"{'MAC'}"
)

print("-" * 90)

for student_id, info in students.items():

    print(
        f"{student_id:<15}"
        f"{info['name']:<12}"
        f"{info['ip']:<18}"
        f"{info['source_ip']:<18}"
        f"{info['mac']}"
    )

sock.close()

八、5 位組員使用的完整程式

5 位組員全部使用同一支程式:

udp_node.py
import socket
import uuid
import json

# =========================================
# 修改成「組長電腦」的 IP
# =========================================

LEADER_IP = "192.168.1.100"
SERVER_PORT = 5005


# =========================================
# 取得本機 IP
# =========================================

def get_ip():

    s = socket.socket(
        socket.AF_INET,
        socket.SOCK_DGRAM
    )

    try:

        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]

    except:

        ip = "127.0.0.1"

    finally:

        s.close()

    return ip


# =========================================
# 取得本機 MAC Address
# =========================================

def get_mac():

    mac = uuid.getnode()

    return ":".join(
        f"{(mac >> i) & 0xff:02X}"
        for i in range(40, -1, -8)
    )


# =========================================
# 主程式
# =========================================

print("=" * 50)
print("UDP 小組節點資料傳送程式")
print("本電腦角色:組員 Node")
print("=" * 50)

student_id = input("請輸入學號:")
name = input("請輸入姓名:")

ip = get_ip()
mac = get_mac()

student = {
    "student_id": student_id,
    "name": name,
    "ip": ip,
    "mac": mac
}

print()
print("準備傳送資料")
print("-" * 40)

print("學號:", student_id)
print("姓名:", name)
print("IP:", ip)
print("MAC:", mac)

print("-" * 40)
print("組長 IP:", LEADER_IP)


# 建立 UDP Socket

sock = socket.socket(
    socket.AF_INET,
    socket.SOCK_DGRAM
)


# Dictionary → JSON → Bytes

data = json.dumps(
    student,
    ensure_ascii=False
).encode("utf-8")


# 傳送給組長

sock.sendto(
    data,
    (LEADER_IP, SERVER_PORT)
)

print()
print("資料已透過 UDP 傳送給組長!")


# 等待組長回覆

sock.settimeout(5)

try:

    reply, address = sock.recvfrom(4096)

    print(
        "組長回覆:",
        reply.decode("utf-8")
    )

except socket.timeout:

    print("5 秒內沒有收到組長回覆")

sock.close()

九、實驗操作流程

本實驗最重要的是執行順序

① 六位學生完成分組

② 選出一位組長

③ 組長查詢自己的 IP

④ 組長將 IP 告訴 5 位組員

⑤ 組長先執行 udp_leader.py

⑥ 5 位組員設定 LEADER_IP

⑦ 組員執行 udp_node.py

⑧ 輸入學號與姓名

⑨ UDP 傳送資料

⑩ 組長完成本組節點資料表

十、組長如何查自己的 IP?

Windows 可以開啟命令提示字元:

ipconfig

找到目前使用中的網路介面,例如:

IPv4 Address . . . . . . . . . :
192.168.1.100

組長就告訴其他 5 位組員:

「我們這組的 Server IP 是 192.168.1.100。」

5 位組員修改:

LEADER_IP = "192.168.1.100"

十一、組長先啟動 Server

組長執行:

python udp_leader.py

畫面:

============================================================
UDP 小組節點資料收集系統
本電腦角色:組長 Server
等待 UDP Port 5005...
============================================================

這代表組長電腦正在等待其他組員送資料到 UDP Port 5005


十二、組員開始傳送

組員執行:

python udp_node.py

例如:

請輸入學號:411001001
請輸入姓名:王小明

準備傳送資料
----------------------------------------
學號:411001001
姓名:王小明
IP:192.168.1.101
MAC:34:12:98:AA:01:10
----------------------------------------
組長 IP:192.168.1.100

資料已透過 UDP 傳送給組長!

組長回覆:
組長已收到 411001001 王小明 的資料

十三、組長會看到什麼?

第一位組員傳送後:

目前收到 1 / 5 位組員
--------------------------------------------------

學號:411001001
姓名:王小明

組員回報 IP:
192.168.1.101

實際來源 IP:
192.168.1.101

MAC:
34:12:98:AA:01:10

來源 Port:
52341

這裡要特別觀察:

組員自己回報的 IP

組長 Server 從 UDP 封包看到的來源 IP

兩者是不是相同?

十四、recvfrom() 告訴組長什麼?

組長程式中最重要的一行之一:

data, address = sock.recvfrom(4096)

它會得到:

data
│
└── 組員傳送的 JSON 資料

address
│
├── 來源 IP
└── 來源 Port

例如:

('192.168.1.101', 52341)

因此:

address[0]

就是組長實際看到的來源 IP。


十五、MAC 為什麼不是組長直接查到的?

本實驗中的 MAC 是:

組員電腦
   ↓
uuid.getnode()
   ↓
取得本機 MAC
   ↓
放入 Python Dictionary
   ↓
轉成 JSON
   ↓
UDP
   ↓
組長 Server

因此:

MAC 是組員主動回報的應用層資料。
它不是組長使用 recvfrom() 直接從遠端 UDP 封包取得的 MAC Address。

十六、5 位組員完成後

當組長收到 5 個不同學號後:

==========================================================================================
本組 5 位組員資料收集完成
==========================================================================================

學號           姓名        回報IP             來源IP             MAC
------------------------------------------------------------------------------------------
411001001      王小明      192.168.1.101      192.168.1.101      34:12:98:AA:01:10
411001002      李小華      192.168.1.102      192.168.1.102      50:EB:F6:22:18:21
411001003      陳大文      192.168.1.103      192.168.1.103      10:62:E5:31:21:33
411001004      林小美      192.168.1.104      192.168.1.104      A4:BB:6D:11:23:44
411001005      張志明      192.168.1.105      192.168.1.105      D8:43:AE:12:35:55

到這裡,一個由學生自行建立的 「1 Server + 5 Nodes」小型網路 就完成了。


十七、從輸入姓名到組長收到資料

把整個網路資料流串起來:

組員輸入學號、姓名

Python 取得 IP、MAC

建立 Dictionary

轉成 JSON

UTF-8 編碼

Socket

UDP

IP Network

組長 IP

UDP Port 5005

recvfrom()

JSON Decode

加入本組節點資料表

組長回覆「已收到」

十八、實驗失敗時如何檢查?

  1. 組長與組員是否位於可以互通的網路環境。
  2. 組長 IP 是否查詢正確。
  3. 5 位組員的 LEADER_IP 是否設定正確。
  4. 組長是否先啟動 udp_leader.py
  5. UDP Port 5005 是否受到防火牆限制。
  6. Python 是否取得 Windows 防火牆的網路存取權限。
  7. 校園 Wi-Fi 是否啟用 Client Isolation / AP Isolation。

如果校園 Wi-Fi 基於資訊安全限制學生裝置互相連線, 可依學校網路政策使用教學實驗用 AP 建立獨立區域網路。


十九、課堂思考題

  1. 為什麼組長要先啟動程式?
  2. 組長在這個實驗中扮演 Server 還是 Client?
  3. 5 位組員為什麼都必須知道組長 IP?
  4. Port 5005 的作用是什麼?
  5. AF_INET 表示什麼?
  6. SOCK_DGRAM 與 UDP 有什麼關係?
  7. IP Address 和 MAC Address 的角色有什麼不同?
  8. 為什麼組員要把資料轉成 JSON?
  9. recvfrom() 可以取得哪些資訊?
  10. 為什麼組長可以知道封包的來源 IP,卻不能用同樣方式取得跨網段的遠端 MAC?
  11. UDP 封包如果遺失,這支程式會自動重傳嗎?
  12. 如果組長換人,程式的網路架構需要修改哪些地方?

二十、進階實驗:組長也加入節點

目前架構是:

1 位組長 Server
+
5 位組員 Node
=
6 位學生

下一階段可以進一步讓組長不只是 Server, 同時也是一個 Node:

              組長
        Server + Node
             ↑ ↓
      ┌──────┼──────┐
      ↓      ↓      ↓
   組員1   組員2   組員3
      ↑      ↑      ↑
      └── 組員4、組員5 ──┘

再進一步加入:

  • UDP Broadcast:自動尋找組長。
  • UDP Unicast:指定某位組員傳送訊息。
  • 節點上線通知。
  • 節點離線通知。
  • 簡易群組聊天室。

二十一、結語:每一組就是一個小型網路實驗室

這次實驗最大的改變,是老師不再擔任 Server

每組由學生自己選出一位組長, 組長的筆記型電腦就是 Server; 其他同學的電腦就是 Node。

6 位學生

選出 1 位組長

組長建立 UDP Server

5 位組員建立 UDP Node

Socket + IP + Port

JSON + UDP

完成本組節點資料表

透過這個實驗,同學不只是背誦 IP、MAC、Port、Socket、UDP、Client 與 Server, 而是親手讓這些技術真正運作一次。

更重要的是,每一組學生都自己建立了一個 「1 Server + 5 Nodes」的小型區域網路。 這可以作為後續學習 UDP Broadcast、Peer-to-Peer、IoT、即時通訊與網路服務 的重要基礎。

Python UDP 五節點互傳實作:讓 5 台電腦自動交換 IP、MAC、學號與姓名

Python UDP 五節點互傳實作:讓 5 台電腦自動交換 IP、MAC、學號與姓名

在學習「網際網路應用」時,我們常會接觸到 IP、MAC Address、Port、TCP 與 UDP, 但如果只是背誦名詞,很難真正理解這些技術如何運作。

這次我們直接使用 Python 建立一個 「UDP 五節點學生資料互傳系統」。 5 位同學各自使用一台電腦,每台電腦都是一個 Node(節點), 不需要另外架設中央 Server。

每位同學啟動程式後輸入自己的學號與姓名, Python 自動取得本機的 IP Address 與 MAC Address, 再利用 UDP Broadcast 將自己的資料傳送給其他同學。


一、這個實驗要做什麼?

假設教室中有 5 位同學:

節點 學號 姓名 IP
Node 1 411001001 王小明 192.168.1.101
Node 2 411001002 李小華 192.168.1.102
Node 3 411001003 陳大文 192.168.1.103
Node 4 411001004 林小美 192.168.1.104
Node 5 411001005 張志明 192.168.1.105

每一台電腦都同時具有兩個角色:

  • UDP Sender:傳送自己的資料。
  • UDP Receiver:接收其他同學的資料。
                 UDP Broadcast
                       ↕
        ┌──────────────┼──────────────┐
        │              │              │
     Node 1 ←────→ Node 2 ←────→ Node 3
        ↕              ↕              ↕
     Node 4 ←──────────────────────→ Node 5

每台電腦:
UDP Sender + UDP Receiver
    

二、為什麼選 UDP?

UDP 是 User Datagram Protocol(使用者資料包協定)。 和 TCP 不同,UDP 在傳送資料之前不需要先建立連線。

可以先用一個簡單概念理解:

TCP:比較像打電話,先建立連線,再開始交談。
UDP:比較像直接寄出一張明信片,不先確認對方是否準備好。

UDP 具有簡單、快速、負擔較低的特色, 常見於即時通訊、影音串流、DNS、IoT 裝置通訊及區域網路設備探索等應用。


三、什麼是 UDP Broadcast?

如果我們已經知道某位同學的 IP,可以直接將 UDP 封包傳給他的 IP, 這種方式稱為 Unicast(單播)

但這次實驗的目的,是希望 5 台電腦可以互相發現彼此, 所以使用 Broadcast(廣播)

255.255.255.255
  

程式將資料送往廣播位址後,在網路環境允許廣播的情況下, 同一區域網路中監聽指定 UDP Port 的其他節點就有機會收到這份資料。


四、我們要交換哪些資料?

每位同學要交換四項基本資料:

  1. 學號 student_id
  2. 姓名 name
  3. IP Address
  4. MAC Address

例如王小明的資料可以表示成:

{
    "student_id": "411001001",
    "name": "王小明",
    "ip": "192.168.1.101",
    "mac": "34:12:98:AA:01:10"
}
  

這種資料格式就是 JSON。 JSON 非常適合用來交換結構化資料, 在 Web API、IoT、雲端服務與 App 開發中都很常見。


五、Python 完整程式

5 位同學都使用完全相同的程式, 不需要為 Node 1、Node 2 分別修改程式。

import socket
import threading
import json
import uuid
import time

PORT = 5005

# 儲存目前收到的節點資料
nodes = {}


# ==========================================
# 取得本機 IP
# ==========================================
def get_ip():

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    try:
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]

    except:
        ip = "127.0.0.1"

    finally:
        s.close()

    return ip


# ==========================================
# 取得 MAC Address
# ==========================================
def get_mac():

    mac = uuid.getnode()

    return ":".join(
        f"{(mac >> i) & 0xff:02X}"
        for i in range(40, -1, -8)
    )


# ==========================================
# 顯示目前節點
# ==========================================
def show_nodes():

    print("\n" + "=" * 70)
    print("目前找到的 UDP 節點")
    print("=" * 70)

    print(
        f"{'學號':<15}"
        f"{'姓名':<12}"
        f"{'IP':<18}"
        f"{'MAC'}"
    )

    print("-" * 70)

    for student_id, info in nodes.items():

        print(
            f"{student_id:<15}"
            f"{info['name']:<12}"
            f"{info['ip']:<18}"
            f"{info['mac']}"
        )

    print("-" * 70)
    print(f"目前共有 {len(nodes)} 個節點")


# ==========================================
# UDP Receiver
# ==========================================
def receiver():

    sock = socket.socket(
        socket.AF_INET,
        socket.SOCK_DGRAM
    )

    sock.setsockopt(
        socket.SOL_SOCKET,
        socket.SO_REUSEADDR,
        1
    )

    sock.bind(("", PORT))

    print(f"\nUDP Receiver 已啟動,Port = {PORT}")

    while True:

        data, address = sock.recvfrom(4096)

        try:

            student = json.loads(
                data.decode("utf-8")
            )

            student_id = student["student_id"]

            nodes[student_id] = student

            print(
                f"\n收到 {student['name']} "
                f"({student_id}) 的資料"
            )

            print(
                f"UDP 封包來源 IP:{address[0]}"
            )

            show_nodes()

        except Exception as e:

            print("接收資料錯誤:", e)


# ==========================================
# UDP Broadcast
# ==========================================
def broadcast(student):

    sock = socket.socket(
        socket.AF_INET,
        socket.SOCK_DGRAM
    )

    sock.setsockopt(
        socket.SOL_SOCKET,
        socket.SO_BROADCAST,
        1
    )

    data = json.dumps(
        student,
        ensure_ascii=False
    ).encode("utf-8")

    sock.sendto(
        data,
        ("255.255.255.255", PORT)
    )

    sock.close()


# ==========================================
# 主程式
# ==========================================

print("=" * 60)
print("      UDP 五節點學生資料互傳系統")
print("=" * 60)

student_id = input("請輸入學號:")
name = input("請輸入姓名:")

ip = get_ip()
mac = get_mac()

my_data = {
    "student_id": student_id,
    "name": name,
    "ip": ip,
    "mac": mac
}

# 將自己的資料放入節點表
nodes[student_id] = my_data

print("\n我的節點資料")
print("-" * 40)
print("學號:", student_id)
print("姓名:", name)
print("IP:", ip)
print("MAC:", mac)


# ==========================================
# 啟動 Receiver Thread
# ==========================================

thread = threading.Thread(
    target=receiver,
    daemon=True
)

thread.start()

time.sleep(1)


# ==========================================
# 操作選單
# ==========================================

while True:

    print("\n")
    print("========= UDP 操作選單 =========")
    print("1. 廣播我的資料")
    print("2. 查看目前5個節點")
    print("3. 離開")
    print("===============================")

    choice = input("請選擇:")

    if choice == "1":

        broadcast(my_data)

        print("\n已將我的資料廣播給其他同學!")

    elif choice == "2":

        show_nodes()

    elif choice == "3":

        print("程式結束")
        break

    else:

        print("請輸入 1、2 或 3")

六、實驗操作步驟

步驟 1:確認 5 台電腦連接同一個區域網路

建議課堂實驗時,5 台電腦連接同一台 Wi-Fi AP 或網路分享器。

步驟 2:5 台電腦都執行程式

python udp_5nodes.py
  

步驟 3:輸入自己的學號與姓名

請輸入學號:411001001
請輸入姓名:王小明

我的節點資料
----------------------------------------
學號:411001001
姓名:王小明
IP:192.168.1.101
MAC:34:12:98:AA:01:10
  

步驟 4:選擇「1. 廣播我的資料」

========= UDP 操作選單 =========
1. 廣播我的資料
2. 查看目前5個節點
3. 離開
===============================

請選擇:1

已將我的資料廣播給其他同學!
  

五位同學依序廣播自己的資料後, 每一台電腦就可以逐漸建立自己的節點資料表。


七、觀察實驗結果

當 5 個節點都成功交換資料後,可以看到:

======================================================================
目前找到的 UDP 節點
======================================================================

學號           姓名        IP                MAC
----------------------------------------------------------------------
411001001      王小明      192.168.1.101     34:12:98:AA:01:10
411001002      李小華      192.168.1.102     50:EB:F6:22:18:21
411001003      陳大文      192.168.1.103     10:62:E5:31:21:33
411001004      林小美      192.168.1.104     A4:BB:6D:11:23:44
411001005      張志明      192.168.1.105     D8:43:AE:12:35:55
----------------------------------------------------------------------

目前共有 5 個節點
  

此時並不是只有某一台 Server 擁有資料, 而是每一個 Node 都可以接收其他節點廣播的資訊。


八、程式中的五個重要觀念

1. IP Address

IP Address 用來識別 IP 網路中的通訊端點。 在本實驗中,每一台學生電腦都有自己的區域網路 IP。

2. MAC Address

MAC Address 是網路介面的鏈路層識別資訊。 本程式由各節點自行取得 MAC,再把它當作 JSON 資料的一部分透過 UDP 傳送。

因此要特別注意: MAC 並不是 UDP 自動幫我們從遠端電腦取得的。

3. Port

這次所有節點共同使用:

UDP Port 5005
  

IP 可以理解為找到哪一台主機,而 Port 則協助作業系統將資料交給對應的網路應用程式。

4. JSON

Python Dictionary:

my_data = {
    "student_id": student_id,
    "name": name,
    "ip": ip,
    "mac": mac
}
  

透過:

json.dumps()
  

將資料序列化,再編碼成位元組後透過 UDP 傳送。

5. Thread

每一台電腦既要等待接收資料,又要讓使用者操作選單。 如果只有單一執行流程,程式可能卡在:

sock.recvfrom(4096)
  

因此使用另一個 Thread 負責 UDP Receiver:

thread = threading.Thread(
    target=receiver,
    daemon=True
)

thread.start()
  

這樣主程式可以處理操作選單, Receiver Thread 則持續等待其他同學送來 UDP 封包。


九、封包到底怎麼走?

例如 Node 1 的王小明按下「廣播我的資料」, Python 先建立資料:

王小明
   ↓
Python Dictionary
   ↓
JSON
   ↓
UTF-8 Bytes
   ↓
UDP
   ↓
IP Network
   ↓
Broadcast
   ↓
Node 2 / Node 3 / Node 4 / Node 5
   ↓
JSON Decode
   ↓
加入 nodes 資料表
  

這一連串流程,就是應用程式資料真正進入網路並在不同主機間交換的一個簡化實例。


十、為什麼有時候 5 台電腦收不到彼此?

如果程式沒有錯,但同學彼此收不到資料,可以優先檢查:

  1. 5 台電腦是否位於可互通的同一區域網路。
  2. Windows 防火牆是否允許 Python 的網路通訊。
  3. UDP Port 5005 是否被防火牆或其他安全機制阻擋。
  4. Wi-Fi AP 是否開啟 Client Isolation / AP Isolation。
  5. 校園無線網路是否禁止裝置之間直接互相通訊。
實驗提醒:
校園 Wi-Fi 為了資訊安全,有時會限制用戶端彼此通訊或廣播。 因此課堂進行這類區域網路實驗時,可以準備獨立的教學用 Wi-Fi AP/分享器, 並依學校網路政策操作。

十一、思考題

  1. UDP 與 TCP 最大的差異是什麼?
  2. 為什麼這個實驗適合使用 Broadcast?
  3. IP Address 和 MAC Address 的角色有什麼不同?
  4. 為什麼所有同學都使用 Port 5005?
  5. 為什麼程式需要使用 Thread?
  6. 如果同一位同學廣播 10 次,為什麼 nodes 不一定會增加成 10 筆?
  7. UDP 如果發生封包遺失,這支程式會知道嗎?
  8. 如果要指定只傳給 Node 3,應該繼續使用 Broadcast,還是改成 Unicast?

十二、進階挑戰:從 Broadcast 走向 Unicast

第一階段,我們利用 Broadcast 完成:

自動交換身分資料 → 找到其他節點 → 建立 5 人節點表

下一階段就可以利用已經取得的 IP Address, 改成指定對象傳送訊息。

Node 1 王小明
      │
      │ UDP Unicast
      ↓
Node 3 陳大文

訊息:
Hello Node 3!
  

如此一來,就能形成更完整的學習路徑:

5 個 Node

UDP Broadcast

自動發現其他節點

建立 IP / MAC / 學號 / 姓名資料表

UDP Unicast

指定同學互傳訊息

十三、結語

這個實驗看似只是讓 5 位同學互傳「學號、姓名、IP、MAC」, 實際上已經把許多網際網路應用的重要概念串在一起:

IP → MAC → Port → Socket → UDP → Broadcast → JSON → Thread → Peer Node

學習網路程式設計最好的方式之一,就是讓不同的實體電腦真正交換資料。 當 Node 1 按下傳送,而 Node 2、Node 3、Node 4、Node 5 的螢幕真的出現 Node 1 的資料時, 原本抽象的 UDP、IP、Port 與封包概念,就會變成可以觀察、測試與修改的程式實驗。

下一個實驗,我們可以在這個五節點架構上加入 「選擇同學 → UDP Unicast → 一對一傳送文字訊息」, 進一步製作一個簡易的區域網路聊天室。

從家鄉食物記憶到地中海健康料理

智慧生活科技專業社群|AI社區共創教材

從家鄉食物記憶到地中海健康料理

用台語說記憶,與AI共同設計保留家鄉味的健康新料理

一道料理不只是食材與步驟,也包含家人、節慶、土地與生活記憶。當我們帶著AI走入社區,不應一開始就要求長輩計算幾公克、幾毫升,而是先聽他們說出最懷念的味道,再運用AI協助查找資料、換算份量,最後共同轉化成符合地中海飲食精神的新料理。

教學核心:
先保存「原味記憶」,再進行「健康轉化」。不是否定長輩原來的料理,而是讓地方飲食智慧與現代健康觀念一起被看見。

一、為什麼從食物記憶開始?

如果直接問長輩「請設計一道地中海料理」,「地中海飲食」可能成為陌生的專業名詞;如果先問「你上懷念的是啥物料理?」長輩就能從熟悉的人、地方、節慶與味道開始說起。

因此,本活動將AI定位為整理與轉化的助手:長輩提供料理的靈魂,AI負責一般資料查找與份量換算,最後仍由長輩決定哪些特色必須保留、哪些健康調整可以接受。

二、教學流程:「4問+2次確認」

  1. 記憶第1問:最懷念哪一道料理?
  2. 記憶第2問:誰煮的?什麼時候、在哪裡吃?
  3. 記憶第3問:一定要有哪些食材?最懷念什麼味道或情景?
  4. 第一次確認:整理並確認「原味記憶食譜」。
  5. 健康第4問:從加菜、換好油、增加魚豆或使用全穀中,選擇適合的調整。
  6. 第二次確認:確認「地中海風味版家鄉料理」。
  7. 成果製作:生成一人份食譜與A4圖卡。

說出記憶 → 確認原味 → 選擇健康調整 → 確認新料理 → 生成食譜圖卡

三、把地中海飲食說得簡單一點

活動中不必向長輩講授太多營養名詞。可以把健康轉化濃縮成四個容易選擇的方向:

方向台語簡單說法料理調整
多蔬菜加一寡菜增加當季、在地蔬菜
好油脂換較好的油適量使用橄欖油或其他植物油
優質蛋白加魚、豆仔優先選擇魚類、豆類,適量禽肉
全穀雜糧換一寡全穀以全穀替代部分精製白米或白麵

重點不是每一道菜都加入橄欖油,而是掌握「多蔬果、全穀、豆類、堅果與魚類,使用適量好油,減少過多鹽、糖、飽和脂肪及高度加工食品」的精神,並優先採用社區原有的在地食材。

四、長輩、AI與教學人員如何分工?

角色主要任務
長輩提供料理名稱、人物、地方、時節、主要食材、特殊做法與記憶,選擇能接受的健康調整。
AI整理口述、查找相近食譜、換算一人份建議用量、提出2至3個合適方案及製作圖卡。
教學人員協助語音操作、控制訪談時間、說明健康概念,確保長輩擁有最後決定權。

五、第一階段提示詞:台語訪談與地中海健康轉化

第一階段只進行訪談、資料查找、健康轉化與文字確認,不要生成圖片。完成後應保留在同一個ChatGPT對話中,再上傳長輩照片並執行第二階段提示詞。

我是參加「115年臺南澎湖農村綠色照顧四面向行動方案」的社區長輩,想用台語說出記憶中的家鄉料理,再和AI一起把它轉化成一道保留家鄉味的地中海健康料理。 請擔任親切的「家鄉料理記憶訪問者」與「健康料理小助手」。全程使用簡單、自然、容易聽懂的台語,講慢一點,一次只問一個問題,等我回答後才繼續。 【第一階段:找回食物記憶】 最多詢問3題: 第1題:「你上懷念的是啥物料理?」 第2題:「這道料理是啥物人煮的?通常佇啥物所在、啥物時陣食?」 第3題:「這道料理一定愛有啥物食材?你上懷念的是啥物滋味抑是情景?」 不要逐項追問食材重量、調味料用量、鍋具及烹調時間。只有料理名稱、主要食材或特色做法完全聽不清楚時,才可簡短追問一次。 回答完成後,整理成:料理名稱、家鄉記憶、主要食材、傳統特色或特殊做法,以及最懷念的味道與情景。 請用簡單台語讀出結果,詢問:「這是你記持內底原來的料理,按呢整理敢有著?」等待確認後,才進入第二階段。 【第二階段:轉化為地中海健康料理】 先依照長輩確認的料理內容,查找可信且相近的公開食譜,換算合理的一人份建議用量。 分析這道料理可從哪些方向調整: 1. 增加當季、在地蔬菜。 2. 增加豆類、魚類或適量禽肉。 3. 將部分精製澱粉換成全穀雜糧。 4. 使用適量橄欖油或其他植物油。 5. 減少過多的鹽、糖、油及加工食品。 只能提出2至3個真正適合這道料理的方向,不要一次介紹太多健康知識,也不要為了符合地中海飲食而硬加不合適的食材。 接著只問一題:「若欲保留原來的家鄉味,閣予伊較健康,你想欲加一寡菜、換較好的油、增加魚豆仔,抑是用一寡全穀?」 如果我不清楚如何選擇,請用簡單台語介紹2個適合的方案讓我選,不要連續追問。 選擇完成後,整理成「地中海風味版家鄉料理」: 1. 新料理名稱。 2. 原料理的家鄉記憶。 3. 一人份食材與精確用量。 4. 4至5個簡單料理步驟。 5. 保留下來的傳統特色。 6. 健康調整的內容。 7. 一句簡單的健康說明。 不可刪除長輩認為重要的主要食材、人物、地點與料理記憶。不可把網路食譜說成長輩親口提供的資料。若網路資料與長輩說法不同,以長輩說法為優先。 最後用簡單台語讀出完整內容,只問一次:「這道料理有留你的家鄉味,嘛有加一寡健康的做法。按呢改,你敢合意?」 長輩回答「好」、「可以」、「正確」或相同意思後,停止提問,輸出最後確認版文字食譜。 重要:第一階段只完成訪談、健康轉化、食譜整理及長輩確認,不要生成圖片,也不要自行進入圖卡製作。請等待使用者另外上傳長輩照片並貼上第二階段提示詞。

六、第二階段提示詞:生成A4地中海食譜圖卡

確認文字食譜後,在同一個對話上傳一張已取得本人同意的長輩上半身照片,再複製以下提示詞。照片不必拿盤子,AI可生成長輩雙手捧著完成料理的自然姿勢。

請使用第一階段最後經過長輩確認的「地中海風味版家鄉料理」,直接生成一張A4直式繁體中文食譜圖卡。不要重新訪問,也不要再次改寫食材、份量、人物、地點、故事或料理方法。 【圖卡內容與版面】 1. 最上方清楚呈現「地中海風味版家鄉料理」的新料理名稱。 2. 畫面上方或適當位置設置「我的食物記憶」小區塊,放入原料理名稱、人物、地方、時節,以及一句經長輩確認的懷念話語。 3. 左側列出「1人份食材」,每項食材都要有清楚的小圖片及精確數量;只能使用第一階段最後確認的食材與份量。 4. 右側安排受訪長輩雙手捧著完成的地中海風味料理。 5. 下方呈現4至5個料理步驟,每一步都要有獨立操作圖片、步驟編號及簡短繁體中文說明。 6. 加入一個簡短的「地中海健康調整」區塊,只呈現第一階段經長輩同意的調整,例如增加在地蔬菜、魚豆類、全穀或適量好油。 7. 呈現臺南或澎湖社區的海島、漁村、農村及在地飲食特色;只使用訪談已確認的場域,不可自行增加其他地點。 8. 最底部完整加入:「115年臺南澎湖農村綠色照顧四面向行動方案」。 【人物照片規則】 1. 已提供並同意使用受訪長輩照片,請參考照片中的年齡感、臉部特徵、髮型、自然神情與服裝特色。 2. 原照片即使沒有拿盤子,也請自然延伸成半身或腰部構圖,生成長輩面帶親切微笑、雙手穩定捧著完成料理的姿勢。 3. 手掌、手指、餐盤與料理的位置必須自然合理。 4. 不可改變人物性別,不可過度年輕化、美化或卡通化,也不可增加其他人物。 5. 若本次沒有提供或未同意使用本人照片,不得推測受訪者外貌,改用不具特定身分的家鄉長輩示意人物。 【文字與視覺規則】 1. 所有文字必須使用清楚、正確的繁體中文,不得出現簡體字、英文、亂碼或無意義文字。 2. 字體要大、對比清楚,版面圖多字少,適合長輩閱讀。 3. 採用溫暖、自然、健康的食譜海報風格,料理必須是畫面焦點。 4. 不得增加第一階段未經長輩確認的主要食材、人物、地點、故事與料理方法。 5. 原味記憶與健康新版要分區呈現,不可把AI提出的新作法寫成長輩過去的生活記憶。 請直接生成圖卡,不要只提供文字說明、版面建議或生圖提示詞。

七、教學現場注意事項

  • 健康轉化要由長輩選擇,不能由AI直接替長輩決定。
  • 原味記憶與健康新版分開呈現,避免歷史記憶被改寫。
  • AI查找的份量須標示為建議值,不可假裝是長輩親口提供。
  • 每次只給2至3個合適方案,避免資訊太多造成選擇壓力。
  • 食材應優先考慮當地取得、季節適合及長輩咀嚼能力。
  • 若涉及糖尿病、腎臟病、吞嚥困難或特殊飲食限制,應由營養師或醫療專業人員個別評估。

八、結語

這套方法不是請AI取代長輩的料理智慧,而是讓AI成為記憶整理、營養轉譯與視覺創作的助手。長輩先說出自己的故事,再親自選擇健康調整,最後確認食譜與圖卡。如此完成的作品,既保留家鄉味,也能成為社區食農教育、綠色照顧與跨世代學習的教材。

長輩的地方飲食智慧 × 地中海健康觀念 × AI共同創作
115年臺南澎湖農村綠色照顧四面向行動方案

2026年9月14日 星期一

台語口述 × AI:快速完成長輩的家鄉食譜圖卡

智慧生活科技專業社群|社區實作教材

台語口述 × AI:快速完成長輩的家鄉食譜圖卡

從逐項追問改為「3問+1確認」,讓長輩說記憶、AI補資料、長輩做決定

一、為什麼要調整訪談方法?

在社區試教時發現,原來的提示詞會依序詢問料理名稱、地點、人物、每項食材、精確份量、器具、步驟、時間與料理祕訣。雖然資料完整,卻也讓AI問得太細。一道料理可能訪談十五至二十分鐘,後面的食譜整理與圖卡生成因而延遲。

長輩最珍貴的知識,不一定是「鹽幾公克」或「水幾毫升」,而是料理在什麼時候出現、有哪些不可缺少的食材、以前怎麼做,以及令人懷念的味道。精確的一人份用量與一般烹調時間,可由AI查找可信且相近的公開食譜,提出建議草稿,再交由長輩一次確認。

核心原則
長輩提供「家鄉記憶與料理特色」,AI補充「標準化參考資料」,最後由長輩決定內容是否正確。

二、哪些資料問長輩?哪些交給AI?

資料內容主要來源處理原則
料理名稱、時節與地點長輩忠實保留口述內容
主要食材、特殊做法與祕訣長輩不可由網路食譜取代
人物、故事與懷念的味道長輩不得自行增加
一人份重量與一般調味用量AI查找標示為AI建議,交由長輩確認
一般處理方法與烹調時間AI查找只補一般細節,不改變家鄉做法

三、社區現場的精簡流程

  1. 說明與取得同意:先說明訪談、拍照、AI生圖及成果公開方式。
  2. 拍一張參考照:拍攝清楚的正面或微側面上半身照片,不必拿盤子。
  3. 進行3個問題:詢問料理背景、主要食材與特色做法。
  4. AI整理:查找一般食譜並換算一人份建議用量,整理成4至5步驟。
  5. 一次整體確認:用簡單台語讀出完整草稿,只修改長輩指出的部分。
  6. 生成食譜圖卡:AI參考照片,生成長輩雙手捧著完成料理的畫面。
  7. 確認成品:請長輩確認人物形象、料理內容與是否同意公開。

同意說明 → 拍照 → 3問訪談 → AI整理 → 長輩確認 → 生成圖卡 → 成品確認

四、照片應該什麼時候拍?

建議在訪談前先取得同意並拍照。此時長輩精神較好,也不會在訪談完成後因離席或疲累而漏拍。一般社區課程只需要一張清楚的上半身照片,AI可以延伸構圖,自然生成雙手、盤子及完成料理。

現場拍照重點:臉部光線均勻、背景簡單、避免背光、不使用美肌濾鏡,一次只拍一位受訪者。照片只要能清楚呈現年齡感、髮型、臉部特徵、神情與服裝即可。

第二張「捧空盤」照片不是必要條件。一般課程可由AI直接生成捧料理的姿勢;若圖卡將用於重要展覽或大型海報,可多拍一張捧空盤照片,提高雙手與盤子構圖的穩定性。

拍照前可使用的台語說明

阿伯/阿姆,等一下欲用你講的料理故事,做一張食譜圖卡。圖卡內底會畫一位長輩,雙手捧料理。阮會使先共你翕一張相,予AI參考你的形貌無?這張相會照頭前講好的方式使用。

「課程成果展示、紙本印刷、網路公開」宜分開說明與勾選。若未取得本人同意,仍可完成訪談,但必須改用不具特定身分的長輩示意人物,不得模仿受訪者外貌。

五、可直接複製:3問+1確認提示詞

我是社區長輩,想用台語介紹一道記憶中的家鄉料理。 請擔任親切的家鄉料理訪問者,全程使用簡單、自然、容易聽懂的台語與我交談。請講慢一點,一次只問一個問題。 這次是社區課程,訪談時間有限,請遵守以下規則: 一、最多只問我3個主要問題: 第1題:這道料理叫做什麼?通常在什麼時候、什麼地方吃? 第2題:這道料理一定要有哪些主要食材?有沒有不能漏掉的材料? 第3題:長輩以前怎麼煮?有沒有特別的順序、祕訣或最懷念的味道? 二、不要逐項追問精確重量、調味料用量、烹調時間、鍋具或一般料理細節。只有料理名稱、主要食材或特色做法完全聽不清楚時,才可簡短追問一次。 三、訪談完成後: 1. 完整保留長輩親口說出的料理名稱、主要食材、地方特色、人物、地點與特殊做法。 2. 對長輩未說明的精確用量,查找可信且相近的公開食譜,換算成合理的「1人份建議用量」。 3. 一般處理方式與烹調時間,可以參考公開食譜整理。 4. 網路資料只能補充標準用量與一般細節,不可改變長輩說的料理特色。 5. 不可自行增加會改變料理風味的主要食材、人物、地點、故事或特殊做法。 6. 網路資料與長輩說法不同時,以長輩說法為準。 7. 查不到相同料理時,可參考最接近的料理換算,但須標示為「AI建議用量」。 四、一次整理出:料理名稱、家鄉記憶、1人份食材與建議用量、4至5個料理步驟、料理時間、長輩的料理祕訣,以及AI參考補充的內容。 五、用簡單台語慢慢讀出整理結果,最後只問一次: 「我共你講的內容整理好矣。食材份量有一部分是參考一般食譜換算做一人份,料理的特色佮做法攏照你講的。按呢敢有著?若有毋著,請講欲改佗一項。」 若長輩回答「正確」、「可以」、「好」或相同意思,就停止訪談,不再逐項確認,直接準備食譜圖卡。

六、可直接複製:A4食譜圖卡提示詞

請依照剛才經過長輩認可的內容,製作一張A4直式繁體中文食譜圖卡。 1. 最上方清楚呈現料理名稱。 2. 左側列出「1人份食材」,每項食材均有清楚圖片及精確數量,份量以長輩認可版本為準。 3. 參考已取得本人同意使用的受訪長輩照片,保留其年齡感、臉部特徵、髮型、自然神情與服裝特色。 4. 將照片自然延伸為半身或腰部構圖,生成長輩親切微笑、雙手穩定捧著完成料理的姿勢。原照片不必出現盤子,請由AI補生成自然合理的雙手、容器與料理。 5. 不可改變人物性別,不可過度年輕化、美化或卡通化,也不可增加其他人物。手掌、手指、盤子與料理的位置必須自然。 6. 下方呈現4至5個料理步驟,每一步均有獨立操作圖片、編號及簡短文字。 7. 呈現料理所屬社區的農村、漁村、海島或在地飲食特色,但不可增加未經確認的地點或人物。 8. 文字使用清楚、正確的繁體中文,不得出現簡體字、英文或亂碼。 9. 不得改變長輩認可的主要食材、料理順序、特殊做法與家鄉記憶。AI查找資料只能用於已認可的用量、時間及一般操作細節。 10. 版面採大字、高對比、圖多字少,方便長輩閱讀。 11. 最底部完整加入:「115年臺南澎湖農村綠色照顧四面向行動方案」。 請直接生成食譜圖卡,不要再次提問,也不要只提供製圖提示詞。

未同意使用本人照片時

受訪長輩未同意使用本人照片。請不要模仿或推測其真實外貌,改以一位不具特定身分、親切自然的家鄉長輩作為示意人物,雙手捧著完成的料理。

七、教學人員現場操作建議

  • 每位工作人員或學生協助1至2位長輩。
  • 活動前先測試手機麥克風、喇叭、網路與AI帳號。
  • 安排較安靜的座位,避免多人同時說話影響台語辨識。
  • 不要讓AI因為「資料完整」而無限追問;超過3題就由工作人員提醒進入整理。
  • AI提供的用量不是歷史證據,而是方便製作食譜圖卡的建議值。
  • 圖卡完成後,必須再確認文字、人物、料理及公開意願。

八、結語

AI進入社區,不應把長輩變成接受盤問的資料提供者,而應成為整理生活智慧的助手。「3問+1確認」把課程時間留給真正重要的家鄉記憶,也讓AI負責擅長的資料查找、份量換算、文字整理與圖像生成。透過清楚的照片同意、長輩認可及成品確認,每一道料理圖卡不只是一份食譜,更是一段受到尊重、能被保存與分享的地方記憶。

115年臺南澎湖農村綠色照顧四面向行動方案

2026年9月13日 星期日

聲聲畫家鄉:用台語說出家鄉料理記憶

聲聲畫家鄉:用台語說出家鄉料理記憶

長輩講台語,AI整理食譜、畫出家鄉味

115年臺南澎湖農村綠色照顧四面向行動方案

一碗料理,不只是食材與步驟,也保存著一個家庭、一段漁村生活及長輩的人生記憶。本活動讓長輩不必學打字,只要用最熟悉的台語慢慢說,AI便能協助整理料理故事、1人份食材與做法,最後生成一張有食材圖片、步驟圖解及長輩捧著料理的A4食譜圖卡。

一、活動特色:讓科技配合長輩

活動的重點不是教長輩操作複雜的生成式AI,而是讓手機成為一位會聆聽的料理訪問者。長輩只要對著手機說台語,AI一次問一個簡單問題;若內容聽不清楚,AI會再詢問,不自行增加人物、食材、數量或做法。

1看圖喚起記憶
2台語慢慢講
3確認食譜內容
4生成A4圖卡

二、活動前的準備

  • 可以使用語音交談及生成圖片的手機或平板。
  • 準備澎湖絲瓜、小管、海菜、南瓜、花生、鮮魚、牡蠣及麵線等食材照片。
  • 每一至二位長輩安排一位工作人員或學生陪伴。
  • 選擇較安靜的座位,先測試麥克風、喇叭與網路。
  • 若要讓圖卡呈現長輩本人,應先取得同意,再拍攝或上傳清楚照片。

三、台語訪談的八個問題

工作人員開啟ChatGPT語音模式後,AI必須全程使用簡單、自然的台語,而且一次只問一題。

順序AI可以這樣問希望取得的資料
1阿伯/阿姆,你今仔日想欲介紹啥物料理?料理名稱
2這項料理,是佇佗位煮?啥物時陣會食?地點與時節
3彼當時有啥物人佮你做伙煮?共同記憶中的人物
4欲準備啥物食材?食材名稱
5逐項食材大約愛偌濟?數量與份量
6頭一步按怎做?紲落來按怎做?料理順序
7大約愛煮偌久?烹調時間
8你上懷念的是啥物味、啥物人,抑是啥物情景?料理故事

若長輩只說「半條絲瓜」或「一碗麵」,AI可以協助換算,但一定要再問:「我共你換算做120克,按呢敢有著?」得到確認後才能記錄。

四、第一段指令:讓AI用台語訪問

我是參加「115年臺南澎湖農村綠色照顧四面向行動方案」的長輩,想用台語講一項記憶中的家鄉料理。 請你擔任親切的澎湖料理訪問者,全程用簡單、自然、長輩容易聽懂的台語和我交談。請講慢一點,一次只能問一個問題,等我回答後才可以繼續問下一題。 請依序問我: 1. 想介紹啥物料理? 2. 這項料理佇佗位煮、啥物時陣食? 3. 當時有啥物人做伙煮? 4. 使用啥物在地食材? 5. 每一項食材大約用偌濟? 6. 頭一步按怎做?後面的步驟按怎做? 7. 大約愛煮偌久? 8. 上懷念的是啥物味道、人物抑是情景? 若食材名稱、數量或做法聽無清楚,請繼續用台語問我,毋通家己增加資料。 訪問完成後,請整理成:料理名稱、100至150字料理記憶、1人份食材與精確數量、4至5個料理步驟、烹調時間,以及長輩最懷念的一句話。 請先用台語讀予我聽,最後問我:「按呢整理敢有著?」等我確認正確後,才詢問我是否生成A4直式食譜圖卡。

五、先確認,才可以畫圖

AI整理完成後,長輩或陪伴者應逐項檢查。尤其是料理名稱、食材、份量、烹調步驟及人物,不正確就用台語告訴AI修改。只有當長輩回答「有著」或「正確」,才能進入圖卡生成。

建議的標準食譜內容:
  1. 料理名稱。
  2. 100至150字料理記憶。
  3. 1人份食材及精確數量。
  4. 4至5個簡短料理步驟。
  5. 烹調時間。
  6. 長輩最懷念的一句話。

六、第二段指令:生成A4食譜圖卡

請將剛才經過長輩確認的料理內容,製作成一張A4直式繁體中文食譜圖卡。 圖卡要求: 1. 最上方清楚呈現料理名稱。 2. 左側列出「1人份食材」,每項食材都要有圖片及精確數量。 3. 右側安排一位親切的家鄉長輩,雙手捧著完成的料理;若已提供並同意使用長輩照片,請參考其外貌。 4. 下方呈現4至5個料理步驟,每一步都要有獨立操作圖片、步驟編號及簡短文字。 5. 呈現澎湖海島、漁村、農村及在地飲食特色。 6. 文字使用清楚、正確的繁體中文,不得出現簡體字、亂碼或英文。 7. 不得增加長輩沒有說過的食材、人物、地點或料理方法。 8. 最底部完整加入:「115年臺南澎湖農村綠色照顧四面向行動方案」。 9. 字體要大、對比清楚,適合長輩閱讀。 10. 採用溫暖、自然、健康的食譜海報風格。

七、工作人員的陪伴任務

  • 協助,不代答:讓長輩自己說出記憶,學生只協助按麥克風及確認畫面。
  • 一次一問:長輩回答完畢後,再進入下一題,不急著追問。
  • 尊重原話:保留長輩慣用的料理名稱與生活語彙。
  • 確認份量:先記錄「一把、半碗、半條」等生活量詞,再協助換算並徵得同意。
  • 檢查文字:圖片中文字可能產生錯字,輸出或展示前必須校對。
  • 尊重肖像:未取得同意,不使用長輩本人照片生成圖像。

八、從一張食譜卡看見四面向綠色照顧

四面向活動中的實踐
綠場域從澎湖社區、漁村與家庭廚房找回地方記憶。
綠飲食認識在地、當季食材,整理適合1人份的健康料理。
綠療育透過回想、口述與圖像創作,促進表達及成就感。
綠陪伴由青年或志工陪伴長輩使用語音AI,形成代間共學。
重要提醒:AI是記錄與創作工具,不是營養師或醫師。若長輩有糖尿病、腎臟病、高血壓、吞嚥困難或食物過敏,食材與份量仍應由營養或醫療專業人員確認。

九、結語

當長輩說出「阮細漢就是按呢煮」,被保存的不只是一道菜,更是澎湖的海味、家庭的關係與地方生活智慧。透過台語訪談、AI整理及食譜圖卡,長輩從被服務者成為知識提供者;青年也能在陪伴過程中重新認識地方,讓一代人的料理記憶成為下一代看得見、學得會、傳得下去的文化資產。

綠照趣旅行:36格智慧互動地圖與六個活動範例

115年臺南分署臺南澎湖地區農村區域亮點計畫

綠照趣旅行:臺南・澎湖36格智慧互動地圖

跟著小車遊庄頭,認識在地好生活!

一張地圖、一台小車,可以如何串起長輩的生活經驗與孩子的好奇心?「綠照趣旅行」把臺南與澎湖的景點、料理、工藝及水果圖卡放進6 × 6的方格地圖,讓參與者從選擇目的地開始,一起想路線、操作小車,再把到站後看到的圖卡,變成彼此交流的話題。

本文整理海報中的四種探索功能、基本操作方法,以及六個可在社區活動中使用的任務範例,提供綠照員、志工、教師與學生帶領者參考。

綠照趣旅行海報:臺南澎湖36格智慧互動地圖、四種探索與四步驟操作
圖1 綠照趣旅行功能與操作海報。地圖插畫用於活動示意,實際格位請以現場6 × 6教具為準。

一、讓地方故事成為學習的起點

地圖共有36格,圖卡交錯呈現地方特色與水果圖像。參與者可以選一個想認識的地方,或一種熟悉的食物,將它設定為小車的目的地。每一次移動,都包含「觀察、選擇、規劃、執行與調整」的過程。

長輩能夠提供料理、農作與生活記憶,孩子或學生可以一起辨識方向、記錄指令。帶領者讓雙方輪流決定與操作,便能把教具活動延伸為代間交流。

活動定位:本活動以圖卡探索、按鍵操作與現場分享為主。「智慧互動」在此指透過小車的指令操作練習規劃與回饋;圖卡內容由參與者及帶領者解說。本文未將自動辨識圖卡或自動語音導覽列為設備功能。

二、一張地圖,四種探索

面向探索內容活動中的做法
綠場域認識庄頭景點看港口、海岸與社區圖卡,分享地方景色和生活經驗。
綠飲食探索在地食材從水果與料理圖卡出發,聊食材、做法與家庭味道。
綠療育欣賞地方工藝觀察工藝圖卡的材料、形狀與顏色,交流手作或看戲記憶。
綠陪伴合作操作小車輪流規劃路線、輸入指令、觀察結果,並互相協助。

同一個任務可以同時涵蓋多個面向。例如,操作小車前往料理圖卡,既能探索綠飲食,也能透過合作體驗綠陪伴。

三、四步驟,出發趣!

  1. 選目的地:挑一張想探索的圖卡,說出「我想去這裡」及選擇原因。
  2. 規劃路線:將小車放在起點格中央,確認車頭朝向,再用手指或方向卡排出路線。
  3. 輸入指令:依設備的操作方式輸入移動與轉向指令,確認後啟動小車。
  4. 到站分享:觀察目的地圖卡,說一段記憶、介紹一種食材,或提出一個問題。

開始前,先試走一次

將地圖平放在穩固、平整且留有移動空間的位置。先測試小車前進一次的距離是否對應一格,轉向是否為原地90度,並確認如何清除上一輪指令。若設備的步距與格子大小不一致,應先調整教具配置或依該設備的方式操作。

路線中的「前進」以小車車頭方向為準。小車右轉後,下一次前進的方向也會跟著改變;因此,每次重新出發都要確認起點與車頭。

以下格號依本次36格地圖:由左至右、由上至下編為1至36號。圖卡若重新排列,請同步調整任務。海報插畫與AI重製圖卡中的細字,宜以原始圖卡核對後再用於地方介紹。

四、六個活動範例:從單站體驗到三站旅行

第一次體驗可先選一個短程任務。熟悉操作後,再增加站點;不需要一次完成全部六個活動。

範例01|綠飲食 × 綠陪伴

小車買水果

任務:從7號草莓出發,前往14號橘子。

將小車放在7號格中央,車頭朝向8號。請參與者用手指走一次「7 → 8 → 14」,再討論需要哪些指令。

若前進一次為一格、右轉為原地90度:
前進 → 右轉 → 前進 → 執行

小車先到8號,原地右轉後朝向14號,再前進一格。請留意:這條路線中的右轉只改變朝向,不會直接移動到下一格。

到站提問:「您最喜歡吃什麼水果?」「以前買水果,會去哪裡買?」

台語帶領語:「咱欲去買橘仔,先想看覓,小車愛按怎行?」

進階玩法:讓參與者另選一張水果圖卡作為下一站,自行決定起點與車頭,再規劃路線。

範例02|綠場域 × 綠陪伴

跟著小車看海趣

任務:前往3號重光漁港,看圖找出與海邊生活有關的景物。

帶領者先選定附近的起點,讓參與者規劃到3號的路線。到站後,大家一起觀察圖卡中的漁船、港口與海面,說出自己看到的細節。

到站提問:「您曾經到漁港做什麼?」「您記得海邊有哪些聲音?」

台語帶領語:「您有佇海邊看過日出無?」

進階玩法:再選27號菓葉社區看日出作為第二站。路程較長時可以分段操作,每段完成後重新確認車頭方向,再繼續旅行。

範例03|綠飲食 × 綠陪伴

古早味尋寶

任務:從19號古早味粿仔與36號紫菜炒米粉中,選一道想介紹的料理。

先請長輩選目的地,再由同伴一起排路線。小車到站後,請長輩介紹自己熟悉的食材或做法;孩子或學生可以記下幾個關鍵詞。

到站提問:「這道料理需要哪些食材?」「以前家裡是誰做的?」「通常在什麼時候吃?」

台語帶領語:「這道料理,以前攏啥人煮?您上懷念啥物滋味?」

進階玩法:把分享整理成「料理名稱、食材、記憶一句話」的小卡。不同家庭的做法可以並列記錄,不必要求只有一種答案。

範例04|綠療育 × 綠陪伴

庄頭工藝小旅行

任務:選17號工藝圖卡或22號掌中聲影偶作為目的地。

參與者到站後,先從圖像辨認作品的形狀、色彩與可能使用的材料,再分享自己曾看過或做過的手工藝。材料若無法從圖片確認,可留作之後查證的問題。

到站提問:「這件作品讓您想到什麼?」「您以前做過哪些手工?」「看布袋戲時,最喜歡哪個角色?」

台語帶領語:「您以前有看過布袋戲無?上佮意啥物角色?」

進階玩法:準備可觸摸的工藝材料樣本,讓參與者描述粗細、軟硬與觸感,再與圖卡中的作品比較。

範例05|綠陪伴

聲聲畫家鄉

任務:前往26號聲聲畫家鄉,到站後分享一段家鄉記憶。

請參與者從熟悉的人物、地方、料理或農村生活中選一個主題。帶領者一次只問一個問題,等對方說完再接著問,讓故事按照分享者的步調展開。

依序提問:「您想講哪一段記憶?」接著才問「發生在哪裡?」「當時有誰?」「最懷念什麼?」

台語帶領語:「細漢時陣,啥物代誌予您上懷念?」

進階玩法:若另備手機,可在取得分享者同意後錄音,或協助整理成短文;先讓本人確認內容,再製作故事圖卡。這是活動延伸工具,不是小車本身的功能。

範例06|四面向整合

我的庄頭一日遊

任務:每組選「一個景點+一道料理+一項工藝」,安排三站旅行。

例如選擇3號重光漁港、19號古早味粿仔、22號掌中聲影偶,依小組決定的順序出發。這是教具中的跨地點探索組合,不代表真實世界的一日交通行程。

小組可分為導航員、操作員與分享員:導航員說明路線,操作員輸入指令,分享員在到站後介紹圖卡。每到一站交換角色,讓每個人都有選擇與操作的機會。

到站提問:「為什麼選這三站?」「如果朋友來玩,您會如何介紹?」

台語帶領語:「若朋友來阮庄頭,您欲帶伊去佗位?」

進階玩法:增加「經過一張水果圖卡」或「找另一條可行路線」等條件。先確認路線走得通,再比較不同規劃,不以速度決定表現。

五、社區活動怎麼帶?40分鐘示範流程

以下為可調整的教學安排,並非固定執行時程。可依參與者人數、小車數量與熟悉程度延長分享時間。

時間活動帶領重點
5分鐘認識地圖與選圖卡讓參與者說出一張熟悉或感興趣的圖卡。
5分鐘示範前進與轉向確認步距、車頭方向及清除指令方式。
10分鐘水果短程任務先用手指走,再輸入指令;完成後交換操作。
15分鐘自選主題探索從其他範例選一個,結合到站分享。
5分鐘回顧與交流說出今天發現的一件事,及下一次想去的圖卡。

走錯格,也是一起思考的機會

若小車沒有到達目的地,先請大家指出「原本預計到哪裡」與「實際到哪裡」,再檢查起點、車頭、指令及步距。一次調整一個地方,重新嘗試。帶領者可以問:「哪一步跟我們想的不一樣?」

初學者可以先用兩三個指令完成短路線;熟悉後再增加轉彎或站點。讓同伴提供口頭提示,並保留操作者自己按鍵與修正的機會。

六、從到達目的地,到聽見彼此的故事

活動觀察可以著重於參與的過程:是否能自己選圖卡、說出下一步方向、輪流操作、遇到偏差後願意再試,以及是否有機會分享或聆聽故事。這些可作為教學紀錄,不應單憑一次遊戲判斷健康或認知狀況。

  • 今天我選了:哪一張圖卡?為什麼?
  • 今天我試了:哪一個操作或路線?如何調整?
  • 今天我聽見:同伴分享了什麼地方記憶?
  • 下次我想:再探索哪個目的地?

「綠照趣旅行」讓地方圖卡成為對話的起點。小車走過一格又一格,參與者也在選擇、合作與分享之間,認識臺南與澎湖的生活樣貌,並讓自己的經驗成為活動中值得被聽見的內容。

一起選目的地、一起想路線,讓每一次到站,都有一個故事可以分享。

計畫名稱:115年臺南分署臺南澎湖地區農村區域亮點計畫
教具名稱:綠照趣旅行—臺南・澎湖36格智慧互動地圖