"# \u7ed9\u4e32\u53e3\u88c5\u4e0a\u5927\u8111\uff1aLLCOM Lua\u811a\u672c\u8c03\u8bd5\u5de5\u5177\n\n> 2025-12-22 | 2026-07-01\n> https:\/\/www.modbus.cn\/en\/37716.html\n\n**Modbus\u8c03\u8bd5\u5de5\u5177**\n\n---\n\n\u6765\u6e90\uff1aModbus\u4e2d\u6587\u7f51\uff08modbus.cn\uff09 \u2014\u2014 \u56fd\u5185\u9886\u5148\u7684Modbus\u901a\u4fe1\u534f\u8bae\u6280\u672f\u793e\u533a\n\n\u672c\u6587\uff1aLLCOM\uff1a\u7ed9\u4e32\u53e3\u88c5\u4e0a Lua \u5927\u8111 \u2014\u2014 Modbus \u8c03\u8bd5\u6548\u7387\u7ffb\u500d\u7684\u5b8c\u6574\u6307\u5357 \u00b7 \u4f5c\u8005\uff1amodbus\u6280\u672f\u56e2\u961f \u00b7 \u53d1\u5e03\u4e8e 2026-07-01\n\n\u6458\u8981\uff1aLLCOM \u662f GitHub \u5f00\u6e90\u7684 Windows \u4e32\u53e3\u8c03\u8bd5\u5de5\u5177\uff08chenxuuu\/llcom\uff09\uff0c\u57fa\u4e8e .NET \u548c\u817e\u8baf xlua \u5f15\u64ce\uff0c\u8ba9\u4e32\u53e3\u8c03\u8bd5\u4e0d\u518d\u662f\u624b\u52a8\u4e00\u6761\u6761\u53d1\u5341\u516d\u8fdb\u5236\u5e27\u3002\u5176\u6838\u5fc3\u6740\u624b\u950f\u662f\u5728\u6807\u51c6\u4e32\u53e3\u5de5\u5177\u57fa\u7840\u4e0a\u5185\u5d4c\u4e86\u5b8c\u6574\u7684 Lua 5.3 \u8fd0\u884c\u73af\u5883\uff0c\u652f\u6301\u5408\u5b99 Luat Task \u534f\u7a0b\u6846\u67b6\uff0c\u53ef\u4ee5\u5199\u811a\u672c\u81ea\u52a8\u6536\u53d1\u3001\u81ea\u52a8\u8ba1\u7b97 Modbus CRC\u3001\u81ea\u52a8\u8f6e\u8be2\u3001\u81ea\u52a8\u89e3\u6790\u54cd\u5e94\u3002\u672c\u6587\u4ece\u5b89\u88c5\u5230 API \u53c2\u8003\u5230\u56db\u4e2a Modbus \u5b9e\u6218\u811a\u672c\uff0c\u8986\u76d6\u4f60\u4ece\u4e0b\u8f7d\u5230\u8dd1\u901a\u81ea\u52a8\u5316\u6d4b\u8bd5\u7684\u6bcf\u4e00\u4e2a\u6b65\u9aa4\u3002\u5173\u952e\u8bcd\uff1aLLCOM\u3001Lua \u4e32\u53e3\u8c03\u8bd5\u3001Modbus CRC \u81ea\u52a8\u8ba1\u7b97\u3001\u4e32\u53e3\u81ea\u52a8\u5316\u6d4b\u8bd5\u3001xlua\u3001Luat Task\u3002\n\n\u8c03\u8bd5 Modbus RTU \u8bbe\u5907\u6700\u5e38\u89c1\u7684\u5de5\u4f5c\u6d41\u662f\uff1a\u6253\u5f00\u4e32\u53e3\u5de5\u5177 \u2192 \u624b\u52a8\u8f93\u5165\u5341\u516d\u8fdb\u5236\u5e27 \u2192 \u70b9\u53d1\u9001 \u2192 \u7b49\u56de\u5e94 \u2192 \u8089\u773c\u5bf9\u7167\u5b57\u8282 \u2192 \u53d1\u73b0 CRC \u7b97\u9519\u4e86 \u2192 \u91cd\u65b0\u6765\u3002\u5341\u53f0\u8bbe\u5907\u8c03\u5b8c\uff0c\u624b\u6307\u75db\u3001\u773c\u775b\u9178\u3001\u5fc3\u91cc\u788e\u6210\u6e23\u3002\n\nLLCOM \u89e3\u51b3\u7684\u5c31\u662f\u8fd9\u4e2a\u95ee\u9898\u3002\u5b83\u4e0d\u53ea\u662f\u4e00\u4e2a\u4e32\u53e3\u52a9\u624b\u2014\u2014\u5b83\u662f\u4e2a\u80fd\u8dd1\u811a\u672c\u7684\u4e32\u53e3\u8c03\u8bd5\u5f15\u64ce\u3002\n\n## \u4e00\u3001LLCOM \u662f\u4ec0\u4e48\n\nLLCOM \u662f GitHub \u5f00\u6e90\u9879\u76ee\uff08chenxuuu\/llcom\uff09\uff0c\u7528 C# \u5199\u3001\u57fa\u4e8e .NET \u6846\u67b6\uff0c\u91c7\u7528\u817e\u8baf xlua \u4f5c\u4e3a\u811a\u672c\u5f15\u64ce\u3002\u8bb8\u53ef\u8bc1\u662f Apache 2.0\u3002\n\n\u5b9a\u4f4d\u4e00\u53e5\u8bdd\uff1a\u53ef\u7f16\u7a0b\u7684\u4e32\u53e3\u8c03\u8bd5\u5de5\u5177\u3002\u666e\u901a\u7684\u4e32\u53e3\u52a9\u624b\u53ea\u80fd\u300c\u952e\u76d8\u6253\u5341\u516d\u8fdb\u5236\u2192\u624b\u70b9\u53d1\u9001\u2192\u8089\u773c\u770b\u51fa\u5341\u516d\u8fdb\u5236\u300d\u3002LLCOM \u53ef\u4ee5\u5199 Lua \u811a\u672c\u8ba9\u6570\u636e\u81ea\u52a8\u5904\u7406\u3001\u81ea\u52a8\u53d1\u9001\u3001\u81ea\u52a8\u89e3\u6790\u3001\u81ea\u52a8\u6821\u9a8c\u3002\n\n\u6838\u5fc3\u67b6\u6784\u4e09\u5c42\uff1a\n\n- **\u4e32\u53e3\u6536\u53d1\u5c42**\uff1a\u6807\u51c6\u4e32\u53e3\u901a\u4fe1\uff0c\u652f\u6301\u6ce2\u7279\u7387\u3001\u6570\u636e\u4f4d\u3001\u6821\u9a8c\u4f4d\u914d\u7f6e\uff0c\u540c\u65f6\u96c6\u6210 TCP\/UDP\/MQTT \u5ba2\u6237\u7aef\u548c\u670d\u52a1\u5668\u529f\u80fd\uff0c\u8fd8\u80fd\u76d1\u542c\u5176\u4ed6\u7a0b\u5e8f\u4f7f\u7528\u7684\u4e32\u53e3\n\n- **Lua \u811a\u672c\u5f15\u64ce\u5c42**\uff1aLua 5.3 \u73af\u5883 + \u5408\u5b99 Luat Task \u534f\u7a0b\u6846\u67b6\uff08sys.wait\u3001sys.taskInit\u3001sys.timerLoopStart \u7b49\u5168\u90e8\u79fb\u690d\uff09\n\n- **\u53d1\u9001\u524d\u5904\u7406\u5c42**\uff1a\u7528\u6237\u5728\u53d1\u9001\u6846\u4e2d\u8f93\u5165\u7684\u6570\u636e\uff0c\u53ef\u4ee5\u7ecf\u8fc7\u4e00\u6bb5 Lua \u811a\u672c\u5904\u7406\u540e\u518d\u53d1\u51fa\u53bb\u2014\u2014\u81ea\u52a8\u6362\u884c\u3001\u81ea\u52a8\u8f6c Hex\u3001\u81ea\u52a8\u7ec4\u88c5 JSON\uff0c\u4ec0\u4e48\u90fd\u80fd\u5e72\n\n\u5f00\u6e90\u5730\u5740\uff1ahttps:\/\/github.com\/chenxuuu\/llcom\n\n## \u4e8c\u3001\u5b89\u88c5\u4e0e\u4e0b\u8f7d\n\n\u4e09\u4e2a\u5b98\u65b9\u6e20\u9053\uff0c\u9009\u4e00\u4e2a\uff1a\n\n**\u65b9\u5f0f\u4e00\uff1a\u5fae\u8f6f\u5546\u5e97** \u5728 Windows \u81ea\u5e26\u7684 Microsoft Store \u4e2d\u641c\u7d22\u300cLLCOM\u300d\u5373\u53ef\u5b89\u88c5\u3002\u4f18\u52bf\u662f\u81ea\u52a8\u66f4\u65b0\uff0c\u7f3a\u70b9\u662f\u9700\u8981\u5fae\u8f6f\u8d26\u53f7\u3002\n\n**\u65b9\u5f0f\u4e8c\uff1a\u4fbf\u643a\u7248\uff08\u514d\u5b89\u88c5\uff09** \u4ece \u5e95\u90e8 \u4e0b\u8f7d zip \u5305\uff0c\u89e3\u538b\u5230\u4efb\u610f\u76ee\u5f55\uff0c\u53cc\u51fb llcom.exe \u8fd0\u884c\u3002\u4e0d\u4e0a\u6ce8\u518c\u8868\u3001\u4e0d\u5199\u7cfb\u7edf\u76ee\u5f55\uff0c\u653e\u5230 U \u76d8\u91cc\u5230\u5904\u5e26\u7740\u8dd1\u3002\n\n**\u65b9\u5f0f\u4e09\uff1aGitHub Releases** \u4ece https:\/\/github.com\/chenxuuu\/llcom\/releases\/latest \u4e0b\u8f7d\u6700\u65b0\u7a33\u5b9a\u7248\u3002\u9002\u5408\u9700\u8981\u7279\u5b9a\u7248\u672c\u7684\u8c03\u8bd5\u573a\u666f\u3002\n\n\u7cfb\u7edf\u8981\u6c42\uff1aWindows 10 \u53ca\u4ee5\u4e0a\uff0c64 \u4f4d\u3002\u9700\u8981 .NET Runtime\uff08Windows 10\/11 \u901a\u5e38\u5df2\u5185\u7f6e\uff1b\u5982\u679c\u63d0\u793a\u7f3a\u5c11 .NET Runtime\uff0c\u4ece\u5fae\u8f6f\u5b98\u7f51\u4e0b\u8f7d .NET Desktop Runtime 8.0 \u5373\u53ef\uff09\u3002\n\n## \u4e09\u3001\u529f\u80fd\u6e05\u5355\uff1a\u6bd4\u666e\u901a\u4e32\u53e3\u52a9\u624b\u591a\u4e86\u4ec0\u4e48\n\nLLCOM \u6709\u666e\u901a\u4e32\u53e3\u52a9\u624b\u7684\u6240\u6709\u529f\u80fd\uff08\u4e32\u53e3\u9009\u62e9\u3001\u6ce2\u7279\u7387\/\u6821\u9a8c\u4f4d\u914d\u7f6e\u3001\u5341\u516d\u8fdb\u5236\/ASCII \u6536\u53d1\u3001\u65e5\u5fd7\u663e\u793a\uff09\uff0c\u7136\u540e\u52a0\u4e86\u8fd9\u4e9b\uff1a\n\n\u529f\u80fd\u8bf4\u660eModbus \u8c03\u8bd5\u573a\u666fLua \u53d1\u9001\u524d\u5904\u7406\u8f93\u5165\u6846\u5185\u5bb9\u7ecf\u8fc7 Lua \u811a\u672c\u5904\u7406\u540e\u624d\u53d1\u9001\u81ea\u52a8\u62fc\u63a5 CRC\u3001\u81ea\u52a8\u52a0\u4ece\u7ad9\u5730\u5740\u72ec\u7acb Lua \u811a\u672c\u533a\u53f3\u4fa7\u53ef\u8dd1\u5b8c\u6574\u7684 Lua \u811a\u672c\uff0c\u5e26\u5b9a\u65f6\u5668\u548c\u534f\u7a0b\u5199\u4e00\u4e2a\u811a\u672c\u81ea\u52a8\u8f6e\u8be2\u6240\u6709\u4ece\u7ad9\u5bc4\u5b58\u5668\u5feb\u6377\u53d1\u9001\u680f\uff0810 \u9875\u00d7\u4e0d\u9650\u6761\u6570\uff09\u4fdd\u5b58\u5e38\u7528\u547d\u4ee4\uff0c\u4e00\u952e\u53d1\u9001\u628a\u8bfb\u4e0d\u540c\u5bc4\u5b58\u5668\u7684\u5e27\u653e\u4e0d\u540c\u9875\u4e32\u53e3\u76d1\u542c\u76d1\u63a7\u5176\u4ed6\u7a0b\u5e8f\u6b63\u5728\u4f7f\u7528\u7684\u4e32\u53e3\u6570\u636e\u770b\u67d0\u4e0a\u4f4d\u673a\u8f6f\u4ef6\u53d1\u4e86\u4ec0\u4e48\u5e27TCP\/UDP\/SSL \u5ba2\u6237\u7aef\u548c\u670d\u52a1\u7aef\u53ef\u4ee5\u4e0d\u901a\u8fc7\u4e32\u53e3\u76f4\u63a5\u8c03 TCP\u6d4b\u8bd5 Modbus TCP \u7aef\u53e3MQTT \u6d4b\u8bd5\u96c6\u6210 MQTT \u53d1\u5e03\/\u8ba2\u9605\u9a8c\u8bc1\u7f51\u5173\u7684 MQTT \u4e0a\u884c\u4e32\u53e3\u81ea\u52a8\u91cd\u8fde\u65ad\u5f00\u540e\u81ea\u52a8\u6062\u590d\u8fde\u63a5\u8bbe\u5907\u65ad\u7535\u91cd\u542f\u4e0d\u4f1a\u65ad\u8c03\u8bd5\u81ea\u52a8\u65e5\u5fd7\u4fdd\u5b58\u5e26\u65f6\u95f4\u6233\u7684\u4e32\u53e3\u548c Lua \u65e5\u5fd7\u8c03\u5b8c\u5168\u90e8\u8bbe\u5907\uff0c\u56de\u5934\u770b\u65e5\u5fd7\u590d\u76d8\u7f16\u7801\u4e92\u8f6c\/\u4e71\u7801\u6062\u590dGBK\/UTF-8\/Hex \u4e4b\u95f4\u8f6c\u6362\u4e2d\u6587\u5b57\u7b26\u4e32\u8c03\u8bd5\n\n## \u56db\u3001\u4e24\u5927\u6838\u5fc3\u529f\u80fd\u8be6\u89e3\n\n### 4.1 \u53d1\u9001\u524d Lua \u5904\u7406\n\n\u8fd9\u662f LLCOM \u6700\u76f4\u63a5\u63d0\u6548\u7684\u529f\u80fd\u3002\u5728\u300c\u53d1\u9001\u8bbe\u7f6e\u300d\u2192\u300c\u53d1\u9001\u5904\u7406\u811a\u672c\u300d\u91cc\u5199\u4e00\u6bb5 Lua\uff0c\u6bcf\u6b21\u70b9\u53d1\u9001\u65f6\uff0c\u4f60\u8f93\u5165\u6846\u91cc\u7684\u539f\u59cb\u6587\u672c\u5148\u4f20\u7ed9\u8fd9\u6bb5\u811a\u672c\uff0c\u811a\u672c\u5904\u7406\u540e\u8fd4\u56de\u7684\u5b57\u7b26\u4e32\u624d\u662f\u771f\u6b63\u53d1\u5230\u4e32\u53e3\u7684\u6570\u636e\u3002\n\n\u4e09\u4e2a\u7ecf\u5178\u7528\u6cd5\uff1a\n\n**\u81ea\u52a8\u52a0 `rn`**\uff1a\u5f88\u591a\u4e32\u53e3\u8bbe\u5907\u8981\u6c42\u547d\u4ee4\u4ee5\u56de\u8f66\u6362\u884c\u7ed3\u5c3e\u2014\u2014\n\n```\n`return uartData .. \"rn\"`\n```\n\n\u4ece\u6b64\u4e0d\u7528\u6bcf\u6b21\u624b\u6253 0D 0A\uff0c\u8f93\u5165\u547d\u4ee4\u76f4\u63a5\u70b9\u53d1\u9001\u5c31\u5b8c\u4e8b\u3002\n\n**\u5341\u516d\u8fdb\u5236\u81ea\u52a8\u8f6c\u6362**\uff1a\u5728\u8f93\u5165\u6846\u91cc\u5199\u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\uff08\u5982 `010300000001`\uff09\uff0c\u811a\u672c\u81ea\u52a8\u8f6c\u6210\u4e8c\u8fdb\u5236\u2014\u2014\n\n```\n`return uartData:fromHex()`\n```\n\n\u8fd9\u662f Modbus RTU \u8c03\u8bd5\u7684\u6807\u914d\u2014\u2014\u4f60\u5728\u8f93\u5165\u6846\u5199 Modbus \u5e27\u7684\u5341\u516d\u8fdb\u5236\u8868\u793a\uff0c\u811a\u672c\u8f6c\u6210\u4e8c\u8fdb\u5236\u53d1\u7ed9\u8bbe\u5907\u3002\n\n**JSON \u81ea\u52a8\u7ec4\u88c5**\uff1a\u8f93\u5165 `a,b,c`\uff0c\u53d1\u51fa\u53bb\u7684\u662f JSON\u2014\u2014\n\n```\njson = require(\"JSON\")\nt = uartData:split(\",\")\nreturn json:encode({\n    key1 = t&#91;1],\n    key2 = t&#91;2],\n    key3 = t&#91;3],\n})\n```\n\n\u5feb\u6377\u53d1\u9001\u680f\u7684\u6bcf\u4e2a\u6309\u94ae\u4e5f\u540c\u6837\u8d70\u8fd9\u4e2a\u811a\u672c\uff0c\u6240\u4ee5\u4f60\u53ef\u4ee5\u5efa 10 \u9875\u5feb\u6377\u53d1\u9001\uff0c\u6bcf\u9875\u653e\u4e0d\u540c\u7684 Modbus \u547d\u4ee4\uff0c\u7136\u540e\u6bcf\u6b21\u70b9\u6309\u94ae\u81ea\u52a8\u52a0 CRC \u53d1\u51fa\u53bb\u2014\u2014\u540e\u9762\u4f1a\u7ed9\u51fa\u5b8c\u6574\u5b9e\u73b0\u3002\n\n### 4.2 \u72ec\u7acb Lua \u811a\u672c\u8fd0\u884c\u533a\n\n\u53f3\u8fb9\u6709\u4e2a\u300cLua \u811a\u672c\u300d\u533a\u57df\uff0c\u4f60\u53ef\u4ee5\u76f4\u63a5\u5199\u5b8c\u6574\u7684 Lua \u7a0b\u5e8f\u5e76\u8fd0\u884c\u3002\u8fd9\u91cc\u7684\u811a\u672c\u62e5\u6709\u5408\u5b99 Luat Task \u6846\u67b6\u7684\u5168\u90e8\u80fd\u529b\uff1a\n\n- `sys.taskInit()` \u521b\u5efa\u534f\u7a0b\u4efb\u52a1\n\n- `sys.wait(ms)` \u5ef6\u65f6\u7b49\u5f85\uff08\u4e0d\u963b\u585e\u754c\u9762\uff09\n\n- `sys.waitUntil()` \u7b49\u5f85\u6d88\u606f\uff08\u4e8b\u4ef6\u9a71\u52a8\uff09\n\n- `sys.timerLoopStart()` \u5faa\u73af\u5b9a\u65f6\u5668\n\n- `sys.publish()` \/ `sys.subscribe()` \u6d88\u606f\u53d1\u5e03\u8ba2\u9605\n\n\u57fa\u7840\u793a\u4f8b\uff1a\u6536\u5230\u4e32\u53e3\u6570\u636e\u540e\u81ea\u52a8\u56de\u590d\u2014\u2014\n\n```\nuartReceive = function (data)\n    log.info(\"uartReceive\", data)\n    sys.publish(\"UART\", data)\nend\n\nsys.taskInit(function()\n    while true do\n        local _, udata = sys.waitUntil(\"UART\")\n        log.info(\"task waitUntil\", udata)\n        local sendResult = apiSendUartData(\"ok!\")\n        log.info(\"uart send\", sendResult)\n    end\nend)\n```\n\n\u4f60\u751a\u81f3\u53ef\u4ee5\u7528 xlua \u76f4\u63a5\u8c03\u7528 C# \u7684 System.Net \u7c7b\u5e93\u53d1 HTTP \u8bf7\u6c42\u2014\u2014\u8fd9\u610f\u5473\u7740\u4f60\u53ef\u4ee5\u5199\u4e00\u4e2a\u811a\u672c\uff0c\u4e32\u53e3\u6536\u5230\u6570\u636e\u540e\u81ea\u52a8\u5f80\u4e91\u5e73\u53f0\u63a8\u4e00\u6761\u6d88\u606f\u3002\n\n## \u4e94\u3001\u6838\u5fc3 API \u901f\u67e5\n\nLLCOM \u7684 Lua API \u6587\u6863\u4f4d\u4e8e https:\/\/github.com\/chenxuuu\/llcom\/blob\/master\/LuaApi.md \uff0c\u8fd9\u91cc\u5217\u51fa Modbus \u8c03\u8bd5\u6700\u5e38\u7528\u7684\u90e8\u5206\u3002\n\n### 5.1 \u4e32\u53e3\u6536\u53d1 API\n\n```\n-- \u53d1\u9001\u6570\u636e\u5230\u4e32\u53e3\uff08\u63a8\u8350\u65b0\u63a5\u53e3\uff09\napiSend(\"uart\", data)\n\n-- \u53d1\u9001\u6570\u636e\u5230\u4e32\u53e3\uff08\u65e7\u63a5\u53e3\uff0c\u540e\u7eed\u4f1a\u79fb\u9664\uff09\napiSendUartData(data)\n\n-- \u8ba2\u9605\u4e32\u53e3\u63a5\u6536\u56de\u8c03\napiSetCb(\"uart\", function(data)\n    log.info(\"received\", data)\nend)\n\n-- \u53d6\u6d88\u8ba2\u9605\napiUnsetCb(\"uart\", callback)\n```\n\n### 5.2 \u5b9a\u65f6\u5668\u4e0e\u534f\u7a0b API\n\n```\n-- \u521b\u5efa\u4efb\u52a1\nsys.taskInit(function()\n    while true do\n        -- \u4f60\u7684\u903b\u8f91\n        sys.wait(1000)  -- \u7b49 1000ms\n    end\nend)\n\n-- \u5faa\u73af\u5b9a\u65f6\u5668\nsys.timerLoopStart(function()\n    log.info(\"tick\", os.time())\nend, 1000)  -- \u6bcf 1000ms \u89e6\u53d1\u4e00\u6b21\n\n-- \u505c\u6b62\u5b9a\u65f6\u5668\nsys.timerStop(timerId)\n```\n\n### 5.3 \u5b57\u7b26\u4e32\u5904\u7406 API\n\n```\n-- \u4e8c\u8fdb\u5236\u8f6c\u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\nstring.toHex(\"x01x03\")  -- \u8fd4\u56de \"0103\"\n\n-- \u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\u8f6c\u4e8c\u8fdb\u5236\n(\"010300000001\"):fromHex()\n\n-- \u5206\u5272\u5b57\u7b26\u4e32\n(\"a,b,c\"):split(\",\")  -- \u8fd4\u56de {\"a\",\"b\",\"c\"}\n```\n\n### 5.4 \u65e5\u5fd7 API\n\n```\nlog.info(\"tag\", \"message\")\nlog.warn(\"tag\", \"warning\")\nlog.error(\"tag\", \"error\")\n```\n\n\u6240\u6709\u65e5\u5fd7\u81ea\u52a8\u4fdd\u5b58\u5230\u8f6f\u4ef6\u76ee\u5f55\uff0c\u5e76\u9644\u5e26\u65f6\u95f4\u6233\u3002\n\n## \u516d\u3001Modbus RTU \u5b9e\u6218\u811a\u672c\n\n\u4ee5\u4e0b\u811a\u672c\u53ef\u4ee5\u76f4\u63a5\u590d\u5236\u5230 LLCOM \u91cc\u8fd0\u884c\u3002\u6bcf\u4e2a\u90fd\u662f\u5b8c\u6574\u53ef\u7528\u7684\u3002\n\n### 6.1 \u667a\u80fd\u53d1\u9001\uff1a\u81ea\u52a8\u8ba1\u7b97 Modbus CRC \u5e76\u53d1\u9001\n\nTired of \u624b\u52a8\u7b97 CRC\uff1f\u8fd9\u662f LLCOM \u5728 Modbus \u8c03\u8bd5\u4e2d\u6700\u7ecf\u5178\u7684\u5e94\u7528\u3002\n\n\u5728\u300c\u53d1\u9001\u5904\u7406\u811a\u672c\u300d\u4e2d\u7c98\u8d34\uff1a\n\n```\n-- Modbus CRC16 \u67e5\u8868\u6cd5\uff08\u591a\u9879\u5f0f 0xA001\uff09\nlocal crcTable = {}\nfor i = 0, 255 do\n    local crc = i\n    for _ = 1, 8 do\n        if (crc &amp; 1) ~= 0 then\n            crc = (crc &gt;&gt; 1) ~ 0xA001\n        else\n            crc = crc &gt;&gt; 1\n        end\n    end\n    crcTable&#91;i] = crc\nend\n\nlocal function modbusCrc16(data)\n    local crc = 0xFFFF\n    for i = 1, #data do\n        local b = string.byte(data, i)\n        crc = (crc &gt;&gt; 8) ~ crcTable&#91;(crc ~ b) &amp; 0xFF]\n    end\n    return crc\nend\n\n-- \u4e3b\u903b\u8f91\uff1a\u628a\u8f93\u5165\u8f6c\u6210\u4e8c\u8fdb\u5236\uff0c\u8ba1\u7b97 CRC\uff0c\u62fc\u4e0a\u53bb\uff0c\u53d1\u7ed9\u4e32\u53e3\nlocal raw = uartData:fromHex()  -- \u8f93\u5165\u6846\u5199\u5341\u516d\u8fdb\u5236\uff0c\u5982 010300000001\nlocal crc = modbusCrc16(raw)\nlocal lo = crc &amp; 0xFF\nlocal hi = (crc &gt;&gt; 8) &amp; 0xFF\nreturn raw .. string.char(lo, hi)\n```\n\n\u73b0\u5728\u4f60\u5728\u8f93\u5165\u6846\u5199 `010300000001`\uff08\u4ece\u7ad9\u5730\u5740=01\uff0c\u529f\u80fd\u7801=03\uff0c\u8d77\u59cb\u5730\u5740=0000\uff0c\u5bc4\u5b58\u5668\u6570=0001\uff09\uff0c\u70b9\u53d1\u9001\u540e\u5b9e\u9645\u53d1\u51fa\u53bb\u7684\u662f `01 03 00 00 00 01 84 0A`\u2014\u2014CRC \u81ea\u52a8\u63a5\u5728\u540e\u9762\u3002\n\n### 6.2 Modbus RTU \u81ea\u52a8\u8f6e\u8be2\u811a\u672c\n\n\u8dd1\u5728\u300cLua \u811a\u672c\u300d\u533a\u57df\u4e2d\uff0c\u6bcf\u9694 2 \u79d2\u8bfb\u4e00\u6b21\u4ece\u7ad9 01 \u7684\u4fdd\u6301\u5bc4\u5b58\u5668 40001\uff08\u5730\u5740 0x0000\uff09\uff0c\u81ea\u52a8\u89e3\u6790\u8fd4\u56de\u6570\u636e\uff1a\n\n```\n-- CRC \u8868\uff08\u540c\u4e0a\uff0c\u8fd9\u91cc\u7701\u7565\uff0c\u9700\u8981\u653e\u5728\u811a\u672c\u6700\u524d\u9762\uff09\n-- ...\uff08\u548c 6.1 \u7684 crcTable \u548c modbusCrc16 \u51fd\u6570\u4e00\u6837\uff09\n\nlocal function buildReadFrame(slave, startAddr, regCount)\n    local frame = string.char(slave, 0x03,\n        (startAddr &gt;&gt; 8) &amp; 0xFF, startAddr &amp; 0xFF,\n        (regCount &gt;&gt; 8) &amp; 0xFF, regCount &amp; 0xFF)\n    local crc = modbusCrc16(frame)\n    return frame .. string.char(crc &amp; 0xFF, (crc &gt;&gt; 8) &amp; 0xFF)\nend\n\nlocal function parseResponse(data)\n    if #data &lt; 5 then\n        log.warn(\"modbus\", \"\u54cd\u5e94\u592a\u77ed: \" .. string.toHex(data))\n        return nil\n    end\n    local slave = string.byte(data, 1)\n    local func = string.byte(data, 2)\n    if func &gt; 0x80 then\n        log.error(\"modbus\", \"\u5f02\u5e38\u7801: 0x\" .. string.format(\"%02X\", string.byte(data, 3)))\n        return nil\n    end\n    local byteCount = string.byte(data, 3)\n    local values = {}\n    for i = 0, (byteCount \/ 2) - 1 do\n        local hi = string.byte(data, 4 + i * 2)\n        local lo = string.byte(data, 5 + i * 2)\n        values&#91;i + 1] = (hi &lt;&lt; 8) | lo\n    end\n    return {slave = slave, values = values}\nend\n\n-- \u63a5\u6536\u5904\u7406\napiSetCb(\"uart\", function(data)\n    local result = parseResponse(data)\n    if result then\n        log.info(\"modbus\", string.format(\"\u4ece\u7ad9%02X \u5bc4\u5b58\u5668\u503c: %s\",\n            result.slave, table.concat(result.values, \", \")))\n    end\nend)\n\n-- \u8f6e\u8be2\u4efb\u52a1\nsys.taskInit(function()\n    while true do\n        local frame = buildReadFrame(0x01, 0x0000, 4)\n        log.info(\"modbus\", \"\u53d1\u9001: \" .. string.toHex(frame))\n        apiSend(\"uart\", frame)\n        sys.wait(2000)\n    end\nend)\n```\n\n\u8fd0\u884c\u540e\u65e5\u5fd7\u8f93\u51fa\u7c7b\u4f3c\uff1a`modbus \u53d1\u9001: 0103000000044409` \u2192 `modbus \u4ece\u7ad901 \u5bc4\u5b58\u5668\u503c: 168, 2234, 0, 512`\u3002\u4e0d\u9700\u8981\u4eba\u5de5\u5e72\u9884\uff0c\u811a\u672c\u81ea\u52a8\u8f6e\u8be2\u3001\u81ea\u52a8\u89e3\u6790\u3001\u81ea\u52a8\u6253\u65e5\u5fd7\u3002\n\n### 6.3 \u591a\u4ece\u7ad9\u626b\u63cf\u5668\n\n\u81ea\u52a8\u626b\u63cf\u603b\u7ebf\u4e0a\u6709\u54ea\u4e9b\u4ece\u7ad9\u5728\u7ebf\u3002\u6328\u4e2a\u53d1\u8bfb\u53d6\u547d\u4ee4\uff0c\u6709\u54cd\u5e94\u7684\u5c31\u662f\u5728\u7ebf\uff1a\n\n```\n-- CRC \u51fd\u6570\u540c\u4e0a\uff0c\u7701\u7565\n\nlocal function scanSlave(slaveId)\n    local frame = string.char(slaveId, 0x03, 0x00, 0x00, 0x00, 0x01)\n    local crc = modbusCrc16(frame)\n    frame = frame .. string.char(crc &amp; 0xFF, (crc &gt;&gt; 8) &amp; 0xFF)\n\n    apiSetCb(\"uart\", function(data)\n        if #data &gt;= 5 and string.byte(data, 1) == slaveId\n            and string.byte(data, 2) == 0x03 then\n            log.info(\"scan\", \"\u4ece\u7ad9 \" .. slaveId .. \" \u5728\u7ebf\u2713\")\n        end\n    end)\n\n    apiSend(\"uart\", frame)\n    log.info(\"scan\", \"\u626b\u63cf\u4ece\u7ad9 \" .. slaveId .. \" ...\")\nend\n\nsys.taskInit(function()\n    for slaveId = 1, 247 do\n        scanSlave(slaveId)\n        sys.wait(150)  -- \u7b49 150ms \u7ed9\u4ece\u7ad9\u54cd\u5e94\u65f6\u95f4\n    end\n    log.info(\"scan\", \"\u626b\u63cf\u5b8c\u6210\")\nend)\n```\n\n\u811a\u672c\u8dd1\u5b8c\uff0c\u65e5\u5fd7\u91cc\u5217\u51fa\u6240\u6709\u5728\u7ebf\u7684\u4ece\u7ad9\u5730\u5740\u3002\u5927\u578b\u9879\u76ee\u4e2d\u7b2c\u4e00\u6b21\u4e0a\u7535\u540e\u8dd1\u4e00\u904d\u8fd9\u4e2a\uff0c\u7701\u53bb\u6328\u4e2a\u770b\u8bf4\u660e\u4e66\u7684\u529f\u592b\u3002\n\n### 6.4 \u54cd\u5e94\u5e27\u81ea\u52a8\u89e3\u6790 + \u6570\u636e\u5bfc\u51fa\n\n\u6536\u5230 Modbus \u54cd\u5e94\u540e\uff0c\u81ea\u52a8\u89e3\u6790\u5e76\u683c\u5f0f\u5316\u4e3a CSV \u884c\uff0c\u65b9\u4fbf\u590d\u5236\u5230 Excel \u505a\u8d8b\u52bf\u5206\u6790\uff1a\n\n```\nlocal results = {}\n\napiSetCb(\"uart\", function(data)\n    if #data &lt; 5 then return end\n    local slave = string.byte(data, 1)\n    local func = string.byte(data, 2)\n    if func &gt;= 0x80 then\n        log.error(\"modbus\", string.format(\"S=%02X \u5f02\u5e38\u78010x%02X\", slave, string.byte(data, 3)))\n        return\n    end\n    local byteCount = string.byte(data, 3)\n    local row = {os.date(\"%H:%M:%S\"), slave}\n    for i = 0, (byteCount \/ 2) - 1 do\n        local hi = string.byte(data, 4 + i * 2)\n        local lo = string.byte(data, 5 + i * 2)\n        table.insert(row, (hi &lt;&lt; 8) | lo)\n    end\n    table.insert(results, table.concat(row, \",\"))\n    log.info(\"csv\", table.concat(row, \",\"))\nend)\n\n-- \u8f6e\u8be2\u4efb\u52a1\uff08\u6bcf 5 \u79d2\u91c7\u96c6\u4e00\u6b21\uff0c\u5171\u91c7\u96c6 60 \u6b21 = 5 \u5206\u949f\uff09\nsys.taskInit(function()\n    for i = 1, 60 do\n        local frame = string.char(0x01, 0x03, 0x00, 0x00, 0x00, 0x04)\n        local crc = modbusCrc16(frame)\n        apiSend(\"uart\", frame .. string.char(crc &amp; 0xFF, (crc &gt;&gt; 8) &amp; 0xFF))\n        sys.wait(5000)\n    end\n    log.info(\"export\", \"\u91c7\u96c6\u5b8c\u6210\uff0c\u5171 \" .. #results .. \" \u884c\")\nend)\n```\n\n\u65e5\u5fd7\u8f93\u51fa\u7684\u6bcf\u4e00\u884c\u90fd\u53ef\u4ee5\u76f4\u63a5\u590d\u5236\u7c98\u8d34\u5230 CSV \u6587\u4ef6\u91cc\u7528 Excel \u6253\u5f00\u3002\n\n## \u4e03\u3001TCP \/ MQTT \/ \u76d1\u542c \u2014\u2014 \u5176\u4ed6 Modbus \u573a\u666f\n\n### 7.1 \u8c03 Modbus TCP\n\nLLCOM \u53ef\u4ee5\u5f53 TCP \u5ba2\u6237\u7aef\u8fde\u63a5 Modbus TCP \u8bbe\u5907\uff08\u9ed8\u8ba4\u7aef\u53e3 502\uff09\u3002\u6253\u5f00 TCP \u5ba2\u6237\u7aef\u529f\u80fd\uff0c\u586b\u5165\u8bbe\u5907 IP \u548c\u7aef\u53e3 502\uff0c\u8fde\u63a5\u6210\u529f\u540e\u50cf\u4e32\u53e3\u4e00\u6837\u6536\u53d1\u2014\u2014\n\nModbus TCP \u5e27\u548c RTU \u5e27\u7684\u533a\u522b\u53ea\u662f\u5c11\u4e86 CRC\u3001\u591a\u4e86 MBAP \u5934\uff086 \u5b57\u8282\uff09\u3002\u4f60\u53ef\u4ee5\u5728\u53d1\u9001\u5904\u7406\u811a\u672c\u91cc\u81ea\u52a8\u8865\u4e0a MBAP \u5934\uff1a\n\n```\n-- \u8f93\u5165\u6846\u5199 000000000006010300000001\n-- \u8868\u793a\uff1a\u4e8b\u52a1ID=0000\uff0c\u534f\u8baeID=0000\uff0c\u957f\u5ea6=0006\uff0c\u5355\u5143ID=01\uff0c\u529f\u80fd\u7801=03\uff0c\u8d77\u59cb\u5730\u5740=0000\uff0c\u5bc4\u5b58\u5668\u6570=0001\nreturn uartData:fromHex()\n```\n\n\u6216\u8005\u811a\u672c\u63a5\u5230\u4e32\u53e3\u6536\u5230\u7684 RTU \u5e27\uff0c\u53bb\u6389 CRC \u6362\u6210 MBAP \u5934\u540e\u901a\u8fc7 TCP \u901a\u9053\u53d1\u51fa\u53bb\u3002\u5177\u4f53\u89c1\u8f6f\u4ef6\u81ea\u5e26\u7684 `channel-demo.lua` \u4f8b\u5b50\u3002\n\n### 7.2 MQTT \u6d4b\u8bd5\n\n\u5982\u679c\u8c03\u8bd5 Modbus \u7f51\u5173\uff08DTU \u628a\u4e32\u53e3\u6570\u636e\u8f6c\u6210 MQTT \u53d1\u5e03\uff09\uff0cLLCOM \u7684 MQTT \u529f\u80fd\u53ef\u4ee5\u76f4\u63a5\u8ba2\u9605\u7f51\u5173\u53d1\u5e03\u7684\u4e3b\u9898\uff0c\u770b\u5230\u7f51\u5173\u53d1\u4e0a\u6765\u7684\u6570\u636e\u662f\u5426\u6b63\u786e\u3002\u8fd8\u80fd\u5f80\u63a7\u5236\u4e3b\u9898\u53d1\u5e03\u6d88\u606f\u6d4b\u8bd5\u4e0b\u884c\u3002\n\n### 7.3 \u4e32\u53e3\u76d1\u542c\n\n\u300c\u4e32\u53e3\u76d1\u542c\u300d\u529f\u80fd\u53ef\u4ee5\u6293\u53d6\u5176\u4ed6\u8f6f\u4ef6\u6b63\u5728\u4f7f\u7528\u7684\u4e32\u53e3\u901a\u4fe1\u2014\u2014\u6bd4\u5982\u4f60\u7684\u4e0a\u4f4d\u673a\u8f6f\u4ef6\u5728\u8ddf\u8bbe\u5907\u901a\u4fe1\uff0c\u4f60\u4e0d\u60f3\u505c\u6389\u5b83\u4f46\u60f3\u770b\u5b83\u53d1\u4e86\u4ec0\u4e48\u5e27\u3002LLCOM \u53ef\u4ee5\u9759\u9ed8\u6293\u53d6\u4e32\u53e3\u6570\u636e\u5e76\u663e\u793a\u65e5\u5fd7\uff0c\u4e0d\u5f71\u54cd\u539f\u6765\u7684\u901a\u4fe1\u3002\n\n## \u516b\u3001\u548c\u540c\u7c7b\u5de5\u5177\u7684\u5bf9\u6bd4\n\n\u5de5\u5177\u811a\u672c\u652f\u6301Modbus CRC\u5f00\u6e90\u4ef7\u683cLLCOMLua 5.3 + xlua + \u534f\u7a0b\u6846\u67b6\u9700\u8981\u81ea\u5df1\u5199\uff08\u672c\u6587\u5df2\u63d0\u4f9b\uff09\u662f\u514d\u8d39SSCOM\u65e0\u4e0d\u652f\u6301\u5426\u514d\u8d39COMTool\u6709\u9650\u4e0d\u652f\u6301\u662f\u514d\u8d39Modbus Poll\u65e0\uff08\u4e13\u4e1a Modbus \u56fa\u5b9a\u529f\u80fd\uff09\u5185\u7f6e\u5426$129ModScan\u65e0\u5185\u7f6e\u5426$69\n\nLLCOM \u4e0d\u662f\u300c\u5f00\u7bb1\u5373\u7528\u300d\u7684 Modbus \u4e13\u7528\u5de5\u5177\uff0c\u5b83\u662f\u300c\u53ef\u4ee5\u53d8\u6210 Modbus \u4e13\u7528\u5de5\u5177\u7684\u4e07\u80fd\u5de5\u5177\u300d\u3002\u5982\u679c\u4f60\u53ea\u9700\u8981\u70b9\u4e00\u4e2a\u6309\u94ae\u5c31\u53d1 Modbus \u5e27\uff0cModbus Poll \u66f4\u76f4\u63a5\u3002\u4f46\u5982\u679c\u4f60\u9700\u8981\u6279\u91cf\u81ea\u52a8\u5316\u6d4b\u8bd5\u3001\u81ea\u5b9a\u4e49\u89e3\u6790\u3001\u6570\u636e\u5bfc\u51fa\u3001\u548c MQTT\/TCP \u8054\u8c03\u2014\u2014LLCOM \u662f\u76ee\u524d\u5f00\u6e90\u5de5\u5177\u91cc\u6700\u7075\u6d3b\u7684\u3002\n\n## \u4e5d\u3001\u5e38\u89c1\u95ee\u9898\n\n**Q: \u6536\u5230\u4e71\u7801\u600e\u4e48\u529e\uff1f** \u68c0\u67e5\u6ce2\u7279\u7387\u3001\u6821\u9a8c\u4f4d\u662f\u5426\u548c\u8bbe\u5907\u4e00\u81f4\u3002\u7136\u540e\u5728 LLCOM \u7684\u63a5\u6536\u533a\u5207\u6362 Hex \u663e\u793a\u2014\u2014\u5f88\u591a\u60c5\u51b5\u4e0b\u300c\u4e71\u7801\u300d\u662f\u56e0\u4e3a\u8bbe\u5907\u8fd4\u56de\u7684\u662f\u4e8c\u8fdb\u5236\u6570\u636e\u4f46\u663e\u793a\u533a\u6309 ASCII \u89e3\u6790\u4e86\u3002\n\n**Q: CRC \u811a\u672c\u53d1\u51fa\u53bb\u7684\u5e27\u8bbe\u5907\u6ca1\u53cd\u5e94\uff1f** \u7b2c\u4e00\u6b65\uff1a\u786e\u8ba4 A\/B \u7ebf\u6ca1\u63a5\u53cd\u3002\u7b2c\u4e8c\u6b65\uff1a\u5728\u63a5\u6536\u533a\u770b\u8bbe\u5907\u6709\u6ca1\u6709\u8fd4\u56de\u4efb\u4f55\u5b57\u8282\u2014\u2014\u5982\u679c\u5b8c\u5168\u6ca1\u6709\uff0c\u53ef\u80fd\u662f\u8bbe\u5907\u5730\u5740\u4e0d\u5bf9\u3002\u7b2c\u4e09\u6b65\uff1a\u5982\u679c\u8bbe\u5907\u8fd4\u56de\u4e86\u6570\u636e\u4f46\u662f\u5f02\u5e38\u7801\uff08\u529f\u80fd\u7801 + 0x80\uff09\uff0c\u770b\u5f02\u5e38\u7801\u662f\u4ec0\u4e48\u2014\u20140x02 \u662f\u300c\u975e\u6cd5\u6570\u636e\u5730\u5740\u300d\uff0c\u5bc4\u5b58\u5668\u5730\u5740\u53ef\u80fd\u4e0d\u5b58\u5728\u3002\n\n**Q: \u5feb\u6377\u53d1\u9001\u680f\u7684\u6309\u94ae\u80fd\u81ea\u52a8\u8dd1 CRC \u811a\u672c\u5417\uff1f** \u80fd\u3002\u5feb\u6377\u53d1\u9001\u680f\u7684\u5185\u5bb9\u4e5f\u4f1a\u7ecf\u8fc7\u300c\u53d1\u9001\u5904\u7406\u811a\u672c\u300d\u2014\u2014\u6240\u4ee5\u4f60\u5199\u597d\u4e86 CRC \u81ea\u52a8\u8ba1\u7b97\u811a\u672c\u540e\uff0c\u5feb\u6377\u53d1\u9001\u680f\u53ea\u9700\u8981\u586b\u88f8\u5e27\uff08\u5982 `010300000001`\uff09\uff0c\u70b9\u6309\u94ae\u81ea\u52a8\u52a0 CRC\u3002\n\n**Q: Lua \u811a\u672c\u8fd0\u884c\u65f6\u62a5\u9519\u4e86\uff1f** \u5728\u811a\u672c\u533a\u57df\u6700\u5f00\u5934\u52a0\u4e00\u884c `log.info(\"boot\", \"script started\")` \u770b\u811a\u672c\u662f\u5426\u6210\u529f\u542f\u52a8\u3002\u5927\u90e8\u5206\u9519\u8bef\u662f\u56e0\u4e3a\u53d8\u91cf\u540d\u62fc\u9519\u6216\u8c03\u7528\u4e86\u4e0d\u5b58\u5728\u7684 API\u2014\u2014\u65e5\u5fd7\u533a\u4f1a\u663e\u793a Lua \u9519\u8bef\u4fe1\u606f\u3002\n\nLLCOM \u7684\u6838\u5fc3\u4ef7\u503c\u4e0d\u662f\u591a\u4e86\u591a\u5c11\u529f\u80fd\uff0c\u800c\u662f\u628a\u300c\u8c03\u8bd5\u300d\u4ece\u91cd\u590d\u4f53\u529b\u52b3\u52a8\u53d8\u6210\u4e86\u53ef\u7f16\u7a0b\u7684\u4efb\u52a1\u3002\u4f60\u5728\u73b0\u573a\u82b1\u534a\u5c0f\u65f6\u5199\u4e86\u4e09\u4e2a\u811a\u672c\uff0c\u63a5\u4e0b\u6765\u4e09\u5929\u70b9\u51e0\u4e0b\u9f20\u6807\u6570\u636e\u81ea\u5df1\u5c31\u6765\u4e86\u3002\u8fd9\u5c31\u662f\u300c\u7ed9\u4e32\u53e3\u88c5\u4e0a\u5927\u8111\u300d\u7684\u771f\u6b63\u542b\u4e49\u2014\u2014\u4e0d\u662f LLCOM \u6709 AI\uff0c\u662f LLCOM \u8ba9\u4f60\u80fd\u628a\u81ea\u5df1\u7684\u8c03\u8bd5\u903b\u8f91\u5199\u6210\u4ee3\u7801\u3001\u7136\u540e\u8ba9\u8f6f\u4ef6\u66ff\u4f60\u8dd1\u3002\n\n\u6709\u95ee\u9898\u518d\u804a\u3002\n\n---\n*modbus.cn*\n"