พิมพ์คำที่ต้องการค้นหา ทั้งภาษาไทยและอังกฤษ

  1. หน้าแรก
  2. คลังความรู้
  3. เซนเซอร์และทรานส์ดิวเซอร์
  4. 20.15 Modbus

// บทที่ 20.15 · Sensors & Transducers · Modbus RTU & TCP

Modbus RTU และ Modbus TCP: จากสาย RS-485 ถึงไบต์ในเฟรม

Modbus เกิดขึ้นในปี 1979 ที่ Modicon ผู้ผลิต PLC ในสหรัฐฯ (ปัจจุบันอยู่ในเครือ Schneider Electric ของฝรั่งเศส) ความเรียบง่ายและการเปิดให้ใช้ฟรีทำให้มันกลายเป็นภาษาพื้นฐานของมิเตอร์ไฟฟ้า อินเวอร์เตอร์ ทรานสมิตเตอร์ และ PLC แทบทุกยี่ห้อ หน้านี้อธิบายจนสร้างและอ่านเฟรมได้ด้วยตัวเอง

เขียนโดย อาจารย์มดอัปเดต
สรุปใน 30 วินาที
  • มาสเตอร์ถาม สเลฟตอบ · RTU บน RS-485 (สเลฟ 1–247) · TCP พอร์ต 502
  • 4 ตาราง: coils · discrete inputs · input registers · holding registers — "40001" = แอดเดรส 0 ในเฟรม
  • อ่าน 2 รีจิสเตอร์จากสเลฟ 1: 01 03 00 00 00 02 C4 0B · CRC ไบต์ต่ำก่อน
  • float32 ใช้ 2 รีจิสเตอร์ ลำดับคำ ABCD หรือ CDAB แล้วแต่ผู้ผลิต

ชั้นกายภาพ: RS-485 พื้นฐาน

RS-485 (TIA/EIA-485) ส่งข้อมูลด้วยผลต่างแรงดันระหว่างสองสาย สัญญาณรบกวนที่เหนี่ยวนำเข้าทั้งสองสายเท่า ๆ กันจึงหักล้างกัน ทำให้ส่งได้ไกลถึงราว 1200 m ที่ความเร็วต่ำ บัสเดียวรองรับ 32 unit load (ทรานซีฟเวอร์รุ่นใหม่ที่เป็น 1/8 unit load ทำให้ต่อได้มากกว่านั้น) ขณะที่ Modbus เองกำหนดแอดเดรสสเลฟได้ 1–247

D+ (B)D− (A)120 Ω120 Ωมาสเตอร์PLC · ไบแอสID 1 มิเตอร์ID 2 ทรานสมิตเตอร์ID 3 อินเวอร์เตอร์ID 4 …ระยะรวม ≤ 1200 m · ≤ 32 unit load ต่อเซกเมนต์ · สายคู่บิดเกลียวมีชีลด์ + สายกราวด์อ้างอิงห้ามต่อแบบดาว (star) หรือกิ่งยาว — สัญญาณสะท้อนทำให้ CRC ผิดเป็นระยะ
รูป 1 · บัส RS-485 ที่ถูกต้อง: เดซี่เชนจากมาสเตอร์ไปยังสเลฟตัวสุดท้าย ตัวต้านทานปลายสายสองตัว และไบแอสหนึ่งชุด
  • สาย: คู่บิดเกลียวมีชีลด์ อิมพีแดนซ์ราว 120 Ω พร้อมสายที่สามเป็นกราวด์อ้างอิง (common) — RS-485 ไม่ใช่ "สองสายจริง" แรงดันโหมดร่วมต้องอยู่ในขอบเขตของทรานซีฟเวอร์
  • ชื่อขั้ว: มาตรฐานเรียก A/B แต่ผู้ผลิตหลายรายสลับกัน ถ้าต่อแล้วไม่ตอบ ลองสลับสองสายก่อนทำอย่างอื่น (ไม่ทำให้อุปกรณ์เสีย)
  • ค่าเริ่มต้นของ Modbus serial line: 8 บิตข้อมูล พาริตีคู่ (Even) 1 stop · ถ้าไม่ใช้พาริตีต้องใช้ 2 stop bit เพื่อให้ยังเป็น 11 บิตต่ออักขระ (ในทางปฏิบัติหลายอุปกรณ์ตั้ง 8N1)

แบบจำลองข้อมูล: 4 ตารางและแอดเดรส พื้นฐาน

