Arduino rs485 Modbus 求助!!
各位好,我是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 到底是什麼?!
位址後面的括弧 又是什麼呢?!
奢求大大有沒有範例提點一下阿!!!~ 先謝了!!
hi lo是高位和地位的意思,Modbus是问答式的,我觉得你的命令应该是正确,括号里面有可能是4000开始的地址吧,不行多试试。 http://playground.arduino.cc/Code/ModbusMaster 本帖最后由 45so 于 2015-3-17 12:42 编辑
先感謝二位回覆
剛剛才注意到我文章發錯區了
應該發在求助區才對~真是抱歉
指令的部分我想也是沒問題的
用PC模擬Master 是能夠正確讀取到數值
但Arduino 上怎麼搞都是回傳連線錯誤
另外官方的文件小弟也有看過了,
但那個庫不知道是太久沒更新還是?
編譯一直過不了
網路上其他的庫也有試過,但不是無法連線不然就是回傳亂碼
亂碼的部分我也有確認過baud rate 是沒設定錯誤的!~
苦惱好幾天了啊
用PC和Arduino连上试试,Arduino的外围接口用连仪表的那部分,看能不能收到PC的 01 03 00 3A 00 02 +crc指令。 https://github.com/search?utf8=%E2%9C%93&q=arduino+modbus 我嘗試用這個庫來實驗
https://github.com/angeloc/simplemodbusng/tree/master/SimpleModbusMaster
並使用範例做修改
如下
#include <SimpleModbusMaster.h>
/* To communicate with a slave you need to create a
packet that will contain all the information
required to communicate to that slave.
There are numerous counters for easy diagnostic.
These are variables already implemented in a
packet. You can set and clear these variables
as needed.
There are general modbus information counters:
requests - contains the total requests to a slave
successful_requests - contains the total successful requests
total_errors - contains the total errors as a sum
timeout - contains the total time out errors
incorrect_id_returned - contains the total incorrect id returned errors
incorrect_function_returned - contains the total incorrect function returned errors
incorrect_bytes_returned - contains the total incorrect bytes returned errors
checksum_failed - contains the total checksum failed errors
buffer_errors - contains the total buffer errors
And there are modbus specific exception counters:
illegal_function - contains the total illegal function errors
illegal_data_address - contains the total illegal data_address errors
illegal_data_value - contains the total illegal data value errors
misc_exceptions - contains the total miscellaneous returned exceptions
And finally there is a variable called "connection" that
at any given moment contains the current connection
status of the packet. If true then the connection is
active if false then communication will be stopped
on this packet untill the programmer sets the "connection"
variable to true explicitly. The reason for this is
because of the time out involved in modbus communication.
EACH faulty slave that's not communicating will slow down
communication on the line with the time out value. E.g.
Using a time out of 1500ms, if you have 10 slaves and 9 of them
stops communicating the latency burden placed on communication
will be 1500ms * 9 = 13,5 seconds!!!!
modbus_update() returns the previously scanned false connection.
You can use this as the index to your packet array to find out
if the connection has failed in that packet and then react to it.
You can then try to re-enable the connecion by setting the
packet->connection attribute to true.
The index will only be available for one loop cycle, after that
it's cleared and ready to return the next false connection index
if there is one else it will return the packet array size indicating
everything is ok.
All the error checking, updating and communication multitasking
takes place in the background!
In general to communicate with to a slave using modbus
RTU you will request information using the specific
slave id, the function request, the starting address
and lastly the number of registers to request.
Function 3 and 16 are supported. In addition to
this broadcasting (id = 0) is supported on function 16.
Constants are provided for:
Function 3 -READ_HOLDING_REGISTERS
Function 16 - PRESET_MULTIPLE_REGISTERS
The example sketch will read a packet consisting
of 3 registers from address 0 using function 3 from
the GM7U LS Industrial PLC (id = 2) and then write
another packet containing the same 3 registers and
the counter information of both packets using function 16
to address 3 in the PLC. Using the supplied PLC software
you can then view in realtime what the values are in
the PLC's registers.
*/
// led to indicate that a communication error is present
#define connection_error_led 13
//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 200 // the scan rate
// If the packets internal retry register matches
// the set retry count then communication is stopped
// on that packet. To re-enable the packet you must
// set the "connection" variable to true.
#define retry_count 10
// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 2
// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
PACKET1,
// leave this last entry
TOTAL_NO_OF_PACKETS
};
// Create an array of Packets for modbus_update()
Packet packets;
// Create a packetPointer to access each packet
// individually. This is not required you can access
// the array explicitly. E.g. packets.id = 2;
// This does become tedious though...
packetPointer packet1 = &packets;
// The data from the PLC will be stored
// in the regs array
unsigned int regs;
void setup()
{
// read 3 registers starting at address 0
packet1->id = 1;
packet1->function = READ_HOLDING_REGISTERS;
packet1->address = 88;
packet1->no_of_registers = 2;
packet1->register_array = regs;
// P.S. the register_array entries above can be different arrays
// Initialize communication settings etc...
modbus_configure(baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
pinMode(connection_error_led, OUTPUT);
}
void loop()
{
unsigned int connection_status = modbus_update(packets);
if (connection_status != TOTAL_NO_OF_PACKETS)
{
digitalWrite(connection_error_led, HIGH);
// You could re-enable the connection by:
//packets.connection = true;
}
else
digitalWrite(connection_error_led, LOW);
// update the array with the counter data
regs = packet1->requests;
regs = packet1->successful_requests;
regs = packet1->total_errors;
}
但卻一直出現這樣的亂碼
Github 的庫我大多都找來用過了,只是新手遇到亂碼又沒範例參考真的好頭痛啊
你想让程序实现什么功能,你上面的程序又实现了什么功能?
你打开的串口监视器,是监视的什么数据? 您問得我好心虛
我希望Arduino 能夠讀取數位電表內的讀數
上面的程序我認為是Arduino 作為 Master 向 Slave 的數位電表要求指定的記憶體位址的數值
串口監視器這段我最心虛
其實我看程序內似乎沒有Serial.print 什麼東西出來的樣子
還請大大盡量指教,我會虛心學習的!!
本帖最后由 i7456 于 2015-3-17 17:03 编辑
45so 发表于 2015-3-17 14:36 static/image/common/back.gif
您問得我好心虛
我希望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/simplemodbusng/blob/master/SimpleModbusMaster/SimpleModbusMaster.cpp),所以你需要用别的串口来输出你调试时想看到的数据。
// update the array with the counter data
regs = packet1->requests;
regs = packet1->successful_requests;
regs = packet1->total_errors;
这里的赋值,示例的程序是想向modbus slave中写入的数据,你只想读取数据,这里的变量赋值都是没用的。 這樣的意思就是如果我用 Arduino +RS485 模組~
用下圖的接法
就不能使用USB 來做監視用途是嗎?!
我有看到另一個範例有加上 SoftwareSerial 去定義其他點做串口使用
我再試試看!!~ 45so 发表于 2015-3-17 18:18 static/image/common/back.gif
這樣的意思就是如果我用 Arduino +RS485 模組~
用下圖的接法
是的,这样接线的话,电脑的串口和485芯片的TX都连在了mega328的rx上,容易出现问题。
另外你若想监视下arduino发出给电表的数据,换别的串口监视软件,看16进制数据。 感謝大大不斷解答小弟的疑惑,
想再請教關於 CRC 校驗的部分,
從數位電表的說明文件上看來,是必須要有CRC 校驗的,
依我目前修改的範例似乎是沒有CRC 這部分,
那這樣應該會無法完成通訊吧?!
我有找到一個庫,範例本身就帶了CRC 校驗的部分,
https://github.com/GDV0/ArduiModbus
#include <Modbus_RTU.h>
// Defines 1 Client and 1 Server devices
Modbus_RTU myServer = Modbus_RTU(0);
Modbus_RTU myClient = Modbus_RTU(0);
// Defines a message and data buffers
Modbus_Frame myFrame;
Modbus_Data myData;
unsigned short CRC16;
t_baud Baudrate;
unsigned long MsgTimeout;
int i;
void setup()
{
// Force type for each device
myServer.SetType(MDB_SERVER);
myClient.SetType(MDB_CLIENT);
// Preset Server address
myServer.Server_SetAddress(5);
// Initialize serial line
myClient.GetBaudrate(&Baudrate);
Serial.begin(Baudrate);
// Get the inter-frame time (equivalent to 3,5 char)
myClient.GetFrameTimeout(&MsgTimeout);
Serial.println("");
Serial.println("Test Modbus_RTU library");
Serial.println("=======================");
// Modbus test type
Serial.println("");
Serial.println(" Test Function code 03: Read Holding Registers");
Serial.println(" ---------------------------------------------");
}
void loop()
{
Serial.println("");
Serial.println("--> Read 5 registers from address 10");
Serial.println(" Result should be 0x1111, 0x2222, 0x3333, 0x4444, 0x5555");
// Build Client request
Serial.println("");
Serial.println("Request sent by the Client");
myClient.Client_ReadHoldingRegisters(5, 10, 5, &myFrame);
DisplayFrame(&myFrame);
// Build Server response
if (myServer.Server_Update(&myFrame))
{
Serial.println("--> OK Response available");
Serial.println("");
Serial.println("Packet sent by the Server");
DisplayFrame(&myFrame);
// extract Data received by the Client
myClient.Client_Update(&myFrame, &myData);
Serial.println("");
Serial.println("Data");
DisplayData(&myData);
}
else
Serial.println("--> No response available");
while(1)
{
}
}
// Function to display a complete frame (Debug mode)
void DisplayFrame(Modbus_Frame* msg)
{
int i;
Serial.print("Frame size ");
Serial.print(msg->length, DEC);
Serial.print(" -> ");
if (msg->length > 0)
{
for (i = 0; i < msg->length; i++)
{
Serial.print((unsigned char)(msg->data)>>4, HEX);
Serial.print((unsigned char)(msg->data)&0x0F, HEX);
Serial.print(" ");
}
Serial.println();
}
}
// Function to display Data received
void DisplayData(Modbus_Data* Data)
{
int i;
Serial.print(" ==> Number of data received = ");
Serial.println(Data->length, DEC);
if (Data->length > 0)
{
Serial.print(" Data type = ");
switch (Data->type)
{
case MDB_BIT:
Serial.println("BIT");
Serial.print(" Data = ");
for (i = 0; i < Data->length; i++)
{
Serial.print(Data->data & 1, DEC);
Serial.print((Data->data & 2)>>1, DEC);
Serial.print((Data->data & 4)>>2, DEC);
Serial.print((Data->data & 8)>>3, DEC);
Serial.print((Data->data & 18)>>4, DEC);
Serial.print((Data->data & 32)>>5, DEC);
Serial.print((Data->data & 64)>>6, DEC);
Serial.print((Data->data & 128)>>7, DEC);
}
break;
case MDB_BYTE:
Serial.println("BYTE");
Serial.print(" Data = ");
for (i = 0; i < Data->length; i++)
{
Serial.print("0x");
Serial.print((unsigned char)(Data->data)>>4, HEX);
Serial.print((unsigned char)(Data->data)&0x0F, HEX);
Serial.print(" ");
}
break;
case MDB_WORD:
Serial.println("WORD");
Serial.print(" Data = ");
for (i = 0; i < Data->length; i++)
{
Serial.print("0x");
Serial.print((unsigned char)(Data->data)>>4, HEX);
Serial.print((unsigned char)(Data->data)&0x0F, HEX);
Serial.print((unsigned char)(Data->data)>>4, HEX);
Serial.print((unsigned char)(Data->data)&0x0F, HEX);
Serial.print(" ");
}
break;
default:
Serial.println("Unknown");
break;
}
Serial.println();
}
}
void DisplayDataOnly(Modbus_Data* Data)
{
int i;
if (Data->length > 0)
{
for (i = 0; i < Data->length; i++)
{
if (Data->type == MDB_BIT)
{
Serial.print(Data->data & 1, DEC);
Serial.print((Data->data & 2)>>1, DEC);
Serial.print((Data->data & 4)>>2, DEC);
Serial.print((Data->data & 8)>>3, DEC);
Serial.print((Data->data & 18)>>4, DEC);
Serial.print((Data->data & 32)>>5, DEC);
Serial.print((Data->data & 64)>>6, DEC);
Serial.print((Data->data & 128)>>7, DEC);
}
if (Data->type == MDB_BYTE)
{
Serial.print("0x");
Serial.print((unsigned char)(Data->data)>>4, HEX);
Serial.print((unsigned char)(Data->data)&0x0F, HEX);
Serial.print(" ");
}
if (Data->type == MDB_WORD)
{
Serial.print("0x");
Serial.print((unsigned char)(Data->data)>>4, HEX);
Serial.print((unsigned char)(Data->data)&0x0F, HEX);
Serial.print((unsigned char)(Data->data)>>4, HEX);
Serial.print((unsigned char)(Data->data)&0x0F, HEX);
Serial.print(" ");
}
}
Serial.println();
}
}
/******************************************************************************
*Callback functions
*Allow the user to define all device objects
******************************************************************************/
/******************************************************************************
* t_status Modbus_CB_GetRegister (unsigned short Addr, int* Value)
* Callback function to read register value
* Parameters:
* - Addr: Address of the register from 0x0000 to 0xFFFF
* - Value: pointer to a variable which will contain the register value
* Return value:
* - OK if register address exists
* - NOK if register address doesn't exist
******************************************************************************/
t_status Modbus_CB_GetRegister(unsigned short Addr, int* Value)
{
t_status Status = OK;
// Value = f(Addr) To be defined by application
switch(Addr)
{
case 10:
*Value = 0x1111;
break;
case 11:
*Value = 0x2222;
break;
case 12:
*Value = 0x3333;
break;
case 13:
*Value = 0x4444;
break;
case 14:
*Value = 0x5555;
break;
default:
Status = NOK;
}
return (Status);
}
這個範例看起來像是一個模擬通訊的功能,
因為我就算沒接上 RS485模組,他也能運行,
自己當自己是Master 同時也是 Slave,是這樣嗎?!
這個就自帶了CRC 校驗~
研究了一會,還是搞不太懂該如何將他實際運用...
從// Build Server response 之後的就看不懂了~
要是我调试的话,先在Arduino上用最简单的串口通信把“ 01 03 00 3A 00 02 +crc”直接发给电表,然后把电表返回的代码收回来显示出来。用这个过程先看看硬件回路有没有问题。
硬件回路没问题后再切换到调试Modbus库,其实如果你不考虑扩展的话,固定几句话发发就行了,Modbus库都不需要:lol:lol:lol。 這樣聽起來感覺可行度滿高的!!~
直接使用 Serial.print()
跟Serial.read()
這樣嗎?!
页:
[1]
2