Arduino to Modbus RTU Master

Rund um die Software von Revolution Pi
Post Reply
ruwen
Posts: 7
Joined: 15 Mar 2020, 22:56

Arduino to Modbus RTU Master

Post by ruwen »

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:
arduino_config.png
arduino_config.png (37.12 KiB) Viewed 13349 times
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:
ModbusPoll_config1.png
ModbusPoll_config1.png (17.38 KiB) Viewed 13349 times
ModbusPoll_config2.png
ModbusPoll_config2.png (14.23 KiB) Viewed 13349 times
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
User avatar
dirk
KUNBUS
Posts: 2174
Joined: 15 Dec 2016, 13:19

Re: Arduino to Modbus RTU Master

Post by dirk »

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?
ruwen
Posts: 7
Joined: 15 Mar 2020, 22:56

Re: Arduino to Modbus RTU Master

Post by ruwen »

Hallo Dirk,

hier die Config und die .ino Datei
modbus.zip
(7.56 KiB) Downloaded 727 times
User avatar
Ingo
Posts: 267
Joined: 10 Nov 2016, 21:56
Location: Luth.Wittenberg

Re: Arduino to Modbus RTU Master

Post by Ingo »

Hallo,

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) {
ist nur ein auszug aus meinem Code
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;
}
und MgsModbus.h

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
ruwen
Posts: 7
Joined: 15 Mar 2020, 22:56

Re: Arduino to Modbus RTU Master

Post by ruwen »

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.
User avatar
Ingo
Posts: 267
Joined: 10 Nov 2016, 21:56
Location: Luth.Wittenberg

Re: Arduino to Modbus RTU Master

Post by Ingo »

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.
User avatar
dirk
KUNBUS
Posts: 2174
Joined: 15 Dec 2016, 13:19

Re: Arduino to Modbus RTU Master

Post by dirk »

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.
Post Reply