ตารางขนาดอ่าน/เขียนแบบเดิมฟังก์ชันโค้ดตัวอย่าง
Coils1 บิตอ่าน/เขียน0xxxx01 อ่าน · 05 เขียนตัวเดียว · 15 เขียนหลายตัวสั่งเปิดปั๊ม รีเซ็ตเตือน
Discrete inputs1 บิตอ่านอย่างเดียว1xxxx02สถานะสวิตช์
Input registers16 บิตอ่านอย่างเดียว3xxxx04ค่าวัด
Holding registers16 บิตอ่าน/เขียน4xxxx03 อ่าน · 06 เขียนตัวเดียว · 16 เขียนหลายตัวค่าวัด ค่าตั้ง ช่วงวัด

D1 Register number vs PDU address เลขรีจิสเตอร์กับแอดเดรสในเฟรม

แอดเดรสในเฟรม=เลขแบบ 4xxxx−4000140001→0x0000,    40010→0x0009\text{แอดเดรสในเฟรม} = \text{เลขแบบ 4xxxx} - 40001 \qquad 40001 \to \texttt{0x0000},\;\; 40010 \to \texttt{0x0009}

เฟรม RTU, เฟรม TCP และ CRC-16 กลาง

RTU (RS-485)≥ 3.5 อักขระเงียบ01Slave03Function00 00Start addr00 02QuantityC4 0BCRC lo hi≥ 3.5 อักขระเงียบTCP (พอร์ต 502)00 01Transaction00 00Protocol00 06Length01Unit03Function00 00 00 02DataPDU เดียวกัน · TCP ไม่มี CRC
รูป 2 · PDU (ฟังก์ชัน + ข้อมูล) เหมือนกันทั้งสองแบบ RTU ห่อด้วยแอดเดรสสเลฟกับ CRC ส่วน TCP ห่อด้วย MBAP header 7 ไบต์

D2 Modbus CRC-16 การตรวจสอบความผิดพลาด

g(x)=x16+x15+x2+1  (0x8005, กลับบิต 0xA001)ค่าเริ่ม 0xFFFF  ⋅  ส่งไบต์ต่ำก่อนg(x) = x^{16} + x^{15} + x^2 + 1 \;(\texttt{0x8005},\ \text{กลับบิต } \texttt{0xA001}) \qquad \text{ค่าเริ่ม } \texttt{0xFFFF} \;\cdot\; \text{ส่งไบต์ต่ำก่อน}
  1. ตั้ง CRC = 0xFFFF
  2. XOR ไบต์ข้อมูลเข้ากับไบต์ต่ำของ CRC
  3. เลื่อนขวา 8 ครั้ง ทุกครั้งที่บิตที่หลุดออกเป็น 1 ให้ XOR ด้วย 0xA001
  4. ทำซ้ำทุกไบต์ ผลลัพธ์ส่งไบต์ต่ำก่อน — คุณสมบัติที่ใช้ตรวจ: CRC ของทั้งเฟรมรวม CRC แล้วต้องได้ 0
ตัวอย่าง D-1

อ่าน 40001–40002 (float อุณหภูมิ) จากสเลฟ 1

  1. D1: 40001 → แอดเดรส 0x0000 · จำนวน 2 → PDU 03 00 00 00 02

  2. D2: CRC ของ 01 03 00 00 00 02 = 0x0BC4 → ส่ง C4 0B · คำขอเต็ม 01 03 00 00 00 02 C4 0B

  3. คำตอบ: 01 03 04 41 BE 00 00 8E 2B — byte count 04 แล้วตามด้วยข้อมูล 4 ไบต์

  4. D6: 41 BE 00 00 แบบ ABCD = 23.75 °C

Exception: สเลฟตอบด้วยฟังก์ชันโค้ด + 0x80 ตามด้วยรหัส
รหัสชื่อสาเหตุที่พบบ่อย
01ILLEGAL FUNCTIONอุปกรณ์ไม่รองรับฟังก์ชันนั้น เช่น ใช้ 03 กับอุปกรณ์ที่มีแต่ input register
02ILLEGAL DATA ADDRESSแอดเดรสเลื่อนหนึ่ง (0-based/1-based) หรืออ่านข้ามช่วงที่ไม่มี
03ILLEGAL DATA VALUEจำนวนเกิน 125 รีจิสเตอร์ หรือค่าที่เขียนนอกขอบเขต
04SERVER DEVICE FAILUREอุปกรณ์ทำคำสั่งไม่สำเร็จ

