arduino-libraries / ArduinoModbus

250 stars 118 forks source link

Does Not Work With Portenta H7 MachineControl #78

Open Twinkle299 opened 2 years ago

Twinkle299 commented 2 years ago

Unfortunately ModbusRTUServer does not seem to work with the portenta machinecontrol, using the kitchen sink examples and connecting 2 machinecontrol boards together the client connection times out on every read and write request. Additionally using the machinecontrol as a server and a third party device as a client the result is the same.

I have successfully used ModbusRTUClient on the machinecontrol with a third party device.

Can anyone else confirm this or give me pointers so that i can attempt to debug the issue further myself ?

facchinm commented 2 years ago

@manchoz can you take a look at this?

manchoz commented 2 years ago

@Twinkle299 would you, please, share the relevant code snippets from the sketches or a minimal sketch to reproduce the issue? Thank you.

Twinkle299 commented 2 years ago

Hi @manchoz, It can be tested by using the KitchenSink Server&Client sketches with a couple of small changes.

SERVER:

#include <Arduino_MachineControl.h>
#include <ArduinoModbus.h>
#include <ArduinoRS485.h>

using namespace machinecontrol;

const int numCoils = 10;
const int numDiscreteInputs = 10;
const int numHoldingRegisters = 10;
const int numInputRegisters = 10;

void setup() {
  Serial.begin(9600);
  while (!Serial);

  Serial.println("Modbus RTU Server Kitchen Sink");
  comm_protocols.init();
  comm_protocols.rs485Enable(true);
  comm_protocols.rs485FullDuplex(false);
  //comm_protocols.rs485.setDelays(50,50);
  //comm_protocols.rs485.begin(9600, 0, 500);
  // start the Modbus RTU server, with (slave) id 42
  if (!ModbusRTUServer.begin(comm_protocols.rs485, 42, 9600, SERIAL_8N1)) {
    Serial.println("Failed to start Modbus RTU Server!");
    while (1);
  }

  // configure coils at address 0x00
  ModbusRTUServer.configureCoils(0x00, numCoils);

  // configure discrete inputs at address 0x00
  ModbusRTUServer.configureDiscreteInputs(0x00, numDiscreteInputs);

  // configure holding registers at address 0x00
  ModbusRTUServer.configureHoldingRegisters(0x00, numHoldingRegisters);

  // configure input registers at address 0x00
  ModbusRTUServer.configureInputRegisters(0x00, numInputRegisters);
}

void loop() {
  // poll for Modbus RTU requests
  ModbusRTUServer.poll();

  // map the coil values to the discrete input values
  for (int i = 0; i < numCoils; i++) {
    int coilValue = ModbusRTUServer.coilRead(i);

    ModbusRTUServer.discreteInputWrite(i, coilValue);
  }

  // map the holding register values to the input register values
  for (int i = 0; i < numHoldingRegisters; i++) {
    long holdingRegisterValue = ModbusRTUServer.holdingRegisterRead(i);

    ModbusRTUServer.inputRegisterWrite(i, holdingRegisterValue);
  }
}

CLIENT:

#include <Arduino_MachineControl.h>
#include <ArduinoModbus.h>
#include <ArduinoRS485.h>

using namespace machinecontrol;

int counter = 0;

void setup() {
  Serial.begin(9600);
  while (!Serial);

  Serial.println("Modbus RTU Client Kitchen Sink");
  comm_protocols.init();  
  comm_protocols.rs485Enable(true);
  comm_protocols.rs485FullDuplex(false);
  // comm_protocols.rs485.setTimeout(500);
  // comm_protocols.rs485.setDelays(50,50);
  // start the Modbus RTU client
  if (!ModbusRTUClient.begin(comm_protocols.rs485, 9600, SERIAL_8N1)) {
    Serial.println("Failed to start Modbus RTU Client!");
    while (1);
  }
}

void writeCoilValues() {
  // set the coils to 1 when counter is odd
  byte coilValue = ((counter % 2) == 0) ? 0x00 : 0x01;

  Serial.print("Writing Coil values ... ");

  // write 10 Coil values to (slave) id 42, address 0x00
  ModbusRTUClient.beginTransmission(42, COILS, 0x00, 10);
  for (int i = 0; i < 10; i++) {
    ModbusRTUClient.write(coilValue);
  }
  if (!ModbusRTUClient.endTransmission()) {
    Serial.print("failed! ");
    Serial.println(ModbusRTUClient.lastError());
  } else {
    Serial.println("success");
  }

  // Alternatively, to write a single Coil value use:
  // ModbusRTUClient.coilWrite(...)
}

