Mehrere Register über Modebus aulesen

Hallo,
ich möchte mit der Bibliothek Modebusmaster.h mehrere Register auslesen,
die aber nicht hintereinander liegen.
Die Werte dann in Variablen zur Weiterverarbeitung transferieren.
Muß ich da zwischendurch den ResponseBuffer löschen?
Der folgende Code funktioniert schon mal,
Register auslesen und in eine Variable übernehmen.

#include <ModbusMaster.h>
#include <SoftwareSerial.h>

uint16_t Batterieniedrig = 0;

ModbusMaster node;
SoftwareSerial RS485Serial(5, 6); // RX, TX

void preTransmission() {
  digitalWrite(A2, 1);
}

void postTransmission() {
  digitalWrite(A2, 0);
}

void setup() {
  pinMode(A2, OUTPUT);
  digitalWrite(A2, 0);

  Serial.begin(9600);
  RS485Serial.begin(9600);

  node.begin(1, RS485Serial);
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t j, result;
  uint16_t data[10];

  result = node.readHoldingRegisters(117, 1);
  if (result == node.ku8MBSuccess)
  {
    Serial.println("Batterie niedrig: ");
    Batterieniedrig = (node.getResponseBuffer(0));
    Serial.println(Batterieniedrig);
  }

  delay(3000);
}

kommt drauf an.
Wenn Du den buffer "am Stück" verarbeiten willst: ja
Wenn Du sequentiell weisst, was Du brauchs und nur die entsprechenden Bytes benutzt: nein.
Empfehlung: ja

Das ist nicht notwendig und wird im Beispiel PhoenixContact_nanoLC.pde auch nicht gemacht. Wenn das Lesen des Registers erfolgreich war, ist der response Puffer gemäß Slave befüllt. War es nicht erfolgreich, wären auch die Nullwerte aus einem zuvor gemachten Löschen ebenso falsch wie "die alten Werte".

Was ich dir empfehle ist, dass du im Fehlerfall den Wert der Variable result in HEX ausgibst falls es zu einem Fehler kommt (also den if um ein else erweitern).

Meine Beobachtung:
Wenn der Buffer ausgelesen wird, wird er anschließend gelöscht.

Ich schaffe es nicht, mehrere Adressen einzulesen und anschließend
einzeln auszulesen. Es kommt dann immer der erste Wert,
alle Folgenden sind dann Null.

Funktionieren tut es, den Code für jedes Register komplett zu wiederhohlen,
was aber nicht gerade elegant ist.

