用AWS Lambda解析Modbus/RS-485感測器封包資料
工廠IT技術人員專用AWS LambdaModbus封包解析ATLANTIS 自有品牌
用AWS Lambda解析Modbus/RS-485感測器封包資料
在ATLANTIS的品牌故事裡,「Re-Atlantis」代表對精密秩序的重現——而在工業數據的世界中,精密不只在感測器本身,更在於資料被正確解讀的每一個環節。一個位元組順序判斷錯誤,可能讓68.4°C的讀值變成完全不同的數字。這正是本篇要仔細拆解的主題。詳見 ATLANTIS 品牌故事。
一、為什麼「解析」是資料管線中最容易出錯的一環?
在第一篇與第三篇的範例中,我們假設現場閘道器已經把資料整理成乾淨的JSON格式(例如{"temperature": 68.4})再發布上雲。但實務上,很多工廠的Modbus閘道器只負責「轉送原始暫存器數值」,真正的「數值換算」與「單位轉換」工作,其實落在Lambda這一層。這也是本篇要補齊的關鍵拼圖。
資料型態
位元組順序組合
足以讓讀值完全失真
封包完整性驗證機制
常見的解析錯誤場景
工廠IT技術人員在第一次處理Modbus封包解析時,最容易遇到三種狀況:把有號整數當成無號整數解析(導致負溫度顯示成一個超大正數)、32位元浮點數的兩個暫存器順序放反(導致數值完全錯亂),以及忘記除以換算比例(例如暫存器實際存的是684,代表68.4°C,換算比例為10)。本篇會逐一拆解這些情境的正確處理方式。
二、架構總覽:從原始封包到工程單位數值
本篇聚焦在③:Lambda函式如何把一串看似無意義的16進位字串,正確還原成有意義的工程單位數值。這一步做對了,後續的告警判斷、資料庫儲存、儀表板顯示才有意義;這一步做錯了,即使前面的雲端架構再完美,顯示出來的數字也是錯的。
三、Modbus暫存器資料型態完整對照表
在解析封包之前,第一件事是確認目標暫存器儲存的是哪一種資料型態。以下整理ATLANTIS產品規格書中常見的四種資料型態,以及它們在暫存器中的儲存方式。
| 資料型態 | 佔用暫存器數 | 數值範圍 | 常見應用 |
|---|---|---|---|
| UInt16(無號16位元整數) | 1個(16-bit) | 0~65535 | 單純正值溫度、狀態碼 |
| Int16(有號16位元整數) | 1個(16-bit) | -32768~32767 | 可能出現負值的溫度(如冷凍庫) |
| UInt32 / Int32(32位元整數) | 2個(合併為32-bit) | 依正負號而定,範圍更大 | 大範圍壓力值、累積流量 |
| Float32(IEEE-754浮點數) | 2個(合併為32-bit) | 依IEEE-754標準 | 高精度傳送器直接輸出工程單位值 |
值得特別注意的是Int16有號整數的處理:如果暫存器數值是0xFF38(十進位65336),若誤當成UInt16處理,會得到一個看似合理但完全錯誤的正值;正確做法應判斷最高位元是否為1,若是則需轉換為對應的負數(此例應為-200,代表-20.0°C,視換算比例而定)。
32位元浮點數的位元組順序(Byte Order / Word Order)
當一個數值需要用兩個16位元暫存器合併表示時,不同廠牌設備對「先放高位還是低位」的定義並不統一,業界常見四種排列組合:
| 順序代號 | 排列方式 | 常見於 |
|---|---|---|
| ABCD(Big-Endian) | 暫存器1高位元組在前,依序排列 | 部分歐系設備預設格式 |
| DCBA(Little-Endian) | 與ABCD完全相反的位元組順序 | 部分低階微控制器晶片 |
| BADC(Byte-Swap) | 每個暫存器內部位元組互換,暫存器順序不變 | 部分Modbus閘道器轉換行為 |
| CDAB(Word-Swap) | 暫存器順序互換,暫存器內部位元組不變 | ATLANTIS部分數位傳送器與多數工業慣例 |
務必在對照產品規格書後再撰寫解析程式——這是我們在協助工廠客戶除錯時,最常發現的問題來源。若不確定,可以先用已知數值(例如常溫下應顯示約25°C)反推目前使用的是哪一種排列組合。
四、Lambda解析範例:從16進位字串到工程單位數值
以下範例假設MQTT發布的payload中包含一個raw_hex欄位,儲存兩個暫存器合併後的4個位元組16進位字串(例如"43094CCD"代表一個Float32浮點數),Lambda函式負責解析並輸出正確的溫度數值。
範例一:解析Word-Swap(CDAB)格式的Float32溫度值
import struct
def parse_float32_cdab(raw_hex: str) -> float:
# raw_hex範例:"4C09CD43"(暫存器順序:CDAB,需重組為ABCD再轉換)
raw_bytes = bytes.fromhex(raw_hex)
# 將暫存器順序由CDAB重組回ABCD:交換前後兩個16-bit字組
reordered = raw_bytes[2:4] + raw_bytes[0:2]
value = struct.unpack('>f', reordered)[0]
return round(value, 2)
def lambda_handler(event, context):
raw_hex = event.get("raw_hex")
device_id = event.get("device_id", "unknown")
if not raw_hex:
return {"statusCode": 400, "body": "缺少raw_hex欄位"}
temperature = parse_float32_cdab(raw_hex)
print(f"裝置 {device_id} 解析後溫度:{temperature}°C")
return {
"statusCode": 200,
"body": json.dumps({"device_id": device_id, "temperature": temperature})
}
這段程式的關鍵在parse_float32_cdab函式:先把16進位字串轉成位元組(bytes),依CDAB規則重新排列成標準ABCD順序,再用Python內建的struct.unpack依IEEE-754標準還原成浮點數。如果你的設備使用不同的位元組順序,只需調整重組那一行的位元組切片方式。
範例二:處理有號整數(Int16)並套用換算比例
另一種常見情境是暫存器存的是簡單的有號整數,需要先判斷正負號,再乘上規格書標示的換算比例(例如除以10代表小數點一位)。
Int16有號整數轉換範例
# raw_value為Modbus讀取到的原始16-bit數值(0~65535)
if raw_value > 32767:
# 超過有號16位元最大正值,代表這是負數,需轉換
raw_value -= 65536
return raw_value / scale
# 範例:暫存器讀到 65336(0xFF38)
result = parse_int16_signed(65336, scale=10.0)
print(f"換算後溫度:{result}°C") # 輸出:-20.0°C
這個轉換邏輯的核心是if raw_value > 32767: raw_value -= 65536——這是處理「二補數(Two's Complement)」有號整數最簡潔的寫法。若你的Lambda函式需要同時處理多款不同型號的變送器,建議把每個型號的資料型態、位元組順序與換算比例整理成一份「裝置設定檔(Device Profile)」,如下一節示範。
五、進階:用裝置設定檔管理多種型號的解析邏輯
當工廠現場同時使用多款不同型號的ATLANTIS變送器時,把每個型號的解析規則寫死在程式碼中會讓維護變得困難。更好的做法是建立一份「裝置設定檔」,讓Lambda函式依device_model欄位動態查表決定解析方式。
範例三:裝置設定檔驅動的通用解析函式
# 裝置設定檔:型號 → 資料型態、位元組順序、換算比例
DEVICE_PROFILES = {
"STT": {"dtype": "float32_cdab", "scale": 1.0},
"DTT-P4": {"dtype": "int16_signed", "scale": 10.0},
"SDPT-3100": {"dtype": "float32_abcd", "scale": 1.0},
"THT-S81": {"dtype": "uint16", "scale": 10.0},
}
def parse_by_profile(device_model: str, raw_hex: str):
profile = DEVICE_PROFILES.get(device_model)
if not profile:
raise ValueError(f"未知裝置型號:{device_model}")
raw_bytes = bytes.fromhex(raw_hex)
dtype = profile["dtype"]
if dtype == "float32_cdab":
reordered = raw_bytes[2:4] + raw_bytes[0:2]
value = struct.unpack('>f', reordered)[0]
elif dtype == "float32_abcd":
value = struct.unpack('>f', raw_bytes)[0]
elif dtype == "int16_signed":
raw_int = struct.unpack('>h', raw_bytes[:2])[0]
value = raw_int / profile["scale"]
elif dtype == "uint16":
raw_int = struct.unpack('>H', raw_bytes[:2])[0]
value = raw_int / profile["scale"]
else:
raise ValueError(f"不支援的資料型態:{dtype}")
return round(value, 2)
def lambda_handler(event, context):
device_model = event.get("device_model")
raw_hex = event.get("raw_hex")
try:
parsed_value = parse_by_profile(device_model, raw_hex)
print(f"型號 {device_model} 解析結果:{parsed_value}")
return {"statusCode": 200, "parsed_value": parsed_value}
except ValueError as e:
print(f"解析失敗:{e}")
return {"statusCode": 500, "error": str(e)}
這種「設定檔驅動」的架構,讓你未來新增第五款、第六款變送器型號時,只需要在DEVICE_PROFILES字典裡新增一行設定,而不需要修改核心解析邏輯。這也是我們建議工廠IT技術人員在設計初期就採用的架構思維——為未來的擴充預留彈性。
六、封包完整性驗證:CRC16 校驗的角色
Modbus RTU封包本身在傳輸層已內建CRC16(循環冗餘校驗)機制,用來偵測傳輸過程中是否發生位元錯誤。雖然多數Modbus函式庫(如pymodbus)會在讀取階段自動驗證CRC並拋出錯誤,但若你的架構是「先由閘道器轉發原始位元組,再交給Lambda處理」,建議在Lambda端也加上一層驗證,避免處理到已損毀的資料。
簡易CRC16(Modbus)驗證函式
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x0001:
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
return crc
實務上,若你採用第一篇範例中的pymodbus函式庫在現場閘道器端讀取數據,CRC驗證已經在函式庫內部自動處理,通常不需要在Lambda端重複驗證;但若架構改為「轉發原始位元組供雲端解析」,這層驗證能提早攔截傳輸錯誤,避免把損毀數據誤判為異常告警。
七、ATLANTIS 支援多種暫存器資料型態的量測產品

DPS-2.5SPD3 多功能壓力開關 —— 可切換7種壓力單位,選配RS-485數位輸出,暫存器資料型態與換算比例詳列於產品規格書

THT-S81 室內溫濕度傳送器 —— 支援RS485 Modbus RTU,溫度與濕度分別以獨立暫存器輸出,適合示範多欄位解析邏輯

DMPT-301-5CD 數位微差壓傳送器 —— 適合空氣微壓測量與洩漏檢測,數位輸出可搭配Lambda解析後用於潔淨室壓差監控告警

AT803-D 高精度數位差壓錶 —— 精度達±0.02%F.S.,適合對暫存器解析精度要求極高的實驗室與校準應用場景
完整規格書請參考 ATLANTIS 產品型錄,暫存器位址對照表與資料型態說明可洽詢我們的應用工程團隊。
八、案例分享:某新竹科學園區廠務單位的多品牌設備整合解析
以下案例經匿名化處理,客戶為新竹科學園區一家半導體相關廠務單位,現場同時存在多個年份採購的溫度壓力變送器,型號與資料格式不完全一致。
| 問題現象 | 排查過程 | 根本原因 | 解決方式 |
|---|---|---|---|
| 部分裝置讀值長期偏高數千倍 | 比對規格書與實際暫存器數值 | Float32位元組順序誤用ABCD而非CDAB | 依裝置設定檔區分不同型號的解析規則 |
| 冷凍區溫度顯示異常大的正數 | 檢查原始暫存器16進位值 | 負溫度以Int16有號整數表示,程式誤用UInt16解析 | 加入二補數轉換邏輯(如本文範例二) |
| 偶發性數值跳動異常 | 加入CRC16驗證後比對錯誤發生頻率 | 部分RS-485線路接地不良,偶發位元錯誤 | 加上CRC驗證過濾損毀封包,並改善接地與屏蔽 |
資深工程師賴祥德分享:「封包解析的錯誤通常不會讓系統當掉,而是讓系統『安靜地顯示錯誤數字』——這比系統直接故障更危險,因為工程師往往要花很長時間才會發現。我們建議任何雲端監控專案上線前,都要準備幾組『已知正確答案』的測試數據,逐一驗證解析邏輯,而不是等上線後才發現數字對不上。」
資料來源與延伸閱讀
本文技術規範參考 Modbus組織發布之Modbus應用協定規範(modbus.org)、IEEE 754浮點數運算標準,以及 Python官方struct模組文件。CRC16演算法為Modbus RTU標準通用實作。ATLANTIS產品暫存器對照資訊引用自內部產品規格書。
十、20 大常見問題 FAQ(Modbus/RS-485封包解析)
1. 為什麼Modbus閘道器不直接幫我把數值換算好?
2. 怎麼知道我的設備是ABCD還是CDAB順序?
3. struct.unpack('>f', ...)裡面的'>'和'f'是什麼意思?
4. Int16和UInt16的差別,具體會造成多大的數值誤差?
5. 為什麼要用裝置設定檔(Device Profile),而不是為每個裝置寫一個函式?
6. CRC16驗證失敗時,Lambda函式應該怎麼處理?
7. 換算比例(scale)都是除以10嗎?
8. 32位元整數(Int32/UInt32)的解析方式跟Float32一樣嗎?
struct.unpack('>I', ...),Int32使用'>i',Float32使用'>f',需依實際資料型態選用正確的格式字元。9. 如果我完全不知道設備的資料格式,有辦法反推嗎?
10. 解析錯誤會不會影響前面幾篇文章教的告警邏輯?
11. 我可以把解析邏輯放在Modbus閘道器端,而不是Lambda嗎?
12. 有沒有現成的Python函式庫可以簡化這些解析工作?
struct標準函式庫外,也可以評估使用pymodbus本身提供的資料解碼工具(如BinaryPayloadDecoder),內建對多種位元組順序與資料型態的解析支援,可減少手動處理位元組排列的程式碼量。13. 為什麼要在Lambda裡用try/except包住解析邏輯?
14. round(value, 2)這樣四捨五入會不會影響精度判斷?
15. 不同批次生產的同型號感測器,資料格式會不會不一樣?
16. 解析後的數值要不要加上單位驗證(例如溫度不應超過材料極限)?
17. raw_hex欄位的長度不對時,程式會怎樣?
bytes.fromhex()若輸入的16進位字串長度不符合預期(如非偶數長度),會拋出例外;後續的struct.unpack若位元組數量與格式字元不匹配,也會拋出錯誤,這些都應該被try/except攔截並記錄,而非讓函式意外中斷。18. 這種封包解析的邏輯,適合放在哪一種Lambda觸發架構?
raw_hex與device_model等原始欄位,交由Lambda做解析與後續判斷,這也是本文架構與前三篇的銜接方式。19. 解析邏輯需要寫單元測試嗎?
20. 學會封包解析後,下一步該學什麼?
十一、下一步:讓 ATLANTIS 協助你確認產品的暫存器規格與解析邏輯
31年工業儀錶製造經驗 × 完整暫存器規格文件支援
從產品選型、暫存器位址對照,到Modbus封包解析邏輯驗證,我們可以陪工廠IT團隊完成資料管線最容易出錯的這一段。
📞 02-2820-3405 免費選型諮詢 📧 線上快速詢價
業務一部 Ian:ian@atlantis.com.tw | 業務二部 Nori:nori@atlantis.com.tw
文章更新時間:2026年7月|作者:ATLANTIS 應用工程團隊|本文為系列教學文章第四篇,下一篇將深入「溫度異常自動告警:Lambda + SNS 實作工廠即時警報系統」。