Arduino to Modbus RTU Master
Hallo zusammen,
Ich habe einen Arduino als ModBus Slave, welcher über einen Seriell-2-TTL-USB-Adapter an das RevPi Core 3+ Modul angeschlossen wird.
Die Konfiguration des Master-Moduls habe ich gemäß dem ModBus RTU Master Tutorial vorgenommen.
Leider kann ich jedoch keine Werte empfangen. Ich nutze die Funktion Read_Holding_Registers mit der SlaveID 11 (gemäß meiner Konfiguration auf dem Arduino) und eine Baudrate von 19200. Meine Code des Arduinos habe ich hier: Empfangen soll es eigentlich vier Float-Werte also 8 Register, angefangen bei Input_Word_1.
Um die Konfiguration zu validieren habe ich die Software https://www.modbustools.com/modbus_poll.html genutzt und dort empfange ich die Werte.
Deren Konfiguration ist in diesen Bildern ersichtlich: Meine Pictory-Config kann ich gerne per PM zur Verfügung stellen.
Ich hoffe jemand woran das liegen könnte, dass es bei mir nicht funktioniert...
Viele Grüße
Ruwen
Ich habe einen Arduino als ModBus Slave, welcher über einen Seriell-2-TTL-USB-Adapter an das RevPi Core 3+ Modul angeschlossen wird.
Die Konfiguration des Master-Moduls habe ich gemäß dem ModBus RTU Master Tutorial vorgenommen.
Leider kann ich jedoch keine Werte empfangen. Ich nutze die Funktion Read_Holding_Registers mit der SlaveID 11 (gemäß meiner Konfiguration auf dem Arduino) und eine Baudrate von 19200. Meine Code des Arduinos habe ich hier: Empfangen soll es eigentlich vier Float-Werte also 8 Register, angefangen bei Input_Word_1.
Um die Konfiguration zu validieren habe ich die Software https://www.modbustools.com/modbus_poll.html genutzt und dort empfange ich die Werte.
Deren Konfiguration ist in diesen Bildern ersichtlich: Meine Pictory-Config kann ich gerne per PM zur Verfügung stellen.
Ich hoffe jemand woran das liegen könnte, dass es bei mir nicht funktioniert...
Viele Grüße
Ruwen
Re: Arduino to Modbus RTU Master
Hallo Ruwen, sieht ja schon ganz gut aus, was Du beschrieben hast. Damit wir Dir helfen können benötige ich den Code als Text und die PiCtory Konfiguration. Kannst Du das posten?
Re: Arduino to Modbus RTU Master
Hallo Dirk,
hier die Config und die .ino Datei
hier die Config und die .ino Datei
Re: Arduino to Modbus RTU Master
Hallo,
ich habe das ganze über TCP/IP mit dem Leonardo gemacht. Vieleicht hilft dir das ja weiter.
ist nur ein auszug aus meinem Code
dazu noch die MgsModbus.ccp
und MgsModbus.h
ich habe das ganze über TCP/IP mit dem Leonardo gemacht. Vieleicht hilft dir das ja weiter.
Code: Select all
#include <Wire.h>
#include <SPI.h>
#include <Ethernet2.h>
#include "MgsModbus.h"
#include "TimerOne.h"
#include <avr/wdt.h>
MgsModbus Mb;
// Ethernet settings (depending on MAC and Local network)
byte mac[] = { macadress eintragen };
IPAddress ip( deine IP Adresse vom Arduino );
void setup()
{
Serial.begin(9600);
Wire.begin(); // enable I2C port.
Ethernet.begin(mac, ip); // start etehrnet interface
Timer1.initialize(10 * 1000); // zeit in microsekunden (1 / 1 000 000) s
Timer1.attachInterrupt(mb);
wdt_enable(WDTO_8S); // Watchdog timer alle 8 s ohne timerreset wird das gerät neu gestartet
// Mb.SetBit(0,false);
Mb.MbData[0] = 0;
Mb.MbData[1] = 0;
Mb.MbData[2] = 0;
Mb.MbData[3] = 0;
Mb.MbData[4] = 0;
}
void mb() {
Mb.MbsRun();
Mb.MbData[0] = ***;
Mb.MbData[1] = ***;
Mb.MbData[2] = *** ;
Mb.MbData[3] = ***;
Mb.MbData[4] = ***;
}
void writeCommand(int addr ,char *cmd) {
dazu noch die MgsModbus.ccp
Code: Select all
#include "MgsModbus.h"
// For Arduino 1.0
EthernetServer MbServer(MB_PORT);
EthernetClient MbmClient;
// #define DEBUG
MgsModbus::MgsModbus()
{
}
//****************** Send data for ModBusMaster ****************
void MgsModbus::Req(MB_FC FC, word Ref, word Count, word Pos)
{
MbmFC = FC;
byte ServerIp[] = {192,168,178,10};
MbmByteArray[0] = 0; // ID high byte
MbmByteArray[1] = 1; // ID low byte
MbmByteArray[2] = 0; // protocol high byte
MbmByteArray[3] = 0; // protocol low byte
MbmByteArray[5] = 6; // Lenght low byte;
MbmByteArray[4] = 0; // Lenght high byte
MbmByteArray[6] = 1; // unit ID
MbmByteArray[7] = FC; // function code
MbmByteArray[8] = highByte(Ref);
MbmByteArray[9] = lowByte(Ref);
//****************** Read Coils (1) & Read Input discretes (2) **********************
if(FC == MB_FC_READ_COILS || FC == MB_FC_READ_DISCRETE_INPUT) {
if (Count < 1) {Count = 1;}
if (Count > 125) {Count = 1000;}
MbmByteArray[10] = highByte(Count);
MbmByteArray[11] = lowByte(Count);
}
//****************** Read Registers (3) & Read Input registers (4) ******************
if(FC == MB_FC_READ_REGISTERS || FC == MB_FC_READ_INPUT_REGISTER) {
if (Count < 1) {Count = 1;}
if (Count > 125) {Count = 125;}
MbmByteArray[10] = highByte(Count);
MbmByteArray[11] = lowByte(Count);
}
//****************** Write Coil (5) **********************
if(MbmFC == MB_FC_WRITE_COIL) {
if (GetBit(Pos)) {MbmByteArray[10] = 0xFF;} else {MbmByteArray[10] = 0;} // 0xFF coil on 0x00 coil off
MbmByteArray[11] = 0; // always zero
}
//****************** Write Register (6) ******************
if(MbmFC == MB_FC_WRITE_REGISTER) {
MbmByteArray[10] = highByte(MbData[Pos]);
MbmByteArray[11] = lowByte(MbData[Pos]);
}
//****************** Write Multiple Coils (15) **********************
// not fuly tested
if(MbmFC == MB_FC_WRITE_MULTIPLE_COILS) {
if (Count < 1) {Count = 1;}
if (Count > 800) {Count = 800;}
MbmByteArray[10] = highByte(Count);
MbmByteArray[11] = lowByte(Count);
MbmByteArray[12] = (Count + 7) /8;
MbmByteArray[4] = highByte(MbmByteArray[12] + 7); // Lenght high byte
MbmByteArray[5] = lowByte(MbmByteArray[12] + 7); // Lenght low byte;
for (int i=0; i<Count; i++) {
bitWrite(MbmByteArray[13+(i/8)],i-((i/8)*8),GetBit(Pos+i));
}
}
//****************** Write Multiple Registers (16) ******************
if(MbmFC == MB_FC_WRITE_MULTIPLE_REGISTERS) {
if (Count < 1) {Count = 1;}
if (Count > 100) {Count = 100;}
MbmByteArray[10] = highByte(Count);
MbmByteArray[11] = lowByte(Count);
MbmByteArray[12] = (Count*2);
MbmByteArray[4] = highByte(MbmByteArray[12] + 7); // Lenght high byte
MbmByteArray[5] = lowByte(MbmByteArray[12] + 7); // Lenght low byte;
for (int i=0; i<Count;i++) {
MbmByteArray[(i*2)+13] = highByte (MbData[Pos + i]);
MbmByteArray[(i*2)+14] = lowByte (MbData[Pos + i]);
}
}
//****************** ?? ******************
if (MbmClient.connect(ServerIp,502)) {
#ifdef DEBUG
Serial.println("connected with modbus slave");
Serial.print("Master request: ");
for(int i=0;i<MbmByteArray[5]+6;i++) {
if(MbmByteArray[i] < 16){Serial.print("0");}
Serial.print(MbmByteArray[i],HEX);
if (i != MbmByteArray[5]+5) {Serial.print(".");} else {Serial.println();}
}
#endif
for(int i=0;i<MbmByteArray[5]+6;i++) {
MbmClient.write(MbmByteArray[i]);
}
MbmCounter = 0;
MbmByteArray[7] = 0;
MbmPos = Pos;
MbmBitCount = Count;
} else {
#ifdef DEBUG
Serial.println("connection with modbus master failed");
#endif
MbmClient.stop();
}
}
//****************** Recieve data for ModBusMaster ****************
void MgsModbus::MbmRun()
{
//****************** Read from socket ****************
while (MbmClient.available()) {
MbmByteArray[MbmCounter] = MbmClient.read();
if (MbmCounter > 4) {
if (MbmCounter == MbmByteArray[5] + 5) { // the full answer is recieved
MbmClient.stop();
MbmProcess();
#ifdef DEBUG
Serial.println("recieve klaar");
#endif
}
}
MbmCounter++;
}
}
void MgsModbus::MbmProcess()
{
MbmFC = SetFC(int (MbmByteArray[7]));
#ifdef DEBUG
for (int i=0;i<MbmByteArray[5]+6;i++) {
if(MbmByteArray[i] < 16) {Serial.print("0");}
Serial.print(MbmByteArray[i],HEX);
if (i != MbmByteArray[5]+5) {Serial.print(".");
} else {Serial.println();}
}
#endif
//****************** Read Coils (1) & Read Input discretes (2) **********************
if(MbmFC == MB_FC_READ_COILS || MbmFC == MB_FC_READ_DISCRETE_INPUT) {
word Count = MbmByteArray[8] * 8;
if (MbmBitCount < Count) {
Count = MbmBitCount;
}
for (int i=0;i<Count;i++) {
if (i + MbmPos < MbDataLen * 16) {
SetBit(i + MbmPos,bitRead(MbmByteArray[(i/8)+9],i-((i/8)*8)));
}
}
}
//****************** Read Registers (3) & Read Input registers (4) ******************
if(MbmFC == MB_FC_READ_REGISTERS || MbmFC == MB_FC_READ_INPUT_REGISTER) {
word Pos = MbmPos;
for (int i=0;i<MbmByteArray[8];i=i+2) {
if (Pos < MbDataLen) {
MbData[Pos] = (MbmByteArray[i+9] * 0x100) + MbmByteArray[i+1+9];
Pos++;
}
}
}
//****************** Write Coil (5) **********************
if(MbmFC == MB_FC_WRITE_COIL){
}
//****************** Write Register (6) ******************
if(MbmFC == MB_FC_WRITE_REGISTER){
}
//****************** Write Multiple Coils (15) **********************
if(MbmFC == MB_FC_WRITE_MULTIPLE_COILS){
}
//****************** Write Multiple Registers (16) ******************
if(MbmFC == MB_FC_WRITE_MULTIPLE_REGISTERS){
}
}
//****************** Recieve data for ModBusSlave ****************
void MgsModbus::MbsRun()
{
//****************** Read from socket ****************
EthernetClient client = MbServer.available();
if(client.available())
{
delay(10);
int i = 0;
while(client.available())
{
MbsByteArray[i] = client.read();
i++;
}
MbsFC = SetFC(MbsByteArray[7]); //Byte 7 of request is FC
}
int Start, WordDataLength, ByteDataLength, CoilDataLength, MessageLength;
//****************** Read Coils (1 & 2) **********************
if(MbsFC == MB_FC_READ_COILS || MbsFC == MB_FC_READ_DISCRETE_INPUT) {
Start = word(MbsByteArray[8],MbsByteArray[9]);
CoilDataLength = word(MbsByteArray[10],MbsByteArray[11]);
ByteDataLength = CoilDataLength / 8;
if(ByteDataLength * 8 < CoilDataLength) ByteDataLength++;
CoilDataLength = ByteDataLength * 8;
MbsByteArray[5] = ByteDataLength + 3; //Number of bytes after this one.
MbsByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data).
for(int i = 0; i < ByteDataLength ; i++)
{
MbsByteArray[9 + i] = 0; // To get all remaining not written bits zero
for(int j = 0; j < 8; j++)
{
bitWrite(MbsByteArray[9 + i], j, GetBit(Start + i * 8 + j));
}
}
MessageLength = ByteDataLength + 9;
client.write(MbsByteArray, MessageLength);
MbsFC = MB_FC_NONE;
}
//****************** Read Registers (3 & 4) ******************
if(MbsFC == MB_FC_READ_REGISTERS || MbsFC == MB_FC_READ_INPUT_REGISTER) {
Start = word(MbsByteArray[8],MbsByteArray[9]);
WordDataLength = word(MbsByteArray[10],MbsByteArray[11]);
ByteDataLength = WordDataLength * 2;
MbsByteArray[5] = ByteDataLength + 3; //Number of bytes after this one.
MbsByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data).
for(int i = 0; i < WordDataLength; i++)
{
MbsByteArray[ 9 + i * 2] = highByte(MbData[Start + i]);
MbsByteArray[10 + i * 2] = lowByte(MbData[Start + i]);
}
MessageLength = ByteDataLength + 9;
client.write(MbsByteArray, MessageLength);
MbsFC = MB_FC_NONE;
}
//****************** Write Coil (5) **********************
if(MbsFC == MB_FC_WRITE_COIL) {
Start = word(MbsByteArray[8],MbsByteArray[9]);
if (word(MbsByteArray[10],MbsByteArray[11]) == 0xFF00){SetBit(Start,true);}
if (word(MbsByteArray[10],MbsByteArray[11]) == 0x0000){SetBit(Start,false);}
MbsByteArray[5] = 2; //Number of bytes after this one.
MessageLength = 8;
client.write(MbsByteArray, MessageLength);
MbsFC = MB_FC_NONE;
}
//****************** Write Register (6) ******************
if(MbsFC == MB_FC_WRITE_REGISTER) {
Start = word(MbsByteArray[8],MbsByteArray[9]);
MbData[Start] = word(MbsByteArray[10],MbsByteArray[11]);
MbsByteArray[5] = 6; //Number of bytes after this one.
MessageLength = 12;
client.write(MbsByteArray, MessageLength);
MbsFC = MB_FC_NONE;
}
//****************** Write Multiple Coils (15) **********************
if(MbsFC == MB_FC_WRITE_MULTIPLE_COILS) {
Start = word(MbsByteArray[8],MbsByteArray[9]);
CoilDataLength = word(MbsByteArray[10],MbsByteArray[11]);
MbsByteArray[5] = 6;
for(int i = 0; i < CoilDataLength; i++)
{
SetBit(Start + i,bitRead(MbsByteArray[13 + (i/8)],i-((i/8)*8)));
}
MessageLength = 12;
client.write(MbsByteArray, MessageLength);
MbsFC = MB_FC_NONE;
}
//****************** Write Multiple Registers (16) ******************
if(MbsFC == MB_FC_WRITE_MULTIPLE_REGISTERS) {
Start = word(MbsByteArray[8],MbsByteArray[9]);
WordDataLength = word(MbsByteArray[10],MbsByteArray[11]);
ByteDataLength = WordDataLength * 2;
MbsByteArray[5] = 6;
for(int i = 0; i < WordDataLength; i++)
{
MbData[Start + i] = word(MbsByteArray[ 13 + i * 2],MbsByteArray[14 + i * 2]);
}
MessageLength = 12;
client.write(MbsByteArray, MessageLength);
MbsFC = MB_FC_NONE;
}
}
//****************** ?? ******************
MB_FC MgsModbus::SetFC(int fc)
{
MB_FC FC;
FC = MB_FC_NONE;
if(fc == 1) FC = MB_FC_READ_COILS;
if(fc == 2) FC = MB_FC_READ_DISCRETE_INPUT;
if(fc == 3) FC = MB_FC_READ_REGISTERS;
if(fc == 4) FC = MB_FC_READ_INPUT_REGISTER;
if(fc == 5) FC = MB_FC_WRITE_COIL;
if(fc == 6) FC = MB_FC_WRITE_REGISTER;
if(fc == 15) FC = MB_FC_WRITE_MULTIPLE_COILS;
if(fc == 16) FC = MB_FC_WRITE_MULTIPLE_REGISTERS;
return FC;
}
word MgsModbus::GetDataLen()
{
return MbDataLen;
}
boolean MgsModbus::GetBit(word Number)
{
int ArrayPos = Number / 16;
int BitPos = Number - ArrayPos * 16;
boolean Tmp = bitRead(MbData[ArrayPos],BitPos);
return Tmp;
}
boolean MgsModbus::SetBit(word Number,boolean Data)
{
int ArrayPos = Number / 16;
int BitPos = Number - ArrayPos * 16;
boolean Overrun = ArrayPos > MbDataLen * 16; // check for data overrun
if (!Overrun){
bitWrite(MbData[ArrayPos],BitPos,Data);
}
return Overrun;
}
Code: Select all
/*
MgsModbus.h - an Arduino library for a Modbus TCP master and slave.
V-0.1.1 Copyright (C) 2013 Marco Gerritse
written and tested with Arduino 1.0
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
For this library the following library is used as start point:
[1] Mudbus.h - an Arduino library for a Modbus TCP slave.
Copyright (C) 2011 Dee Wykoff
[2] Function codes 15 & 16 by Martin Pettersson
The following references are used to write this library:
[3] Open Modbus/Tcp specification, Release 1.0, 29 March 1999
By Andy Swales, Schneider Electric
[4] Modbus application protocol specification V1.1b3, 26 april 202
From http:/www.modbus.org
External software used for testing:
[5] modpoll - www.modbusdriver.com/modpoll.html
[6] ananas - www.tuomio.fi/ananas
[7] mod_rssim - www.plcsimulator.org
[8] modbus master - www.cableone.net/mblansett/
This library use a single block of memory for all modbus data (mbData[] array). The
same data can be reached via several modbus functions, either via a 16 bit access
or via an access bit. The length of MbData must at least 1.
For the master the following modbus functions are implemented: 1, 2, 3, 4, 5, 6, 15, 16
For the slave the following modbus functions are implemented: 1, 2, 3, 4, 5, 6, 15, 16
The internal and external addresses are 0 (zero) based
V-0.1.1 2013-06-02
bugfix
V-0.1.0 2013-03-02
initinal version
*/
#include "Arduino.h"
#include <SPI.h>
#include <Ethernet2.h>
#ifndef MgsModbus_h
#define MgsModbus_h
#define MbDataLen 30 // length of the MdData array
#define MB_PORT 502
enum MB_FC {
MB_FC_NONE = 0,
MB_FC_READ_COILS = 1,
MB_FC_READ_DISCRETE_INPUT = 2,
MB_FC_READ_REGISTERS = 3,
MB_FC_READ_INPUT_REGISTER = 4,
MB_FC_WRITE_COIL = 5,
MB_FC_WRITE_REGISTER = 6,
MB_FC_WRITE_MULTIPLE_COILS = 15,
MB_FC_WRITE_MULTIPLE_REGISTERS = 16
};
class MgsModbus
{
public:
// general
MgsModbus();
word MbData[MbDataLen]; // memory block that holds all the modbus user data
boolean GetBit(word Number);
boolean SetBit(word Number,boolean Data); // returns true when the number is in the MbData range
// modbus master
void Req(MB_FC FC, word Ref, word Count, word Pos);
void MbmRun();
IPAddress remSlaveIP;
// modbus slave
void MbsRun();
word GetDataLen();
private:
// general
MB_FC SetFC(int fc);
// modbus master
uint8_t MbmByteArray[260]; // send and recieve buffer
MB_FC MbmFC;
int MbmCounter;
void MbmProcess();
word MbmPos;
word MbmBitCount;
//modbus slave
uint8_t MbsByteArray[260]; // send and recieve buffer
MB_FC MbsFC;
};
#endif
Re: Arduino to Modbus RTU Master
Danke Dir Ingo! Leider konnte ich das nicht auf ModBus RTU editieren..
Habe jetzt eine andere Möglichkeit genutzt, bei der ich nicht über ein virtuelles RevPi Modbus RTU Master Modul gehe, sondern die Daten direkt über mein python-Script abfrage.
In drei Zeilen Code bekommt man so direkt die Werte, wie man sie vom Arduino sendet, ohne Register-Kampf.
Genutzt habe ich hierfür die pymodbus Library, welche direkt auf die ttyUSB0 Schnittstelle (oder eine beliebig andere) zugreift.
Habe jetzt eine andere Möglichkeit genutzt, bei der ich nicht über ein virtuelles RevPi Modbus RTU Master Modul gehe, sondern die Daten direkt über mein python-Script abfrage.
In drei Zeilen Code bekommt man so direkt die Werte, wie man sie vom Arduino sendet, ohne Register-Kampf.
Genutzt habe ich hierfür die pymodbus Library, welche direkt auf die ttyUSB0 Schnittstelle (oder eine beliebig andere) zugreift.
Re: Arduino to Modbus RTU Master
hi,
schön zu höhren das es doch noch auf einen anderen Weg Funktioniert. Leider nutze ich bei meiner Config kein Python da alle Modbusgeräte bei mir über TCP/IP laufen. Ich weiss, was es für ein Kampf mit den Registern ist, " wenn sie dann mal nicht ein eigenleben entwickeln würden". . Aber das ist wieder eine andere Geschichte.
Lösung ist in Arbeit.
schön zu höhren das es doch noch auf einen anderen Weg Funktioniert. Leider nutze ich bei meiner Config kein Python da alle Modbusgeräte bei mir über TCP/IP laufen. Ich weiss, was es für ein Kampf mit den Registern ist, " wenn sie dann mal nicht ein eigenleben entwickeln würden". . Aber das ist wieder eine andere Geschichte.
Lösung ist in Arbeit.
Re: Arduino to Modbus RTU Master
Hi Ruwen, das hört sich ja gut an, dass Du das Problem mit Python beheben konntest. Ingo, ich habe Deinen Post bereits gesehen und wir werden das analysieren.