Modbus TCP ตัด CRC ออก (TCP/IP ตรวจความถูกต้องเอง) และเพิ่ม MBAP header: Transaction ID 2 ไบต์ (จับคู่คำถาม–คำตอบ) · Protocol ID = 0 · Length 2 ไบต์ · Unit ID 1 ไบต์ (ใช้เมื่อผ่านเกตเวย์ไปยังสเลฟ RTU) ใช้พอร์ต 502 ส่วน Modbus/TCP Security ห่อด้วย TLS ที่พอร์ต 802

เครื่องสร้างเฟรม Modbus กับทรานสมิตเตอร์เสมือน กลาง

เลือกฟังก์ชัน แอดเดรส และจำนวน แล้วดูคำขอทั้งแบบ RTU และ TCP พร้อมคำตอบจริงจากทรานสมิตเตอร์เสมือน ลองอ่านแอดเดรส 20 (ได้ exception 02) ลองใช้ฟังก์ชัน 01 (exception 01) และลองเขียนค่าเข้ารีจิสเตอร์ 9

เวลาบนสายและรอบการโพล กลาง

D3 Character time เวลาต่ออักขระ

tchar=11 bitbaud(1 start+8 data+1 parity+1 stop)t_\text{char} = \frac{11\ \text{bit}}{\text{baud}} \qquad (1\ \text{start} + 8\ \text{data} + 1\ \text{parity} + 1\ \text{stop})

D4 RTU framing gaps ช่องว่างระหว่างเฟรม

t3.5=3.5 tchart1.5=1.5 tcharbaud>19200: t3.5=1.75 ms, t1.5=0.75 mst_{3.5} = 3.5\,t_\text{char} \quad t_{1.5} = 1.5\,t_\text{char} \qquad \text{baud} > 19200:\ t_{3.5} = 1.75\ \text{ms},\ t_{1.5} = 0.75\ \text{ms}

D5 Polling cycle รอบโพลทั้งบัส

Tcycle≈∑slaves[(nreq+nresp) tchar+2t3.5+tproc]T_\text{cycle} \approx \sum_{\text{slaves}}\left[(n_\text{req} + n_\text{resp})\,t_\text{char} + 2t_{3.5} + t_\text{proc}\right]
ตัวอย่าง D-2

มิเตอร์ 8 ตัว อ่านตัวละ 10 รีจิสเตอร์ ที่ 19200 8E1

  1. D3: tchar=11/19200=573 μst_\text{char} = 11/19200 = 573\ \mu\text{s} · D4: t3.5=2.0 mst_{3.5} = 2.0\ \text{ms}

  2. คำขอ 8 ไบต์ คำตอบ 5+2(10)=255 + 2(10) = 25 ไบต์ → บนสาย 33×0.573=18.9 ms33 \times 0.573 = 18.9\ \text{ms}

  3. D5 (สเลฟประมวลผล 5 ms): 18.9+4.0+5=27.9 ms18.9 + 4.0 + 5 = 27.9\ \text{ms} ต่อตัว → 223 ms ต่อรอบ

  4. เพิ่มเป็น 115200 baud ได้ 93 ms — ไม่ลดลง 6 เท่าเพราะเวลาประมวลผลและ t3.5 ขั้นต่ำไม่ลดตาม Modbus RTU จึงเหมาะกับการเฝ้าดูและตั้งค่า ไม่ใช่การควบคุมความเร็วสูง

ค่า 32 บิตบนรีจิสเตอร์ 16 บิต: Float32 และลำดับคำ ขั้นสูง

D6 IEEE 754 single precision เลขทศนิยม 32 บิต

x=(−1)s×1.f×2 e−1270x41BE0000=1.484375×24=23.75x = (-1)^s \times 1.f \times 2^{\,e - 127} \qquad \texttt{0x41BE0000} = 1.484375 \times 2^4 = 23.75
ตัวอย่าง D-3

อ่านได้ N = 0x0000, N+1 = 0x41BE

  1. ตีความแบบ ABCD: 00 00 41 BE = 2.4 × 10⁻⁴¹ — เล็กจนไม่สมเหตุสมผล

  2. ตีความแบบ CDAB (สลับคำ): 41 BE 00 00 = 23.75 → อุปกรณ์นี้ส่งคำต่ำก่อน

  3. บันทึกลำดับคำไว้ในเอกสาร I/O list ทุกครั้ง เพราะอุปกรณ์รุ่นอื่นของผู้ผลิตเดียวกันอาจไม่เหมือนกัน