void readCoilValues() {
  Serial.print("Reading Coil values ... ");

  // read 10 Coil values from (slave) id 42, address 0x00
  if (!ModbusRTUClient.requestFrom(42, COILS, 0x00, 10)) {
    Serial.print("failed! ");
    Serial.println(ModbusRTUClient.lastError());
  } else {
    Serial.println("success");

    while (ModbusRTUClient.available()) {
      Serial.print(ModbusRTUClient.read());
      Serial.print(' ');
    }
    Serial.println();
  }

  // Alternatively, to read a single Coil value use:
  // ModbusRTUClient.coilRead(...)
}

void readDiscreteInputValues() {
  Serial.print("Reading Discrete Input values ... ");

  // read 10 Discrete Input values from (slave) id 42, address 0x00
  if (!ModbusRTUClient.requestFrom(42, DISCRETE_INPUTS, 0x00, 10)) {
    Serial.print("failed! ");
    Serial.println(ModbusRTUClient.lastError());
  } else {
    Serial.println("success");

    while (ModbusRTUClient.available()) {
      Serial.print(ModbusRTUClient.read());
      Serial.print(' ');
    }
    Serial.println();
  }

  // Alternatively, to read a single Discrete Input value use:
  // ModbusRTUClient.discreteInputRead(...)
}

void writeHoldingRegisterValues() {
  // set the Holding Register values to counter

  Serial.print("Writing Holding Registers values ... ");

  // write 10 coil values to (slave) id 42, address 0x00
  ModbusRTUClient.beginTransmission(42, HOLDING_REGISTERS, 0x00, 10);
  for (int i = 0; i < 10; i++) {
    ModbusRTUClient.write(counter);
  }
  if (!ModbusRTUClient.endTransmission()) {
    Serial.print("failed! ");
    Serial.println(ModbusRTUClient.lastError());
  } else {
    Serial.println("success");
  }

  // Alternatively, to write a single Holding Register value use:
  // ModbusRTUClient.holdingRegisterWrite(...)
}

void readHoldingRegisterValues() {
  Serial.print("Reading Input Register values ... ");

  // read 10 Input Register values from (slave) id 42, address 0x00
  if (!ModbusRTUClient.requestFrom(42, HOLDING_REGISTERS, 0x00, 10)) {
    Serial.print("failed! ");
    Serial.println(ModbusRTUClient.lastError());
  } else {
    Serial.println("success");

    while (ModbusRTUClient.available()) {
      Serial.print(ModbusRTUClient.read());
      Serial.print(' ');
    }
    Serial.println();
  }

  // Alternatively, to read a single Holding Register value use:
  // ModbusRTUClient.holdingRegisterRead(...)
}

void readInputRegisterValues() {
  Serial.print("Reading input register values ... ");

  // read 10 discrete input values from (slave) id 42,
  if (!ModbusRTUClient.requestFrom(42, INPUT_REGISTERS, 0x00, 10)) {
    Serial.print("failed! ");
    Serial.println(ModbusRTUClient.lastError());
  } else {
    Serial.println("success");

    while (ModbusRTUClient.available()) {
      Serial.print(ModbusRTUClient.read());
      Serial.print(' ');
    }
    Serial.println();
  }

  // Alternatively, to read a single Input Register value use:
  // ModbusRTUClient.inputRegisterRead(...)
}

void loop() {
  writeCoilValues();

  readCoilValues();

  readDiscreteInputValues();

  writeHoldingRegisterValues();

  readHoldingRegisterValues();

  readInputRegisterValues();

  counter++;

  delay(5000);
  Serial.println();
}
Twinkle299 commented 2 years ago

Just as a sense check and to prove to myself that I wasn't doing something silly I setup the same test with a MKR NB1500 and an MKR RS485 Shield. As expected all tests worked first try.

Dedalo17 commented 2 years ago

Hi @Twinkle299, I´m struggling with this too, I have a RS485 monitor and the results are well quite insteresting, the message is sended but with incorrect crc last byte, first byte of CRC is ok, also the message is not sended when ordered, it seems to catch some of the message in a buffer and then send everything together.

My monitor reads 01 F6 12 03 00 4B 00 01 F6 12 03 00 4B 00 01 F6

When the correct message should be 12 03 00 4B 00 01 F6 BF when reading a single Holding register on addres 0x4B and slave 0x12.

Did you get this working?

manchoz commented 2 years ago

Hi @Dedalo17 and @Twinkle299, Please, try configuring the preamble and portable time for transmitting the Modbus RTU word. You can do this by adding the following call to the setup()

    // Set RS485 preamble and postamble word delays. YMMV
    comm_protocols.rs485.setDelays(0, 85);

You need to experiment with what numbers are right for your specific scenario.

Dedalo17 commented 2 years ago

Thanks, it´s working know.

I read the Modbus.c file and figured out that these delays are in micros, so (100,1000) is working fine now