CMB27 / ModbusRTUSlave

This is an Arduino library that implements the slave/server logic of the Modbus RTU protocol.
MIT License
71 stars 21 forks source link

Library used on Arduino MEGA 2560 - no reply from the slave #30

Closed DocAlex closed 10 months ago

DocAlex commented 10 months ago

Hello,

I try to use the library on an arduino Mega 2560 @ 16MHz.

Here ist the example file modified for my application.

Changes:

#include "Arduino.h"
/*
  ModbusRTUSlaveExample

  This example demonstrates how to setup and use the ModbusRTUSlave library.
  It is intended to be used with a second board running ModbusRTUMasterExample from the ModbusRTUMaster library.

  Circuit:
  - A pushbutton switch from pin 2 to GND
  - A pushbutton switch from pin 3 to GND
  - A LED from pin 5 to GND with a 330 ohm series resistor
  - A LED from pin 6 to GND with a 330 ohm series resistor
  - A LED from pin 7 to GND with a 330 ohm series resistor
  - A LED from pin 8 to GND with a 330 ohm series resistor
  - The center pin of a potentiometer to pin A0, and the outside pins of the potentiometer to 5V and GND
  - The center pin of a potentiometer to pin A0, and the outside pins of the potentiometer to 5V and GND

  !!! If your board's logic voltage is 3.3V, use 3.3V instead of 5V; if in doubt use the IOREF pin !!!

  - Pin 10 to pin 11 of the master/client board
  - Pin 11 to pin 10 of the master/client board
  - GND to GND of the master/client board

  A schematic and illustration of the circuit is in the extras folder of the ModbusRTUSlave library.

  - Pin 13 is set up as the driver enable pin. This pin will be HIGH whenever the board is transmitting.

  Created: 2023-07-22
  By: C. M. Bulliner
  Modified: 2023-07-29
  By: C. M. Bulliner

*/

#include <SoftwareSerial.h>
#include <ModbusRTUSlave.h>

const byte ledPins[4] = {47, 46, 45, 43};
const byte buttonPins[2] = {15, 14};
const byte potPins[2] = {A0, A1};

const uint8_t rxPin = 0;
const uint8_t txPin = 1;
const uint8_t dePin = 2;

SoftwareSerial mySerial(rxPin, txPin);
ModbusRTUSlave modbus(mySerial, dePin); // serial port, driver enable pin for rs-485 (optional)

bool coils[2];
bool discreteInputs[2];
uint16_t holdingRegisters[2];
uint16_t inputRegisters[2];

void setup() {
  pinMode(ledPins[0], OUTPUT);
  pinMode(ledPins[1], OUTPUT);
  pinMode(ledPins[2], OUTPUT);
  pinMode(ledPins[3], OUTPUT);
  pinMode(buttonPins[0], INPUT_PULLUP);
  pinMode(buttonPins[1], INPUT_PULLUP);
  pinMode(potPins[0], INPUT);
  pinMode(potPins[1], INPUT);

  modbus.configureCoils(coils, 2);                       // bool array of coil values, number of coils
  modbus.configureDiscreteInputs(discreteInputs, 2);     // bool array of discrete input values, number of discrete inputs
  modbus.configureHoldingRegisters(holdingRegisters, 2); // unsigned 16 bit integer array of holding register values, number of holding registers
  modbus.configureInputRegisters(inputRegisters, 2);     // unsigned 16 bit integer array of input register values, number of input registers
  modbus.begin(1, 9600);                                // slave id, baud rate, config (optional)
}

void loop() {
  discreteInputs[0] = !digitalRead(buttonPins[0]);
  discreteInputs[1] = !digitalRead(buttonPins[1]);
  inputRegisters[0] = map(analogRead(potPins[0]), 0, 1023, 0, 255);
  inputRegisters[1] = map(analogRead(potPins[1]), 0, 1023, 0, 255);

  modbus.poll();

  digitalWrite(ledPins[0], coils[0]);
  digitalWrite(ledPins[1], coils[1]);
  analogWrite(ledPins[2], holdingRegisters[0]);
  analogWrite(ledPins[3], holdingRegisters[1]);
}

From a Modbus terminal program I generate a modbus message to write a single coil (0) to value 1.

2023-12-01 19_46_01-New Issue · CMB27_ModbusRTUSlave – Mozilla Firefox

This is sent from the PC to the Arduino 2560 via a USB --> RS485 adapter.

On the Board the RS485 is decoded to TTL using a MAX487.

At the output of this converter the signal is fed directly into (physical) PIN 2 = Arduino PIN 0.

Here is a Scope measurement directly on the processor pin:

DS1Z_QuickPrint3

What is the result:

2023-12-01 20_03_14-MAX487 Datasheet and Product Info _ Analog Devices – Mozilla Firefox

DS1Z_QuickPrint4

Therefore I conclude, that the library is initializing correctly. When I select another (wrong) pin in the sketch, the dirctional pin stays on high (there is a 10k pullup resistor).