อุปกรณ์บางรุ่นเลี่ยงปัญหานี้ด้วยการส่งจำนวนเต็มที่ขยายสเกล เช่น 2375 แทน 23.75 (×100) ซึ่งต้องรู้ตัวคูณจากคู่มือ และระวังค่าลบ (int16 แบบ two's complement) กับค่าที่เกิน 32767

เช็กลิสต์แก้ปัญหา Modbus หน้างาน พื้นฐาน

อาการตรวจอะไร
ไม่ตอบเลย (timeout)แอดเดรสสเลฟ · baud/พาริตี/stop ตรงกันทุกตัว · สาย A/B สลับ · มีอุปกรณ์สองตัวใช้แอดเดรสเดียวกัน
ตอบบ้าง CRC ผิดบ้างตัวต้านทานปลายสายขาดหรือมีมากเกินไป · ต่อแบบดาว · ไม่มีไบแอส · สายใกล้สายกำลังหรือไดรฟ์
ได้ exception 02แอดเดรสเลื่อนหนึ่ง (0-based/1-based) · อ่านเกินช่วงที่อุปกรณ์มี
ค่าผิดแบบสุดขั้วลำดับคำ/ไบต์ของ float · ตัวคูณสเกล · signed/unsigned
ช้า ข้อมูลไม่ทันใจรวมการอ่านเป็นบล็อกเดียว (ลดจำนวนคำขอ) · ลด timeout · แบ่งบัสเป็นหลายพอร์ต

ข้อควรรู้ด้านความปลอดภัยไซเบอร์: Modbus แบบดั้งเดิมไม่มีการยืนยันตัวตน ใครเข้าถึงเครือข่ายได้ก็สั่งเขียนค่าได้ ระบบที่ต่ออินเทอร์เน็ตต้องแยกเครือข่ายและใช้แนวทางตามชุดมาตรฐาน IEC 62443 ต่อไป: ฟิลด์บัสและ Industrial Ethernet · ดูรีจิสเตอร์ที่อัปเดตสดใน แล็บวัดคุมเสมือน

แหล่งอ้างอิง

คำถามที่พบบ่อย

แอดเดรส 40001 ในคู่มือกับแอดเดรสในเฟรม Modbus ต่างกันอย่างไร?

40001 เป็นการเขียนแบบดั้งเดิมที่ตัวเลขแรก 4 บอกตาราง holding register และนับเริ่มจาก 1 ส่วนในเฟรมจริงส่งเฉพาะออฟเซ็ตที่นับจาก 0 ดังนั้น 40001 คือแอดเดรส 0x0000 และ 40010 คือ 0x0009 โปรแกรมหลายตัวให้เลือกว่าจะใช้แบบใด ความผิดพลาดที่พบบ่อยที่สุดคืออ่านเลื่อนไปหนึ่งรีจิสเตอร์

ทำไมอ่านค่า float จาก Modbus แล้วได้ตัวเลขแปลก ๆ?

Modbus กำหนดเพียงรีจิสเตอร์ 16 บิตแบบ big-endian ค่า float 32 บิตจึงต้องใช้สองรีจิสเตอร์ และผู้ผลิตแต่ละรายเรียงคำต่างกัน ลำดับที่พบบ่อยคือ ABCD (คำสูงก่อน) และ CDAB (คำต่ำก่อน) ถ้าเลือกผิด ค่าที่ได้จะเป็นเลขเล็กมากหรือใหญ่มากผิดปกติ วิธีแก้คือลองทุกลำดับกับค่าที่รู้ เช่น อุณหภูมิห้อง แล้วเลือกลำดับที่สมเหตุสมผล

RS-485 ต้องใส่ตัวต้านทานปลายสายที่ไหน?

ใส่ตัวต้านทานประมาณ 120 Ω (เท่ากับอิมพีแดนซ์ลักษณะของสาย) ที่ปลายทั้งสองของบัสเท่านั้น ไม่ใช่ทุกอุปกรณ์ และต่ออุปกรณ์แบบเดซี่เชนเรียงต่อกัน ไม่ต่อแบบดาว นอกจากนี้ควรมีไบแอส (pull-up/pull-down) ที่มาสเตอร์หนึ่งชุด เพื่อให้บัสอยู่ในสถานะที่แน่นอนเมื่อไม่มีใครส่ง