void loop()
{
  uint8_t j, result;
  uint16_t data[10];

  result = node.readHoldingRegisters(117, 1);
  if (result == node.ku8MBSuccess)
  {
    Serial.println("Batterie niedrig: ");
    Batterieniedrig = (node.getResponseBuffer(0));
    Serial.println(Batterieniedrig);
  }
  
    result = node.readHoldingRegisters(676, 1);
  if (result == node.ku8MBSuccess)
  {
    Serial.println("PV1: ");
    Serial.println(node.getResponseBuffer(0));
  }

das ist vermutlich ein anderes Problem. Du hältst die Zeitabstände nicht ein und liest zu schnell das nächste Register.

dirty:
mach ein delay(1000) vor dem zweiten readHoldingRegister.

hier hab ich ein Beispiel wie ich es meist mache.
Das postTransmission setzt eine Zeitmarke wann die Client->Server Kommunikation geendet hat
Das preTransmission prüft, ob genug zeit vergangen ist (und blockiert falls nicht erreicht).
Ist zwar auch blockierend, aber nur wenn es notwendig ist.

P.S.: Die "Wartezeit" soll abhängig von der Schnittstellengeschwindigkeit sein. Da gibts Vorgaben in der Modbus Spezifikation

Summary
*******************************************************

  Modbus Client Example 0x50 (Modbus Master)
  Using several Modbus Function Codes

  This Modbus Client
  - reads from Modbus Servers periodically and switches a local pin accordingly
  - sends a new value to Modbus Servers when a button is pressed

  hardware
  - a LED
  - 4 push buttons
  - a MAX485-TTL adapter

  by noiasca
  2022-01-02

 *******************************************************/

#include <ModbusMaster.h>  // Modbus Master 2.0.0 by Doc Walker - install with Library Manager
// client and servers must know the same registers
enum
{
  // just add or remove registers and your good to go
  // The first register starts at address 0 which is for Function 3 Register 40001
  regButton,         // read a button
  regADC0,           // analog port 0
  regLED,            // set a LED
  regServo,          // set a Servo
  HOLDING_REGS_SIZE  // leave this one
};

// client specific
constexpr byte ledPinA = 13;
constexpr byte ledPinB = 16;
constexpr byte buttonPinA = A0;
constexpr byte buttonPinB = A1;
constexpr byte buttonServoPinA = A2;
constexpr byte buttonServoPinB = A3;
constexpr byte modbus_enable_pin = 5;            // The GPIO used to control the MAX485 TX pin. Set to 255 if you are not using RS485 or a selfsensing adapter
constexpr uint32_t modbus_baud = 19200;            // use slow speeds with SoftSerial
//constexpr byte rxPin = 2;                        // for Softserial
//constexpr byte txPin = 3;
uint32_t previousTx = 0;
uint16_t restTx = 15000; // rest time between transmissions - microseconds

ModbusMaster serverA;  // instantiate ModbusMaster object - slave - node

void preTransmission()
{
  while (micros() - previousTx < restTx)
  {
    yield(); // wait some time and do background tasks.
  }
  Serial.println();
  digitalWrite(modbus_enable_pin, HIGH);
}

void postTransmission()
{
  previousTx = micros();   // remember last timestamp
  digitalWrite(modbus_enable_pin, LOW);
}

struct Input {
  const byte buttonPin;
  uint32_t previousMillis;
  byte lastButtonState;    // stores the true readed state
};

Input input[] {
  {buttonPinA, 0, HIGH},
  {buttonPinB, 0, HIGH},
  {buttonServoPinA, 0, HIGH},
  {buttonServoPinB, 0, HIGH},
};

/* *******************************************************
   Serial Interface
 * **************************************************** */
// if you don't have enough HW Serial (i.e. on an UNO)
// you are forced to use SoftwareSerial or AltSoftSerial
//include <SoftwareSerial.h>
//SoftwareSerial mySerial(rxPin, txPin);

byte stateChangeDetection(byte i)  // returns true if change was changed
{
  byte buttonState = digitalRead(input[i].buttonPin);
  byte result = false;
  // compare the buttonState to its previous state
  if (buttonState != input[i].lastButtonState) {
    result = true;
    Serial.print(input[i].buttonPin); Serial.print(F(" is "));
    if (buttonState == LOW) {
      Serial.println(F("on"));
    } else {
      Serial.println(F("off"));
    }
    delay(50);// Delay a little bit to avoid bouncing
  }
  input[i].lastButtonState = buttonState; // save the current state as the last state, for next time through the loop
  return result;
}

void handleButton()
{
  int result = 0;
  // if the button has changed, we send the (inversed) last state to the server
  if (stateChangeDetection(0)) {
    result = serverA.writeSingleCoil(regLED, !input[0].lastButtonState);
    if (result != serverA.ku8MBSuccess)
    {
      Serial.print(F("E: ServerA no success register ")); Serial.println(regLED);
    }
  }
  // send a random value to the servo register
  if (stateChangeDetection(2) && input[2].lastButtonState == LOW) {
    result = serverA.writeSingleRegister(regServo, random(256));
    if (result != serverA.ku8MBSuccess)
    {
      Serial.print(F("E: ServerA no success register ")); Serial.println(regServo);
    }
  }
}

void requestData()
{
  static uint32_t previousMillis = 0;
  uint32_t currentMillis = millis();
  if (currentMillis - previousMillis > 5000)  // set the interval in ms
  {
    previousMillis = currentMillis;
    uint16_t reg = regButton;
    int result;
    // slave: read (6) 16-bit registers starting at register 2 to RX buffer
    //result = serverA.readHoldingRegisters(regButton, 2);
    result = serverA.readHoldingRegisters(0, 1);
    if (result == serverA.ku8MBSuccess) // do something if read is successfull
    {
      uint16_t value = serverA.getResponseBuffer(0);
      Serial.println(value);
      if (value) digitalWrite(ledPinA, HIGH); else digitalWrite(ledPinA, LOW);
      value = serverA.getResponseBuffer(1); // we requested two registers, so we can print the ADC0 value also:
      Serial.print(F("ADC0=")); Serial.println(value);
    }
    else
    {
      Serial.print(F(" ServerA no success register ")); Serial.print(regButton); Serial.print(F(" result=")); Serial.println(result, HEX);
    }
 
    reg = 1002;
    result = serverA.readHoldingRegisters(reg, 2);
    if (result == serverA.ku8MBSuccess) // do something with data if read is successful
    {
      uint16_t value = serverA.getResponseBuffer(0);
      Serial.println(value);
    }
    else
    {
      Serial.print(F("E ServerA no success add register ")); Serial.print(reg); Serial.print(F(" result=")); Serial.println(result, HEX);
    }

    reg = 1002;
    result = serverA.writeSingleRegister(reg, random(256));
    if (result == serverA.ku8MBSuccess) // do something with data if read is successful
    {
      ;
    }
    else
    {
      Serial.print(F("E ServerA no success add register ")); Serial.print(reg); Serial.print(F(" result=")); Serial.println(result, HEX);
    } 

    reg = 2000;
    byte noOfValues = 4;
    result = serverA.readInputRegisters(reg, noOfValues); //FC4
    if (result == serverA.ku8MBSuccess) // do something with data if read is successful
    {
      for (byte i = 0; i < noOfValues; i++)
      {
        Serial.print(serverA.getResponseBuffer(i));
        Serial.print("\t");
      }
      Serial.println();
    }
    else
    {
      Serial.print(F("E ServerA no input register ")); Serial.print(reg); Serial.print(F(" result=")); Serial.println(result, HEX);
    }
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Modbus Client Example 0x50"));
  for (auto & i : input) pinMode(i.buttonPin, INPUT_PULLUP);
  // use Serial (port 0); initialize Modbus communication baud rate
  Serial3.begin(modbus_baud);
  serverA.begin(2, Serial3);   // communicate with Modbus server ID 2 over the given Serial interface

  // Init enable pins for modbus master library
  pinMode(modbus_enable_pin, OUTPUT);
  digitalWrite(modbus_enable_pin, LOW);
  // Callbacks allow us to configure the RS485 transceiver correctly
  serverA.preTransmission(preTransmission);
  serverA.postTransmission(postTransmission);

  // init local hardware
  pinMode(ledPinA, OUTPUT);
  pinMode(ledPinB, OUTPUT);
}

void loop()
{
  handleButton();
  requestData();
}

Die "quiet time" ist definiert als 3,5t, mit t=Übertragungszeit eines Zeichens, muss aber mindestens 1750μs betragen.

... der fixe Werte von 1750 gilt als Empfehlung ab 19200 baud.

Bei langsameren Geschwindigkeiten kann das schon mehr sein. Der TO verwendet 9600 baud, dann wäre T3,5 = 4000µs.

Abhängig vom Anwendungsfall könnte man auch die Register zeitversetzt abfragen, also statt dem einen delay 3000, alle 1500ms das eine oder das andere Register abfragen, dann gibt es das Problem auch nicht.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.