But then ... nothing happens. No response is being generated on TX0, The direction is not changed on the directional pin. The board is not reacting at all.

Questions:

Regards

Alexander

CMB27 commented 10 months ago

I tried out your code. I can confirm it doesn't work. SoftwareSerial needs pin change interrupts on the pins it operates on. Pins 0 and 1 do not have pin change interrupts on the Arduino Mega 2560.

There is an easy solution. Pins 0 and 1 have a HardwareSerial port on them. The following code shows how to implement the HardwareSerial port with the ModbusRTUMaster library.

#include <ModbusRTUSlave.h>

const byte ledPins[4] = {47, 46, 45, 43};
const byte buttonPins[2] = {15, 14};
const byte potPins[2] = {A0, A1};

const uint8_t dePin = 2;

ModbusRTUSlave modbus(Serial, dePin); // serial port, driver enable pin for rs-485 (optional)

bool coils[2];
bool discreteInputs[2];
uint16_t holdingRegisters[2];
uint16_t inputRegisters[2];

void setup() {
  pinMode(ledPins[0], OUTPUT);
  pinMode(ledPins[1], OUTPUT);
  pinMode(ledPins[2], OUTPUT);
  pinMode(ledPins[3], OUTPUT);
  pinMode(buttonPins[0], INPUT_PULLUP);
  pinMode(buttonPins[1], INPUT_PULLUP);
  pinMode(potPins[0], INPUT);
  pinMode(potPins[1], INPUT);

  modbus.configureCoils(coils, 2);                       // bool array of coil values, number of coils
  modbus.configureDiscreteInputs(discreteInputs, 2);     // bool array of discrete input values, number of discrete inputs
  modbus.configureHoldingRegisters(holdingRegisters, 2); // unsigned 16 bit integer array of holding register values, number of holding registers
  modbus.configureInputRegisters(inputRegisters, 2);     // unsigned 16 bit integer array of input register values, number of input registers
  modbus.begin(1, 9600);                                 // slave id, baud rate, config (optional)
}

void loop() {
  discreteInputs[0] = !digitalRead(buttonPins[0]);
  discreteInputs[1] = !digitalRead(buttonPins[1]);
  inputRegisters[0] = map(analogRead(potPins[0]), 0, 1023, 0, 255);
  inputRegisters[1] = map(analogRead(potPins[1]), 0, 1023, 0, 255);

  modbus.poll();

  digitalWrite(ledPins[0], coils[0]);
  digitalWrite(ledPins[1], coils[1]);
  analogWrite(ledPins[2], holdingRegisters[0]);
  analogWrite(ledPins[3], holdingRegisters[1]);
}

Just a note, the last two pins of ledPins should be PWM pins. Pin 45 is PWM capable, but pin 43 is not. This won't have any damaging effects, but you won't have variable control of the brightness of an LED attached to pin 43; it will just be on or off.

I hope this helps,

Chris Bulliner

DocAlex commented 10 months ago

Thank you very much for your help. It works now.

One comment: When I use the Library with a hardware serial, within the library the soft serial library is linked to the program and the object is initialized anyway. This causes unnecessary use of both RAM and FLASH. Maybe in a future version this could be avoided somehow...

Greetings

Alexander

CMB27 commented 10 months ago

SoftwareSerial is #included in the library so that the library can use a SoftwareSerial object/instance. Most of the SoftwareSerial code doesn't actually get compiled unless an instance of it is constructed: e.g. SoftwareSerial mySerial(rxPin, txPin). The library itself does not create an instance of SoftwareSerial, so it does not unnecessarily use up much RAM or flash.

You can test this. Take a program, Blink for example, compile it, and check how much flash and ram it uses. Compiling for the Arduino UNO, I get 924 bytes (2%) of program storage space used and 9 bytes (0%) of dynamic memory used.

Then add #include <SoftwareSerial.h> to the top of the program and compile again. I get 944 bytes (2%) of program storage space used and 11 bytes (0%) of dynamic memory used. This is a little bit more, but even on an AVR, 10 bytes of flash storage and 2 bytes of ram is pretty insignificant.

However if I add SoftwareSerial mySerial(10, 11); before void setup() {, I get 2052 bytes (6%) of program storage space used and 126 bytes (6%) of dynamic memory used.

Using HardwareSerial, uses up some resources as well. Take stock Blink and add Serial.begin(9600); to the `void setup() function and compile. I get 1918 bytes (5%) of program storage space used and 184 bytes (8%) of dynamic memory used.

In Summary:

Program Description Flash Usage RAM Usage
Stock Blink 924 bytes (2%) 9 bytes (0%)
Blink with SoftwareSerial included 944 bytes (2%) 11 bytes (0%)
Blink with an Instance of SoftwareSerial 2052 bytes (6%) 126 bytes (6%)
Blink with HardwareSerial 1918 bytes (5%) 184 bytes (8%)

I hope this answers your concerns,

Chris Bulliner