极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 58083|回复: 19

Arduino rs485 Modbus 求助!!

[复制链接]
发表于 2015-3-16 22:38:31 | 显示全部楼层 |阅读模式
各位好,我是Arduino 的新手,
最近遇到一個難題,我想用Arduino 去取得 數位電表中的電流值,
卻碰到 Modbus 這項讓我覺得苦惱的通訊協議,

我是使用 TTL 轉 RS485 (MAX485)模塊來達成與數位電表的RS485通訊,
搞不懂的地方是有

問題1 :
如果是 Arduino 去向 數位電表 讀取資料,
那Arduino 應該要設定成 Modbus Master 對吧?!

問題2 :
數位電表的說明文件是這樣寫的..

假設今天我電表是 slave 編號1
我是不是傳送 01 03 00 3A 00 02 +crc
就能夠讀取到 KW1 的數值呢?!

這段要求命令有沒有大大能講解一下,特別是 HI LO 到底是什麼?!
位址後面的括弧 又是什麼呢?!


奢求大大有沒有範例提點一下阿!!!~ 先謝了!!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2015-3-17 07:52:13 | 显示全部楼层
hi lo是高位和地位的意思,Modbus是问答式的,我觉得你的命令应该是正确,括号里面有可能是4000开始的地址吧,不行多试试。
回复 支持 反对

使用道具 举报

发表于 2015-3-17 09:33:02 | 显示全部楼层
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-17 12:41:22 | 显示全部楼层
本帖最后由 45so 于 2015-3-17 12:42 编辑

先感謝二位回覆
剛剛才注意到我文章發錯區了
應該發在求助區才對~真是抱歉

指令的部分我想也是沒問題的
用PC模擬Master 是能夠正確讀取到數值
但Arduino 上怎麼搞都是回傳連線錯誤

另外官方的文件小弟也有看過了,
但那個庫不知道是太久沒更新還是?
編譯一直過不了

網路上其他的庫也有試過,但不是無法連線不然就是回傳亂碼
亂碼的部分我也有確認過baud rate 是沒設定錯誤的!~

苦惱好幾天了啊
回复 支持 反对

使用道具 举报

发表于 2015-3-17 12:58:00 | 显示全部楼层
用PC和Arduino连上试试,Arduino的外围接口用连仪表的那部分,看能不能收到PC的 01 03 00 3A 00 02 +crc指令。
回复 支持 反对

使用道具 举报

发表于 2015-3-17 13:03:58 | 显示全部楼层
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-17 14:12:18 | 显示全部楼层
我嘗試用這個庫來實驗
https://github.com/angeloc/simplemodbusng/tree/master/SimpleModbusMaster

