├── Modbus.ipynb ├── README.md ├── image ├── e27.jpg ├── fatek.jpg ├── fatek_modbus_addr.png ├── mb_exception.jpg └── 磁簧開關.jpg ├── mb_demo0.py ├── mb_demo1.py ├── mb_demo1_mq.py ├── mb_demo2.py ├── mb_demo3.py └── mb_demo3_mq.py /Modbus.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Modbus 的相關說明\n", 8 | "\n", 9 | "- 首先可以來看看維基怎麼說: https://zh.wikipedia.org/wiki/Modbus\n", 10 | "- 英文的wiki寫的更詳細: https://en.wikipedia.org/wiki/Modbus\n", 11 | "- 一般的分類:\n", 12 | " - Modbus/RTU: 在RS-485, RS-232這類的串列通訊上使用\n", 13 | " - Modbus/TCP: 基於TCP的Modbus協定,因此只要是走網路的大致上都可以適用\n", 14 | " - 另外還有modbus ascii,這類似於Modbus/RTU,但通訊資料量較大\n", 15 | "- 一個Master vs 多個slave\n", 16 | "- 一問一答的通訊方式\n", 17 | "- 若對通訊格式有興趣可以參考:\n", 18 | " - https://www.rtaautomation.com/technologies/modbus-tcpip/\n" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## Demo\n", 26 | "- mb_demo0: (初學可跳過),在不知道Modbus設備的通訊參數下可以使用此demo來掃出何種參數才是正確的\n", 27 | "- mb_demo1: 配合FATEK PLC進行通訊控制,在此demo中將對PLC的DO進行控制:OFF-->ON-->OFF-->ON-->OFF\n", 28 | "- mb_demo2: 配合FATEK PLC進行通訊控制,在此demo中將會讀回PLC的DI,當接上的磁簧開關是ON時會讀回1;磁簧開關斷開時會讀回0\n", 29 | "- mb_demo3: 配合電表,讀回輸入的交流電電壓" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "### 開始前需要先安裝的套件\n", 37 | "- pip install serial\n", 38 | "- pip install modbus_tk\n", 39 | " - modbus_tk 支援 Modbus/TCP, Modbus/RTU\n", 40 | " - 其中Modbus/RTU需要serial的套件,因此要自行另外安裝" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "### Demo1 : PLC DO控制" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 2, 53 | "metadata": {}, 54 | "outputs": [], 55 | "source": [ 56 | "import serial\n", 57 | "import modbus_tk\n", 58 | "import modbus_tk.defines as cst\n", 59 | "import modbus_tk.modbus_rtu as modbus_rtu\n", 60 | "import time\n" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 3, 66 | "metadata": { 67 | "collapsed": true 68 | }, 69 | "outputs": [], 70 | "source": [ 71 | "mbComPort = 'COM7' # your RS-485 port. for linux --> \"/dev/ttyUSB2\"\n", 72 | "baudrate = 9600\n", 73 | "databit = 8 #7, 8\n", 74 | "parity = 'N' #N, O, E\n", 75 | "stopbit = 1 #1, 2\n", 76 | "mbTimeout = 100 # ms\n" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 5, 82 | "metadata": {}, 83 | "outputs": [ 84 | { 85 | "name": "stdout", 86 | "output_type": "stream", 87 | "text": [ 88 | "Write(addr, value)= (2, 0)\n", 89 | "Write(addr, value)= (2, 65280)\n", 90 | "Write(addr, value)= (2, 0)\n", 91 | "Write(addr, value)= (2, 65280)\n", 92 | "Write(addr, value)= (2, 0)\n" 93 | ] 94 | }, 95 | { 96 | "data": { 97 | "text/plain": [ 98 | "True" 99 | ] 100 | }, 101 | "execution_count": 5, 102 | "metadata": {}, 103 | "output_type": "execute_result" 104 | } 105 | ], 106 | "source": [ 107 | "\n", 108 | "mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit)\n", 109 | "master = modbus_rtu.RtuMaster(mb_port)\n", 110 | "master.set_timeout(mbTimeout/1000.0)\n", 111 | "\n", 112 | "mbId = 1\n", 113 | "addr = 2 #base0 --> my 110V Led燈泡\n", 114 | "\n", 115 | "for i in range(5):\n", 116 | " try:\n", 117 | " value = i%2\n", 118 | " #-- FC5: write multi-coils\n", 119 | " rr = master.execute(mbId, cst.WRITE_SINGLE_COIL, addr, output_value=value)\n", 120 | " print(\"Write(addr, value)=\", rr)\n", 121 | "\n", 122 | " except Exception as e:\n", 123 | " print(\"modbus test Error: \" + str(e))\n", 124 | "\n", 125 | " time.sleep(2)\n", 126 | "\n", 127 | "master._do_close()\n" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "### other example for reading DI, DO, AI, AO (補充教材)" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": null, 140 | "metadata": { 141 | "collapsed": true 142 | }, 143 | "outputs": [], 144 | "source": [ 145 | "## other example for reading DI, DO, AI, AO\n", 146 | "\n", 147 | "addr = 1\n", 148 | "n = 4\n", 149 | "\n", 150 | "#-- DI read: FC2 Read multi-input discrete ( 1xxxx )\n", 151 | "rr = master.execute(mbId, cst.READ_DISCRETE_INPUTS, addr, n)\n", 152 | "print(\"DI value= \", rr)\n", 153 | "\n", 154 | "#-- FC01: Read multi-coils status (0xxxx) for DO\n", 155 | "rr = master.execute(mbId, cst.READ_COILS, addr, n)\n", 156 | "print(\"DO value= \", rr)\n", 157 | "\n", 158 | "#-- FC04: read multi-input registers (3xxxx), for AI\n", 159 | "rr = master.execute(mbId, cst.READ_INPUT_REGISTERS, addr, n)\n", 160 | "print(\"AI value= \", rr)\n", 161 | "\n", 162 | "#-- FC03: read multi-registers (4xxxx) for AO\n", 163 | "rr = master.execute(mbId, cst.READ_HOLDING_REGISTERS, addr, n)\n", 164 | "print(\"AO value= \", rr)\n", 165 | "\n" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "### Demo2: PLC DI點讀取\n", 173 | "- 範例中,第二個DI是ON,因此讀回來為1" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 6, 179 | "metadata": { 180 | "collapsed": true 181 | }, 182 | "outputs": [], 183 | "source": [ 184 | "import serial\n", 185 | "import modbus_tk\n", 186 | "import modbus_tk.defines as cst\n", 187 | "import modbus_tk.modbus_rtu as modbus_rtu\n", 188 | "import time" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 10, 194 | "metadata": { 195 | "collapsed": true 196 | }, 197 | "outputs": [], 198 | "source": [ 199 | "mbComPort = 'COM7'\n", 200 | "baudrate = 9600\n", 201 | "databit = 8\n", 202 | "parity = 'N'\n", 203 | "stopbit = 1\n", 204 | "mbTimeout = 100 # ms" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 12, 210 | "metadata": {}, 211 | "outputs": [ 212 | { 213 | "name": "stdout", 214 | "output_type": "stream", 215 | "text": [ 216 | "value= (0, 1, 0, 0)\n", 217 | "value= (0, 1, 0, 0)\n", 218 | "value= (0, 1, 0, 0)\n", 219 | "value= (0, 1, 0, 0)\n", 220 | "value= (0, 1, 0, 0)\n" 221 | ] 222 | }, 223 | { 224 | "data": { 225 | "text/plain": [ 226 | "True" 227 | ] 228 | }, 229 | "execution_count": 12, 230 | "metadata": {}, 231 | "output_type": "execute_result" 232 | } 233 | ], 234 | "source": [ 235 | "mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit)\n", 236 | "master = modbus_rtu.RtuMaster(mb_port)\n", 237 | "master.set_timeout(mbTimeout/1000.0)\n", 238 | "\n", 239 | "mbId = 1\n", 240 | "addr = 1000 #base0\n", 241 | "\n", 242 | "for i in range(5):\n", 243 | " try:\n", 244 | " # FATEK的PLC把DI點放在DO的address中\n", 245 | " #-- FC01: Read multi-coils status (0xxxx) for DO\n", 246 | " rr = master.execute(mbId, cst.READ_COILS, addr, 4)\n", 247 | " print(\"value= \", rr)\n", 248 | "\n", 249 | " except Exception as e:\n", 250 | " print(\"modbus test Error: \" + str(e))\n", 251 | "\n", 252 | " time.sleep(1)\n", 253 | "\n", 254 | "master._do_close()\n" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "### Demo3: 電表電壓資訊讀取" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 13, 267 | "metadata": { 268 | "collapsed": true 269 | }, 270 | "outputs": [], 271 | "source": [ 272 | "import serial\n", 273 | "import modbus_tk\n", 274 | "import modbus_tk.defines as cst\n", 275 | "import modbus_tk.modbus_rtu as modbus_rtu\n", 276 | "import time\n", 277 | "from struct import *" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": 19, 283 | "metadata": { 284 | "collapsed": true 285 | }, 286 | "outputs": [], 287 | "source": [ 288 | "mbComPort = 'COM7' #your RS-485 port\n", 289 | "baudrate = 19200\n", 290 | "databit = 8\n", 291 | "parity = 'N'\n", 292 | "stopbit = 1\n", 293 | "mbTimeout = 100 # ms" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": 39, 299 | "metadata": {}, 300 | "outputs": [ 301 | { 302 | "name": "stdout", 303 | "output_type": "stream", 304 | "text": [ 305 | "read value: (27533, 17112, 0, 0)\n", 306 | "v_a= (108.2100601196289,)\n" 307 | ] 308 | }, 309 | { 310 | "data": { 311 | "text/plain": [ 312 | "True" 313 | ] 314 | }, 315 | "execution_count": 39, 316 | "metadata": {}, 317 | "output_type": "execute_result" 318 | } 319 | ], 320 | "source": [ 321 | "mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit)\n", 322 | "master = modbus_rtu.RtuMaster(mb_port)\n", 323 | "master.set_timeout(mbTimeout/1000.0)\n", 324 | "\n", 325 | "mbId = 4\n", 326 | "#[0x1000-0x1001]=VIn_a\n", 327 | "addr = 0x1000#4096\n", 328 | "\n", 329 | "try:\n", 330 | " # FC3\n", 331 | " rr = master.execute(mbId, cst.READ_INPUT_REGISTERS, addr, 4)\n", 332 | " print('read value:', rr)\n", 333 | "\n", 334 | " # convert to float:\n", 335 | " # IEEE754 ==> (Hi word Hi Bite, Hi word Lo Byte, Lo word Hi Byte, Lo word Lo Byte)\n", 336 | " try:\n", 337 | " v_a_hi = rr[1]\n", 338 | " v_a_lo = rr[0]\n", 339 | " v_a = unpack('>f', pack('>HH', v_a_hi, v_a_lo))\n", 340 | " print('v_a=', v_a)\n", 341 | " #struct.unpack(\">f\",'\\x42\\xd8\\x6b\\x8d')\n", 342 | " except Exception as e:\n", 343 | " print(e)\n", 344 | "\n", 345 | "except Exception as e:\n", 346 | " print(\"modbus test Error: \" + str(e))\n", 347 | "\n", 348 | "\n", 349 | "master._do_close()\n" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "metadata": {}, 355 | "source": [ 356 | "### 補充資料\n", 357 | "### 掃Modbus設備 --> 用於不知道設備的baudrate等參數時\n", 358 | "\n", 359 | "- 以下例的結果來說,代表這19200 8N1這個參數下是有回應的,代表可以如此通訊:\n", 360 | "\n", 361 | "scan @ 19200 N 1\n", 362 | "rr: (2,)" 363 | ] 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": 41, 368 | "metadata": {}, 369 | "outputs": [ 370 | { 371 | "name": "stdout", 372 | "output_type": "stream", 373 | "text": [ 374 | "scan @ 9600 N 1\n", 375 | "modbus test Error: Response length is invalid 0\n", 376 | "scan @ 9600 N 2\n", 377 | "modbus test Error: Response length is invalid 0\n", 378 | "scan @ 9600 O 1\n", 379 | "modbus test Error: Response length is invalid 0\n", 380 | "scan @ 9600 O 2\n", 381 | "modbus test Error: Response length is invalid 0\n", 382 | "scan @ 9600 E 1\n", 383 | "modbus test Error: Response length is invalid 0\n", 384 | "scan @ 9600 E 2\n", 385 | "modbus test Error: Response length is invalid 0\n", 386 | "scan @ 19200 N 1\n", 387 | "rr: (2,)\n", 388 | "scan @ 19200 N 2\n", 389 | "rr: (2,)\n", 390 | "scan @ 19200 O 1\n", 391 | "modbus test Error: Invalid CRC in response\n", 392 | "scan @ 19200 O 2\n", 393 | "modbus test Error: Invalid CRC in response\n", 394 | "scan @ 19200 E 1\n", 395 | "modbus test Error: Invalid CRC in response\n", 396 | "scan @ 19200 E 2\n", 397 | "modbus test Error: Invalid CRC in response\n", 398 | "scan @ 38400 N 1\n", 399 | "modbus test Error: Response length is invalid 0\n", 400 | "scan @ 38400 N 2\n", 401 | "modbus test Error: Response length is invalid 0\n", 402 | "scan @ 38400 O 1\n", 403 | "modbus test Error: Response length is invalid 0\n", 404 | "scan @ 38400 O 2\n", 405 | "modbus test Error: Response length is invalid 0\n", 406 | "scan @ 38400 E 1\n", 407 | "modbus test Error: Response length is invalid 0\n", 408 | "scan @ 38400 E 2\n", 409 | "modbus test Error: Response length is invalid 0\n", 410 | "scan @ 115200 N 1\n", 411 | "modbus test Error: Response length is invalid 0\n", 412 | "scan @ 115200 N 2\n", 413 | "modbus test Error: Response length is invalid 0\n", 414 | "scan @ 115200 O 1\n", 415 | "modbus test Error: Response length is invalid 0\n", 416 | "scan @ 115200 O 2\n", 417 | "modbus test Error: Response length is invalid 0\n", 418 | "scan @ 115200 E 1\n", 419 | "modbus test Error: Response length is invalid 0\n", 420 | "scan @ 115200 E 2\n", 421 | "modbus test Error: Response length is invalid 0\n" 422 | ] 423 | } 424 | ], 425 | "source": [ 426 | "import serial\n", 427 | "import modbus_tk\n", 428 | "import modbus_tk.defines as cst\n", 429 | "import modbus_tk.modbus_rtu as modbus_rtu\n", 430 | "\n", 431 | "\n", 432 | "mbComPort = 'COM7'\n", 433 | "baudrate = 38400\n", 434 | "databit = 8\n", 435 | "parity = 'N'\n", 436 | "stopbit = 1\n", 437 | "mbTimeout = 100 # ms\n", 438 | "\n", 439 | "# scan_test:\n", 440 | "for baudrate in [9600, 19200, 38400, 115200]:\n", 441 | " for parity in ['N', 'O', 'E']:\n", 442 | " for stopbit in[1, 2]:\n", 443 | " print('scan @', baudrate, parity, stopbit)\n", 444 | " mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit)\n", 445 | " master = modbus_rtu.RtuMaster(mb_port)\n", 446 | " master.set_timeout(mbTimeout/1000.0)\n", 447 | "\n", 448 | " mbId = 1\n", 449 | " addr = 1\n", 450 | "\n", 451 | " try:\n", 452 | " # FC3\n", 453 | " rr = master.execute(mbId, cst.READ_HOLDING_REGISTERS, addr, 1)\n", 454 | " print('rr:', rr)\n", 455 | "\n", 456 | " except Exception as e:\n", 457 | " print(\"modbus test Error: \" + str(e))\n", 458 | "\n", 459 | " master._do_close()\n" 460 | ] 461 | } 462 | ], 463 | "metadata": { 464 | "kernelspec": { 465 | "display_name": "Python 3", 466 | "language": "python", 467 | "name": "python3" 468 | }, 469 | "language_info": { 470 | "codemirror_mode": { 471 | "name": "ipython", 472 | "version": 3 473 | }, 474 | "file_extension": ".py", 475 | "mimetype": "text/x-python", 476 | "name": "python", 477 | "nbconvert_exporter": "python", 478 | "pygments_lexer": "ipython3", 479 | "version": "3.6.1" 480 | } 481 | }, 482 | "nbformat": 4, 483 | "nbformat_minor": 2 484 | } 485 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PLC & Python 2 | 3 | ## PLC 4 | 5 | ![PLC](image/fatek.jpg) 6 | - Programmable Logic Controller,簡稱PLC 7 | - 先來看wiki的 [說明](https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%BC%96%E7%A8%8B%E9%80%BB%E8%BE%91%E6%8E%A7%E5%88%B6%E5%99%A8) 8 | - 今天 demo要用的PLC為[FATEK PLC](http://www.fatek.com/tw/prod.php?act=view&no=1) 9 | 10 | 11 | ## PLC 如何通訊? 12 | - 各家自有協定 13 | - 各種工業用常用protocol: modbus, canbus, ethercat... 14 | - 今天要說明的是最常見的Modbus 15 | 16 | ## Modbus in Python 17 | - 看一下[wiki說明](https://zh.wikipedia.org/wiki/Modbus) 18 | - package: https://pypi.python.org/pypi/modbus_tk 19 | - https://github.com/ljean/modbus-tk/ 20 | - 使前先安裝一下套件 21 | - pip install serial 22 | - pip install modbus_tk 23 | - example: 24 |  - DI/DO/AI/AO的存取 25 | 26 | ## Python與PLC共舞 27 | - demo1: 使用modbus控制PLC的relay輸出,點亮家中的照明用電燈 28 |  - 家用110V E27 LED燈泡: 29 | ![家用110V E27 LED燈泡](image/e27.jpg) 30 | 31 | - demo2: 使用modbus取得開關狀態-->常見的有保全使用的磁簧開關 32 | ![磁簧開關](image/磁簧開關.jpg) 33 | - DI和DO混放,所以要查表才能了解 34 | - FATEK說明書的Modbus Table節錄 35 | ![PLC的Modbus Table](image/fatek_modbus_addr.png) 36 | 37 | - demo3: 讀電錶的資訊 38 | - 這邊的Demo範例是使用[士林電機電表](http://www.seec.com.tw/Content/Goods/GCont.aspx?SiteID=10&MmmID=655575436061073254&CatId=2015120316233269372&MSID=655575454164207353#ad-image-0) 39 |  ![電表](http://www.seec.com.tw/UpFiles/10/Goods_NPics655575436061073254/EG-%E9%9B%BB%E8%A1%A8SPM-8.jpg) 40 | 41 | ### 會後補充 42 | * 程式的說明有再增加一個jupyter notebook的說明範例,可以比較清楚的看到執行的結果,有興趣可以參考[這篇](Modbus.ipynb) 43 | 44 | ### 實作補充 45 | - 在實際案場你在使用modbus/tcp時,也許會遇到設備回回來的header長度欄位是錯的,但後面帶的pdu資料卻是正確的,這時modbus_tk就沒辨法正常運作,可以參考以下的解法: 46 | 47 | - 修改 python資料夾下 `Lib\site-packages\modbus_tk` 中的 `modbus_tcp.py` (我使用的版本是 '0.5.10'),把 `_recv` 函式改成如下 48 | ``` 49 | def _recv(self, expected_length=-1): 50 | """ 51 | Receive the response from the slave 52 | Do not take expected_length into account because the length of the response is 53 | written in the mbap. Used for RTU only 54 | """ 55 | to_be_recv_length = expected_length-2+6 #MB/TCP data length must add 6 byte header, sub 2 byte crc 56 | expected_pdu_length = expected_length-2 57 | response = to_data('') 58 | length = 255 59 | while len(response) < to_be_recv_length: #for H5A: length error issue 60 | rcv_byte = self._sock.recv(1) 61 | if rcv_byte: 62 | response += rcv_byte 63 | if len(response) == 6: 64 | #to_be_recv_length = struct.unpack(">HHH", response)[2] 65 | #length = to_be_recv_length + 6 66 | if response[5] != expected_pdu_length: 67 | print('length field of header error, fix: %s-->%s' %(response[5], expected_pdu_length)) 68 | response = response[:5] + struct.pack("B", expected_pdu_length) 69 | 70 | else: 71 | break 72 | retval = call_hooks("modbus_tcp.TcpMaster.after_recv", (self, response)) 73 | if retval is not None: 74 | return retval 75 | return response 76 | ``` 77 | 78 | ---- 79 | ## Exception code 補充 80 | 81 | - [link](https://product-help.schneider-electric.com/ED/ES_Power/PP-HJL_Modbus_Guide/EDMS/0611IB1302/0611IB13xx/NSX_MB_Modbus_Protocol/NSX_MB_Modbus_Protocol-5.htm) 82 | 83 | ![pic](image/mb_exception.jpg) 84 | -------------------------------------------------------------------------------- /image/e27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maloyang/PLC-Python/51b744dca160ce9f75d819094486ac5fda17fc09/image/e27.jpg -------------------------------------------------------------------------------- /image/fatek.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maloyang/PLC-Python/51b744dca160ce9f75d819094486ac5fda17fc09/image/fatek.jpg -------------------------------------------------------------------------------- /image/fatek_modbus_addr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maloyang/PLC-Python/51b744dca160ce9f75d819094486ac5fda17fc09/image/fatek_modbus_addr.png -------------------------------------------------------------------------------- /image/mb_exception.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maloyang/PLC-Python/51b744dca160ce9f75d819094486ac5fda17fc09/image/mb_exception.jpg -------------------------------------------------------------------------------- /image/磁簧開關.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maloyang/PLC-Python/51b744dca160ce9f75d819094486ac5fda17fc09/image/磁簧開關.jpg -------------------------------------------------------------------------------- /mb_demo0.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import serial 5 | import modbus_tk 6 | import modbus_tk.defines as cst 7 | import modbus_tk.modbus_rtu as modbus_rtu 8 | 9 | 10 | mbComPort = 'COM7' 11 | baudrate = 38400 12 | databit = 8 13 | parity = 'N' 14 | stopbit = 1 15 | mbTimeout = 100 # ms 16 | 17 | def main(): 18 | 19 | # scan_test: 20 | for baudrate in [9600, 19200, 38400, 115200]: 21 | for parity in ['N', 'O', 'E']: 22 | for stopbit in[1, 2]: 23 | print('scan @', baudrate, parity, stopbit) 24 | mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit) 25 | master = modbus_rtu.RtuMaster(mb_port) 26 | master.set_timeout(mbTimeout/1000.0) 27 | 28 | mbId = 1 29 | addr = 0xFA 30 | ao_n = 1 31 | 32 | try: 33 | # FC3 34 | rr = master.execute(mbId, cst.READ_HOLDING_REGISTERS, addr, 1) 35 | print('rr:', rr) 36 | 37 | except Exception, e: 38 | print("modbus test Error: " + str(e)) 39 | 40 | master._do_close() 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /mb_demo1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import serial 5 | import modbus_tk 6 | import modbus_tk.defines as cst 7 | import modbus_tk.modbus_rtu as modbus_rtu 8 | import time 9 | 10 | mbComPort = 'COM7' # your RS-485 port. for linux --> "/dev/ttyUSB2" 11 | baudrate = 9600 12 | databit = 8 #7, 8 13 | parity = 'N' #N, O, E 14 | stopbit = 1 #1, 2 15 | mbTimeout = 100 # ms 16 | ser = None 17 | sendBuf = None 18 | 19 | 20 | def main(): 21 | 22 | mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit) 23 | master = modbus_rtu.RtuMaster(mb_port) 24 | master.set_timeout(mbTimeout/1000.0) 25 | 26 | mbId = 1 27 | #addr = 0 #base0 28 | addr = 2 #base0 --> my 110V Led燈泡 29 | 30 | for i in range(5): 31 | try: 32 | value = i%2 33 | #-- FC5: write multi-coils 34 | rr = master.execute(mbId, cst.WRITE_SINGLE_COIL, addr, output_value=value) 35 | print("Write(addr, value)=%s" %(str(rr))) 36 | 37 | except Exception, e: 38 | print("modbus test Error: " + str(e)) 39 | 40 | time.sleep(2) 41 | 42 | master._do_close() 43 | 44 | 45 | if __name__ == '__main__': 46 | main() 47 | 48 | -------------------------------------------------------------------------------- /mb_demo1_mq.py: -------------------------------------------------------------------------------- 1 | import paho.mqtt.client as mqtt #import the client1 2 | import time 3 | 4 | import serial 5 | import modbus_tk 6 | import modbus_tk.defines as cst 7 | import modbus_tk.modbus_rtu as modbus_rtu 8 | import time 9 | 10 | mbComPort = 'COM7' # COM3->RS-485 11 | baudrate = 9600 12 | databit = 8 13 | parity = 'N' 14 | stopbit = 1 15 | mbTimeout = 100 # ms 16 | 17 | def on_connect(client, userdata, flags, rc): 18 | m="Connected flags"+str(flags)+", result code "+str(rc)+", client_id "+str(client) 19 | print(m) 20 | 21 | # first value OFF 22 | print('set light off!') 23 | control_light(0) 24 | client1.publish(topic,0) 25 | 26 | def on_message(client1, userdata, message): 27 | print("message received " ,str(message.payload.decode("utf-8")), message.topic, message.qos, message.retain) 28 | if message.topic == topic: 29 | print("set light: ", message.payload) 30 | if message.payload=='1' or message.payload==1: 31 | control_light(1) 32 | else: 33 | control_light(0) 34 | 35 | def on_log(client, userdata, level, buf): 36 | print("log: ",buf) 37 | 38 | def control_light(value): 39 | mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit) 40 | master = modbus_rtu.RtuMaster(mb_port) 41 | master.set_timeout(mbTimeout/1000.0) 42 | 43 | mbId = 1 44 | addr = 2 #base0 45 | 46 | try: 47 | #-- FC5: write multi-coils 48 | rr = master.execute(mbId, cst.WRITE_SINGLE_COIL, addr, output_value=value) 49 | print("Write(addr, value)=%s" %(str(rr))) 50 | 51 | except Exception, e: 52 | print("modbus test Error: " + str(e)) 53 | 54 | master._do_close() 55 | 56 | 57 | # some online free broker: 58 | # iot.eclipse.org 59 | # test.mosquitto.org 60 | # broker.hivemq.com 61 | broker_address="iot.eclipse.org" 62 | topic = "malo-iot/light" 63 | client1 = mqtt.Client() #create new instance 64 | client1.on_connect = on_connect #attach function to callback 65 | client1.on_message = on_message #attach function to callback 66 | #client1.on_log=on_log 67 | 68 | time.sleep(1) 69 | client1.connect("iot.eclipse.org", 1883, 60) #connect to broker 70 | #client1.loop_start() #start the loop 71 | client1.subscribe(topic) 72 | 73 | client1.loop_forever() 74 | -------------------------------------------------------------------------------- /mb_demo2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import serial 5 | import modbus_tk 6 | import modbus_tk.defines as cst 7 | import modbus_tk.modbus_rtu as modbus_rtu 8 | import time 9 | 10 | mbComPort = 'COM7' 11 | baudrate = 9600 12 | databit = 8 13 | parity = 'N' 14 | stopbit = 1 15 | mbTimeout = 100 # ms 16 | 17 | def main(): 18 | 19 | mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit) 20 | master = modbus_rtu.RtuMaster(mb_port) 21 | master.set_timeout(mbTimeout/1000.0) 22 | 23 | mbId = 1 24 | addr = 1000 #base0 25 | 26 | for i in range(5): 27 | try: 28 | # FATEK的PLC把DI點放在DO的address中 29 | #-- FC01: Read multi-coils status (0xxxx) for DO 30 | rr = master.execute(mbId, cst.READ_COILS, addr, 4) 31 | print("value= ", rr) 32 | 33 | except Exception, e: 34 | print("modbus test Error: " + str(e)) 35 | 36 | time.sleep(1) 37 | 38 | master._do_close() 39 | 40 | 41 | if __name__ == '__main__': 42 | main() 43 | 44 | -------------------------------------------------------------------------------- /mb_demo3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import serial 5 | import modbus_tk 6 | import modbus_tk.defines as cst 7 | import modbus_tk.modbus_rtu as modbus_rtu 8 | import time 9 | from struct import * 10 | 11 | mbComPort = 'COM7' 12 | baudrate = 19200 13 | databit = 8 14 | parity = 'N' 15 | stopbit = 1 16 | mbTimeout = 100 # ms 17 | 18 | def main(): 19 | 20 | mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit) 21 | master = modbus_rtu.RtuMaster(mb_port) 22 | master.set_timeout(mbTimeout/1000.0) 23 | 24 | mbId = 4 25 | #[0x1000-0x1001]=VIn_a 26 | addr = 0x1000#4096 27 | 28 | try: 29 | # FC3 30 | rr = master.execute(mbId, cst.READ_INPUT_REGISTERS, addr, 4) 31 | print('read value:', rr) 32 | 33 | # convert to float: 34 | # IEEE754 ==> (Hi word Hi Bite, Hi word Lo Byte, Lo word Hi Byte, Lo word Lo Byte) 35 | try: 36 | v_a_hi = rr[1] 37 | v_a_lo = rr[0] 38 | v_a = unpack('>f', pack('>HH', v_a_hi, v_a_lo)) 39 | print('v_a=', v_a) 40 | except Exception, e: 41 | print(e) 42 | 43 | except Exception, e: 44 | print("modbus test Error: " + str(e)) 45 | return 46 | 47 | 48 | master._do_close() 49 | 50 | 51 | if __name__ == '__main__': 52 | main() 53 | 54 | -------------------------------------------------------------------------------- /mb_demo3_mq.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | import paho.mqtt.client as mqtt #import the client1 4 | import time 5 | 6 | import serial 7 | import modbus_tk 8 | import modbus_tk.defines as cst 9 | import modbus_tk.modbus_rtu as modbus_rtu 10 | import time 11 | from struct import * 12 | import random 13 | 14 | mbComPort = 'COM7' # COM3->RS-485 15 | baudrate = 9600 16 | databit = 8 17 | parity = 'N' 18 | stopbit = 1 19 | mbTimeout = 100 # ms 20 | 21 | def on_connect(client, userdata, flags, rc): 22 | m="Connected flags"+str(flags)+", result code "+str(rc)+", client_id "+str(client) 23 | print(m) 24 | 25 | # first value OFF 26 | print('set light off!') 27 | control_light(0) 28 | client1.publish(topic,0) 29 | 30 | def on_message(client1, userdata, message): 31 | print("message received " ,str(message.payload.decode("utf-8")), message.topic, message.qos, message.retain) 32 | if message.topic == topic: 33 | print("set light: ", message.payload) 34 | if message.payload=='1' or message.payload==1: 35 | control_light(1) 36 | else: 37 | control_light(0) 38 | 39 | def on_log(client, userdata, level, buf): 40 | print("log: ",buf) 41 | 42 | def control_light(value): 43 | mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit) 44 | master = modbus_rtu.RtuMaster(mb_port) 45 | master.set_timeout(mbTimeout/1000.0) 46 | 47 | mbId = 1 48 | addr = 2 #base0 49 | 50 | try: 51 | #-- FC5: write multi-coils 52 | rr = master.execute(mbId, cst.WRITE_SINGLE_COIL, addr, output_value=value) 53 | print("Write(addr, value)=%s" %(str(rr))) 54 | 55 | except Exception, e: 56 | print("modbus test Error: " + str(e)) 57 | 58 | master._do_close() 59 | 60 | 61 | def read_power_meter(): 62 | mb_port = serial.Serial(port=mbComPort, baudrate=baudrate, bytesize=databit, parity=parity, stopbits=stopbit) 63 | master = modbus_rtu.RtuMaster(mb_port) 64 | master.set_timeout(mbTimeout/1000.0) 65 | 66 | mbId = 4 67 | #[0x1000-0x1001]=VIn_a 68 | addr = 0x1000#4096 69 | 70 | v_a = None 71 | try: 72 | # FC3 73 | rr = master.execute(mbId, cst.READ_INPUT_REGISTERS, addr, 4) 74 | print('read value:', rr) 75 | 76 | # convert to float: 77 | # IEEE754 ==> (Hi word Hi Bite, Hi word Lo Byte, Lo word Hi Byte, Lo word Lo Byte) 78 | try: 79 | v_a_hi = rr[1] 80 | v_a_lo = rr[0] 81 | v_a = unpack('>f', pack('>HH', v_a_hi, v_a_lo)) 82 | print('v_a=', v_a) 83 | except Exception, e: 84 | print(e) 85 | except Exception, e: 86 | print("modbus test Error: " + str(e)) 87 | 88 | master._do_close() 89 | return v_a 90 | 91 | # some online free broker: 92 | # iot.eclipse.org 93 | # test.mosquitto.org 94 | # broker.hivemq.com 95 | broker_address="iot.eclipse.org" 96 | topic = "malo-iot/light" 97 | client1 = mqtt.Client() #create new instance 98 | client1.on_connect = on_connect #attach function to callback 99 | client1.on_message = on_message #attach function to callback 100 | #client1.on_log=on_log 101 | 102 | time.sleep(1) 103 | client1.connect("iot.eclipse.org", 1883, 60) #connect to broker 104 | client1.subscribe(topic) 105 | 106 | #client1.loop_forever() 107 | # 有自己的while loop,所以call loop_start(),不用loop_forever 108 | client1.loop_start() #start the loop 109 | 110 | while True: 111 | #v = read_power_meter() 112 | v = random.randint(100, 120) 113 | client1.publish("malo-iot/voltage", v) 114 | time.sleep(2) 115 | --------------------------------------------------------------------------------