並使用範例做修改
如下

  1. #include <SimpleModbusMaster.h>

  2. /* To communicate with a slave you need to create a
  3.    packet that will contain all the information
  4.    required to communicate to that slave.
  5.    
  6.    There are numerous counters for easy diagnostic.
  7.    These are variables already implemented in a
  8.    packet. You can set and clear these variables
  9.    as needed.
  10.    
  11.    There are general modbus information counters:
  12.    requests - contains the total requests to a slave
  13.    successful_requests - contains the total successful requests
  14.    total_errors - contains the total errors as a sum
  15.    timeout - contains the total time out errors
  16.    incorrect_id_returned - contains the total incorrect id returned errors
  17.    incorrect_function_returned - contains the total incorrect function returned errors
  18.    incorrect_bytes_returned - contains the total incorrect bytes returned errors
  19.    checksum_failed - contains the total checksum failed errors
  20.    buffer_errors - contains the total buffer errors
  21.   
  22.    And there are modbus specific exception counters:
  23.    illegal_function - contains the total illegal function errors
  24.    illegal_data_address - contains the total illegal data_address errors
  25.    illegal_data_value - contains the total illegal data value errors
  26.    misc_exceptions - contains the total miscellaneous returned exceptions
  27.        
  28.    And finally there is a variable called "connection" that
  29.    at any given moment contains the current connection
  30.    status of the packet. If true then the connection is
  31.    active if false then communication will be stopped
  32.    on this packet untill the programmer sets the "connection"
  33.    variable to true explicitly. The reason for this is
  34.    because of the time out involved in modbus communication.
  35.    EACH faulty slave that's not communicating will slow down
  36.    communication on the line with the time out value. E.g.
  37.    Using a time out of 1500ms, if you have 10 slaves and 9 of them
  38.    stops communicating the latency burden placed on communication
  39.    will be 1500ms * 9 = 13,5 seconds!!!!
  40.   
  41.    modbus_update() returns the previously scanned false connection.
  42.    You can use this as the index to your packet array to find out
  43.    if the connection has failed in that packet and then react to it.
  44.    You can then try to re-enable the connecion by setting the
  45.    packet->connection attribute to true.
  46.    The index will only be available for one loop cycle, after that
  47.    it's cleared and ready to return the next false connection index
  48.    if there is one else it will return the packet array size indicating
  49.    everything is ok.
  50.    
  51.    All the error checking, updating and communication multitasking
  52.    takes place in the background!
  53.   
  54.    In general to communicate with to a slave using modbus
  55.    RTU you will request information using the specific
  56.    slave id, the function request, the starting address
  57.    and lastly the number of registers to request.
  58.    Function 3 and 16 are supported. In addition to
  59.    this broadcasting (id = 0) is supported on function 16.
  60.    Constants are provided for:
  61.    Function 3 -  READ_HOLDING_REGISTERS
  62.    Function 16 - PRESET_MULTIPLE_REGISTERS
  63.    
  64.    The example sketch will read a packet consisting
  65.    of 3 registers from address 0 using function 3 from
  66.    the GM7U LS Industrial PLC (id = 2) and then write
  67.    another packet containing the same 3 registers and
  68.    the counter information of both packets using function 16
  69.    to address 3 in the PLC. Using the supplied PLC software
  70.    you can then view in realtime what the values are in
  71.    the PLC's registers.
  72. */   

  73. // led to indicate that a communication error is present
  74. #define connection_error_led 13

  75. //////////////////// Port information ///////////////////
  76. #define baud 9600
  77. #define timeout 1000
  78. #define polling 200 // the scan rate

  79. // If the packets internal retry register matches
  80. // the set retry count then communication is stopped
  81. // on that packet. To re-enable the packet you must
  82. // set the "connection" variable to true.
  83. #define retry_count 10

  84. // used to toggle the receive/transmit pin on the driver
  85. #define TxEnablePin 2

  86. // This is the easiest way to create new packets
  87. // Add as many as you want. TOTAL_NO_OF_PACKETS
  88. // is automatically updated.
  89. enum
  90. {
  91.   PACKET1,
  92.   // leave this last entry
  93.   TOTAL_NO_OF_PACKETS
  94. };

  95. // Create an array of Packets for modbus_update()
  96. Packet packets[TOTAL_NO_OF_PACKETS];

  97. // Create a packetPointer to access each packet
  98. // individually. This is not required you can access
  99. // the array explicitly. E.g. packets[PACKET1].id = 2;
  100. // This does become tedious though...
  101. packetPointer packet1 = &packets[PACKET1];
  102. // The data from the PLC will be stored
  103. // in the regs array
  104. unsigned int regs[6];

  105. void setup()
  106. {
  107.   // read 3 registers starting at address 0
  108.   packet1->id = 1;
  109.   packet1->function = READ_HOLDING_REGISTERS;
  110.   packet1->address = 88;
  111.   packet1->no_of_registers = 2;
  112.   packet1->register_array = regs;
  113.   // P.S. the register_array entries above can be different arrays
  114.   
  115.   // Initialize communication settings etc...
  116.   modbus_configure(baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
  117.   
  118.   pinMode(connection_error_led, OUTPUT);
  119. }

  120. void loop()
  121. {
  122.   unsigned int connection_status = modbus_update(packets);
  123.   
  124.   if (connection_status != TOTAL_NO_OF_PACKETS)
  125.   {
  126.     digitalWrite(connection_error_led, HIGH);
  127.     // You could re-enable the connection by:
  128.     //packets[connection_status].connection = true;
  129.   }
  130.   else
  131.     digitalWrite(connection_error_led, LOW);
  132.   
  133.   // update the array with the counter data
  134.   regs[3] = packet1->requests;
  135.   regs[4] = packet1->successful_requests;
  136.   regs[5] = packet1->total_errors;
  137. }
复制代码


但卻一直出現這樣的亂碼




Github 的庫我大多都找來用過了,只是新手遇到亂碼又沒範例參考真的好頭痛啊

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2015-3-17 14:28:47 | 显示全部楼层
你想让程序实现什么功能,你上面的程序又实现了什么功能?
你打开的串口监视器,是监视的什么数据?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-17 14:36:51 | 显示全部楼层
您問得我好心虛
我希望Arduino 能夠讀取數位電表內的讀數
上面的程序我認為是Arduino 作為 Master 向 Slave 的數位電表要求指定的記憶體位址的數值

串口監視器這段我最心虛
其實我看程序內似乎沒有Serial.print 什麼東西出來的樣子

還請大大盡量指教,我會虛心學習的!!
回复 支持 反对

使用道具 举报

发表于 2015-3-17 17:02:33 | 显示全部楼层
本帖最后由 i7456 于 2015-3-17 17:03 编辑
45so 发表于 2015-3-17 14:36
您問得我好心虛
我希望Arduino 能夠讀取數位電表內的讀數
上面的程序我認為是Arduino 作為 Master 向 Sla ...



  // read 2 registers starting at address 88
  packet1->id = 1;
  packet1->function = READ_HOLDING_REGISTERS;
  packet1->address = 88;
  packet1->no_of_registers = 2;
  packet1->register_array = regs;
上面这个packert1的参数配置,说明是要读id为1 的modbus slave的HOLDING_REGISTERS,起始地址是88,读的寄存器的个数是2个。
这里的参数需要改为你的电表中对应的地址及个数。
从电表里都回的数据,会保存在reg[]这个数组中。

为了方便调试,建议你用有2个串口的arduino来调试程序,一个用来与电表通讯,一个用来在串口监视器中观察读回的数据。这个库文件是使用Arduino的Serial来做通信的(在这里你能看到定义https://github.com/angeloc/simpl ... pleModbusMaster.cpp),所以你需要用别的串口来输出你调试时想看到的数据。

// update the array with the counter data
  regs[3] = packet1->requests;
  regs[4] = packet1->successful_requests;
  regs[5] = packet1->total_errors;
这里的赋值,示例的程序是想向modbus slave中写入的数据,你只想读取数据,这里的变量赋值都是没用的。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-17 18:18:10 | 显示全部楼层
這樣的意思就是如果我用 Arduino +RS485 模組~
用下圖的接法


就不能使用USB 來做監視用途是嗎?!

我有看到另一個範例有加上 SoftwareSerial 去定義其他點做串口使用

我再試試看!!~

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2015-3-17 21:56:07 | 显示全部楼层
45so 发表于 2015-3-17 18:18
這樣的意思就是如果我用 Arduino +RS485 模組~
用下圖的接法

是的,这样接线的话,电脑的串口和485芯片的TX都连在了mega328的rx上,容易出现问题。
另外你若想监视下arduino发出给电表的数据,换别的串口监视软件,看16进制数据。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-18 00:57:42 | 显示全部楼层
感謝大大不斷解答小弟的疑惑,
想再請教關於 CRC 校驗的部分,
從數位電表的說明文件上看來,是必須要有CRC 校驗的,
依我目前修改的範例似乎是沒有CRC 這部分,
那這樣應該會無法完成通訊吧?!

我有找到一個庫,範例本身就帶了CRC 校驗的部分,
https://github.com/GDV0/ArduiModbus




  1. #include <Modbus_RTU.h>

  2. // Defines 1 Client and 1 Server devices
  3. Modbus_RTU myServer = Modbus_RTU(0);
  4. Modbus_RTU myClient = Modbus_RTU(0);

  5. // Defines a message and data buffers
  6. Modbus_Frame myFrame;
  7. Modbus_Data myData;

  8. unsigned short CRC16;

  9. t_baud Baudrate;
  10. unsigned long MsgTimeout;
  11. int i;

  12. void setup()
  13. {
  14.   // Force type for each device
  15.   myServer.SetType(MDB_SERVER);
  16.   myClient.SetType(MDB_CLIENT);

  17.   // Preset Server address
  18.   myServer.Server_SetAddress(5);

  19.   // Initialize serial line
  20.   myClient.GetBaudrate(&Baudrate);
  21.   Serial.begin(Baudrate);
  22.   
  23.   // Get the inter-frame time (equivalent to 3,5 char)
  24.   myClient.GetFrameTimeout(&MsgTimeout);
  25.   
  26.   Serial.println("");
  27.   Serial.println("Test Modbus_RTU library");
  28.   Serial.println("=======================");

  29.   // Modbus test type
  30.   Serial.println("");
  31.   Serial.println("   Test Function code 03: Read Holding Registers");
  32.   Serial.println("   ---------------------------------------------");
  33. }

  34. void loop()
  35. {
  36.   Serial.println("");
  37.   Serial.println("  --> Read 5 registers from address 10");
  38.   Serial.println("      Result should be 0x1111, 0x2222, 0x3333, 0x4444, 0x5555");

  39.   // Build Client request
  40.   Serial.println("");
  41.   Serial.println("  Request sent by the Client");
  42.   myClient.Client_ReadHoldingRegisters(5, 10, 5, &myFrame);
  43.   DisplayFrame(&myFrame);
  44.    
  45.   // Build Server response
  46.   if (myServer.Server_Update(&myFrame))
  47.   {
  48.     Serial.println("  --> OK Response available");
  49.     Serial.println("");
  50.     Serial.println("  Packet sent by the Server");
  51.     DisplayFrame(&myFrame);
  52.    
  53.     // extract Data received by the Client
  54.     myClient.Client_Update(&myFrame, &myData);
  55.     Serial.println("");
  56.     Serial.println("  Data");
  57.     DisplayData(&myData);
  58.   }
  59.   else
  60.     Serial.println("  --> No response available");
  61.   
  62.   while(1)
  63.   {
  64.   }
  65. }

  66. // Function to display a complete frame (Debug mode)
  67. void DisplayFrame(Modbus_Frame* msg)
  68. {
  69.   int i;
  70.   
  71.   Serial.print("  Frame size ");
  72.   Serial.print(msg->length, DEC);
  73.   Serial.print(" -> ");
  74.   if (msg->length > 0)
  75.   {
  76.     for (i = 0; i < msg->length; i++)
  77.     {
  78.       Serial.print((unsigned char)(msg->data[i])>>4, HEX);
  79.       Serial.print((unsigned char)(msg->data[i])&0x0F, HEX);
  80.       Serial.print(" ");
  81.     }  
  82.   Serial.println();
  83.   }
  84. }

  85. // Function to display Data received
  86. void DisplayData(Modbus_Data* Data)
  87. {
  88.   int i;
  89.   
  90.   Serial.print("    ==> Number of data received = ");
  91.   Serial.println(Data->length, DEC);
  92.   if (Data->length > 0)
  93.   {
  94.     Serial.print("        Data type = ");
  95.     switch (Data->type)
  96.     {
  97.       case MDB_BIT:
  98.           Serial.println("BIT");
  99.           Serial.print("        Data = ");
  100.           for (i = 0; i < Data->length; i++)
  101.           {
  102.             Serial.print(Data->data[i] & 1, DEC);
  103.             Serial.print((Data->data[i] & 2)>>1, DEC);
  104.             Serial.print((Data->data[i] & 4)>>2, DEC);
  105.             Serial.print((Data->data[i] & 8)>>3, DEC);
  106.             Serial.print((Data->data[i] & 18)>>4, DEC);
  107.             Serial.print((Data->data[i] & 32)>>5, DEC);
  108.             Serial.print((Data->data[i] & 64)>>6, DEC);
  109.             Serial.print((Data->data[i] & 128)>>7, DEC);
  110.           }
  111.           break;
  112.       case MDB_BYTE:
  113.           Serial.println("BYTE");
  114.           Serial.print("        Data = ");
  115.           for (i = 0; i < Data->length; i++)
  116.           {
  117.             Serial.print("0x");
  118.             Serial.print((unsigned char)(Data->data[i])>>4, HEX);
  119.             Serial.print((unsigned char)(Data->data[i])&0x0F, HEX);
  120.             Serial.print(" ");
  121.           }
  122.           break;
  123.       case MDB_WORD:
  124.           Serial.println("WORD");
  125.           Serial.print("        Data = ");
  126.           for (i = 0; i < Data->length; i++)
  127.           {
  128.             Serial.print("0x");
  129.             Serial.print((unsigned char)(Data->data[i*2])>>4, HEX);
  130.             Serial.print((unsigned char)(Data->data[i*2])&0x0F, HEX);
  131.             Serial.print((unsigned char)(Data->data[i*2+1])>>4, HEX);
  132.             Serial.print((unsigned char)(Data->data[i*2+1])&0x0F, HEX);
  133.             Serial.print(" ");
  134.           }
  135.           break;
  136.       default:
  137.           Serial.println("Unknown");
  138.           break;
  139.     }
  140.   Serial.println();
  141.   }
  142. }

  143. void DisplayDataOnly(Modbus_Data* Data)
  144. {
  145.   int i;
  146.   if (Data->length > 0)
  147.   {
  148.     for (i = 0; i < Data->length; i++)
  149.     {
  150.       if (Data->type == MDB_BIT)
  151.       {
  152.         Serial.print(Data->data[i] & 1, DEC);
  153.         Serial.print((Data->data[i] & 2)>>1, DEC);
  154.         Serial.print((Data->data[i] & 4)>>2, DEC);
  155.         Serial.print((Data->data[i] & 8)>>3, DEC);
  156.         Serial.print((Data->data[i] & 18)>>4, DEC);
  157.         Serial.print((Data->data[i] & 32)>>5, DEC);
  158.         Serial.print((Data->data[i] & 64)>>6, DEC);
  159.         Serial.print((Data->data[i] & 128)>>7, DEC);
  160.       }
  161.       if (Data->type == MDB_BYTE)
  162.       {
  163.         Serial.print("0x");
  164.         Serial.print((unsigned char)(Data->data[i])>>4, HEX);
  165.         Serial.print((unsigned char)(Data->data[i])&0x0F, HEX);
  166.         Serial.print(" ");
  167.       }
  168.       if (Data->type == MDB_WORD)
  169.       {
  170.         Serial.print("0x");
  171.         Serial.print((unsigned char)(Data->data[i*2])>>4, HEX);
  172.         Serial.print((unsigned char)(Data->data[i*2])&0x0F, HEX);
  173.         Serial.print((unsigned char)(Data->data[i*2+1])>>4, HEX);
  174.         Serial.print((unsigned char)(Data->data[i*2+1])&0x0F, HEX);
  175.         Serial.print(" ");
  176.       }
  177.     }
  178.     Serial.println();
  179.   }
  180. }


  181. /******************************************************************************
  182. *  Callback functions
  183. *  Allow the user to define all device objects
  184. ******************************************************************************/

  185. /******************************************************************************
  186. * t_status Modbus_CB_GetRegister (unsigned short Addr, int* Value)
  187. *     Callback function to read register value
  188. * Parameters:
  189. *     - Addr: Address of the register from 0x0000 to 0xFFFF
  190. *     - Value: pointer to a variable which will contain the register value
  191. * Return value:
  192. *     - OK if register address exists
  193. *     - NOK if register address doesn't exist
  194. ******************************************************************************/
  195. t_status Modbus_CB_GetRegister(unsigned short Addr, int* Value)
  196. {
  197.   t_status Status = OK;
  198.   
  199.   // Value = f(Addr) To be defined by application
  200.   switch(Addr)
  201.   {
  202.     case 10:
  203.         *Value = 0x1111;
  204.         break;
  205.     case 11:
  206.         *Value = 0x2222;
  207.         break;
  208.      case 12:
  209.         *Value = 0x3333;
  210.         break;
  211.      case 13:
  212.         *Value = 0x4444;
  213.         break;
  214.     case 14:
  215.         *Value = 0x5555;
  216.         break;
  217.     default:
  218.         Status = NOK;
  219. }
  220.   return (Status);
  221. }
复制代码


這個範例看起來像是一個模擬通訊的功能,
因為我就算沒接上 RS485模組,他也能運行,
自己當自己是Master 同時也是 Slave,是這樣嗎?!

這個就自帶了CRC 校驗~
研究了一會,還是搞不太懂該如何將他實際運用...
從// Build Server response 之後的就看不懂了~
回复 支持 反对

使用道具 举报

发表于 2015-3-18 07:58:56 | 显示全部楼层
要是我调试的话,先在Arduino上用最简单的串口通信把“ 01 03 00 3A 00 02 +crc”直接发给电表,然后把电表返回的代码收回来显示出来。用这个过程先看看硬件回路有没有问题。

硬件回路没问题后再切换到调试Modbus库,其实如果你不考虑扩展的话,固定几句话发发就行了,Modbus库都不需要
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-18 08:05:42 | 显示全部楼层
這樣聽起來感覺可行度滿高的!!~
直接使用 Serial.print()
跟Serial.read()
這樣嗎?!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-14 10:07 , Processed in 0.038739 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表