Help with ModBus RTU Master-Slave: SimpleModbus [SOLVED]

Thanks for your reply, Paul!
Think i've already all that what you wrote.
I have just shortened my code with only the basic things.
Doesn't work at all. SPI will only read once at startup. Also if I call the modbys within the "delay".

#include <SimpleModbusSlave.h>
#include <SPI.h>

enum 
{     
  WMOD0_TEMP_EXTR,                  
  HOLDING_REGS_SIZE 
};
unsigned int holdingRegs[HOLDING_REGS_SIZE]; // function 3 and 16 register array           
unsigned long prevMillis = 0;        // will store last time LED was updated

int CSEXTR= 9; // D09: Chipselect MAX6675 Extruder
const long chktemp = 100; 
/*
============
=  I N I T =
============
*/
void setup() {
  // Pins deklarieren                    
  pinMode(CSEXTR, OUTPUT);       // CS-Pin Extruder als Output
  digitalWrite(CSEXTR, HIGH);    // CS-Pin Extruder auf High
  
  // Init Modbus
  modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 2, HOLDING_REGS_SIZE, holdingRegs);
  modbus_update_comms(9600, SERIAL_8N2, 1);
  
  // Init SPI  
  SPI.setClockDivider(SPI_CLOCK_DIV2);  // wären 8 MHz SPI Takt
  SPI.setDataMode(SPI_MODE0);           // DataMode 0
  SPI.begin();  
}
/*
============
=  M A I N =
============
*/
void loop() {
  modbus_update();
  
  unsigned long actMillis = millis();
  if (actMillis - prevMillis >= chktemp) {
      prevMillis = actMillis;                             
      digitalWrite(CSEXTR, LOW);
      delayMicroseconds(0);        // zusätzliche Wartezeit, 100ns erforderlich
      uint16_t reading = SPI.transfer16(0);
      digitalWrite(CSEXTR, HIGH);
      reading = (reading >>3)*25;  // the reading is in bits 14..03, get rid of bit 2,1,0
      holdingRegs[WMOD0_TEMP_EXTR]=reading;    // READ MAX6675 1            
  }            
}

Maybe more ideas?

Tobi, you wrote:

Think i've already all that what you wrote.

But it makes no sense to me.
Also, what do you mean when you say

Also if I call the modbys within the "delay".

Also what ?

Remove the line that is modbus_update_comms(9600, SERIAL_8N2, 1);It is just replicating the line above. This statement is for when you need to change Modbus settings during run-time.

Use your diagnostic skills, get your SPI working by itself reliably first.
So, comment out the modbus_update() function and make your program get the SPI data every one second and send that out the serial port to your computer so you can see it is working. Make sure you set the serial_begin correctly and no modbus code.

One you have proved the SPI code to function, then re-insert your Modbus code.


Paul - VK7KPA

But as I said: SPI function works, if i try it in serial monitor without modbus. No Prob!

that doesn't work:

void loop() {
   unsigned long actMillis = millis();
    if (actMillis - prevMillis >= chktemp) {
      prevMillis = actMillis;  
    modbus_update();
   } 
 
                           
      digitalWrite(CSEXTR, LOW);
      delayMicroseconds(0);        // zusätzliche Wartezeit, 100ns erforderlich
      uint16_t reading = SPI.transfer16(0);
      digitalWrite(CSEXTR, HIGH);
      reading = (reading >>3)*25;  // the reading is in bits 14..03, get rid of bit 2,1,0
      holdingRegs[WMOD0_TEMP_EXTR]=reading;    // READ MAX6675 1            
            
}

Init looks now like this:

// Init Modbus
  modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 2, HOLDING_REGS_SIZE, holdingRegs);
 // modbus_update_comms(9600, SERIAL_8N2, 1);

But It works like this (but slow and ugly):

void loop() {
   delay(100);
    modbus_update();
    delay(100);
    
 
                           
      digitalWrite(CSEXTR, LOW);
      delayMicroseconds(0);        // zusätzliche Wartezeit, 100ns erforderlich
      uint16_t reading = SPI.transfer16(0);
      digitalWrite(CSEXTR, HIGH);
      reading = (reading >>3)*25;  // the reading is in bits 14..03, get rid of bit 2,1,0
      holdingRegs[WMOD0_TEMP_EXTR]=reading;    // READ MAX6675 1 
}

Hmmmm... Sorry, but I tried more than 2 days before asking stupid question. Im looking foreward for more ideas. Thanks!

Okay, you did mention you tried SPI without Modbus code, sorry, I read too quickly.

The code at post #585 looks to me to be good.
You might like to try increasing the time period you read the SPI device, from 100mSec to 1000 or 2000mSec.

There is probably little point in reading it faster than what you are getting it with the Modbus master in reality.

I checked the MAX6675 specifications PDF, and it is not a complicated chip to work with, so it should work quite well.

But what ever you do, avoid using delay(x) where x is longer than 10mSec.
With good coding, you should not need to ever use delay(x), there is always something the CPU can do :slight_smile:


Paul - VK7KPA

Good day!

I am currently working on this set-up:

  • Arduino Mega as the modbus master (using RS 485)
  • several Eastron Meters as slaves (model: SDM630)

As an initial step, I tried to compile and upload the code provide by JuanB to Sbeghers (found in page 13 eastron_sdm320mv4.ino). Sbeghers confirmed that this code worked. A few modifications on the code would eventually make communication with SDM630 possible. However i am faced with the following errors even though i have not made any modifications yet on the original code. What i did was just to open the file and compile.

/Users/mcbathan/Documents/Arduino/Eastron_SDM320Mv4/Eastron_SDM320Mv4.ino: In function 'void setup()':
Eastron_SDM320Mv4:39: error: 'modbus_construct' was not declared in this scope
   modbus_construct(packet1, 1, READ_HOLDING_REGISTERS, 0, 2, readRegs);
                                                                      ^
Eastron_SDM320Mv4:41: error: invalid conversion from 'HardwareSerial*' to 'long int' [-fpermissive]
   modbus_configure(&Serial1, baud, SERIAL_8E1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
                                                                                                                        ^
/Users/mcbathan/Documents/Arduino/Eastron_SDM320Mv4/Eastron_SDM320Mv4.ino:41:120: warning: large integer implicitly truncated to unsigned type [-Woverflow]
Eastron_SDM320Mv4:41: error: invalid conversion from 'int' to 'Packet*' [-fpermissive]
Eastron_SDM320Mv4:41: error: too many arguments to function 'void modbus_configure(long int, unsigned int, unsigned int, unsigned char, unsigned char, Packet*, unsigned int)'
In file included from /Users/mcbathan/Documents/Arduino/Eastron_SDM320Mv4/Eastron_SDM320Mv4.ino:1:0:
/Users/mcbathan/Documents/Arduino/libraries/SimpleModbusMaster/SimpleModbusMaster.h:143:6: note: declared here
 void modbus_configure(long baud, unsigned int _timeout, unsigned int _polling, 

      ^
/Users/mcbathan/Documents/Arduino/Eastron_SDM320Mv4/Eastron_SDM320Mv4.ino: In function 'void loop()':
Eastron_SDM320Mv4:46: error: too few arguments to function 'unsigned int modbus_update(Packet*)'
   modbus_update();
                 ^
In file included from /Users/mcbathan/Documents/Arduino/Eastron_SDM320Mv4/Eastron_SDM320Mv4.ino:1:0:
/Users/mcbathan/Documents/Arduino/libraries/SimpleModbusMaster/SimpleModbusMaster.h:142:14: note: declared here
 unsigned int modbus_update(Packet* packets);

              ^
Multiple libraries were found for "SimpleModbusMaster.h"
 Used: /Users/mcbathan/Documents/Arduino/libraries/SimpleModbusMaster
 Not used: /Users/mcbathan/Documents/Arduino/libraries/SimpleModbusMasterV2rev2
exit status 1
'modbus_construct' was not declared in this scope

Im very new to programming and need your help to decipher what went wrong or what i did wrong. Below is the code provided by JuanB (eastron_sdm320mv4.ino):

#include <SimpleModbusMaster.h>


//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 200 // the scan rate

#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 2 

enum
{
  PACKET1,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

packetPointer packet1 = &packets[PACKET1];

unsigned int readRegs[2];

unsigned long currentMillis;
long previousMillis = 0;
long interval = 1000;


void setup()
{
  // read 2 registers starting at address 0  
  // This is the Line to neutral voltage
  // The value received needs to be transposed to floating value
  modbus_construct(packet1, 1, READ_HOLDING_REGISTERS, 0, 2, readRegs);
 
  modbus_configure(&Serial1, baud, SERIAL_8E1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
}

void loop()
{
  modbus_update();
  
  // shift and cast value to float
  float voltage;
  unsigned long temp = (unsigned long)readRegs[0] << 16 | readRegs[1];
  voltage = *(float*)&temp;
  
  // use the following code to avoid using delay()
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    Serial.print("Low byte: ");
    Serial.println(readRegs[0]);
    Serial.print("High byte: ");
    Serial.println(readRegs[1]);
    Serial.print("Voltage: ");
    Serial.println(voltage);
    Serial.println("********");
    previousMillis = currentMillis;
  }
}

Would very much appreciate your help on these. Thank you in advance.

Thanks once more for your reply rockwallaby,

I am not at home at the moment and can not try.
To be honest, i am new in C - in real live i am Siemens PLC Programmer.

There is probably little point in reading it faster than what you are getting it with the Modbus master in reality

I can adjust the poll time in the Master, but increasing from 25ms to 1000 makes no different at all. I also tried with enable/disable interrupts and with disabling, i get a receive timeout. Im not sure if there could be the problem that spi uses interrupts?

Greetz, Tobi

@rockwallaby

You might like to try increasing the time period you read the SPI device, from 100mSec to 1000 or 2000mSec.

Yeeeeeaaaaah Dude - you made my day! Increasing the period to 300 (not less) works fine for me!
I don't know why and that is a little bit sad, but there are enough other problems with other things.
So, that should be good and i can live with that.

Many THX 4 support and greetings!!

Hi Tobi, you are welcome.
Yes, in my other life, I too am a industrial systems engineer, have designed with S7 PLCs, but mostly use Allen Bradley PLC, with SCADA.

Anyhow, I'm happy you have made some progress, I know it can be frustrating when things look correct, but do not work.
The same can happen with hardware as well.

Have fun with your project now :slight_smile:


Paul - VK7KPA

mavinrya, it appears to me you are using code that was developed for older libraries.

If so, you can use the code you have with the libraries of that time, or use up to date libraries with corrected code.
Please read the SimpleModbus manual, it is all explained in there.


Paul - VK7KPA

hi guys
i have power meter pm800 schneider i want to connect it with arduino using rs485 port in power meter, but i have no idea to wiring (+) and (-) in communication port in power meter, port (+) to A or to B in max485

i googling and then i found this in wattmetrics.com but still i dont understand

Agent COM1 or COM2 Color Function PM800 (COM1)
485+ white w/orange stripe data (B) + D1+ (20)
485- orange data (A) - D0- (19)
GND brown common [gnd symbol] (18)

thank you srry for my bad english....

Ivino, you need help with understanding on how to search effectively online.

I simply put in my search the following:

RS-485 wiring schneider PM800

Then from the very first in the list I get the following link:RS-485 wiring PM800

Start looking from page 13 and it details how to connect RS-485 to the PM800.

When giving details on this forum, please provide clear and detailed information about what you are actually using. We have no idea what Arduino or EIA-485 interface you are using.

Please spend a few extra minutes in making your post on this forum as detailed as you can, provide links or images to tell us what you have and are trying to do.

Also, do you see your table above, it's a mess, why do you not tidy it up so it looks readable ?


Paul - VK7KPA

rockwallaby:
Ivino, you need help with understanding on how to search effectively online.

I simply put in my search the following:Then from the very first in the list I get the following link:RS-485 wiring PM800

Start looking from page 13 and it details how to connect RS-485 to the PM800.

When giving details on this forum, please provide clear and detailed information about what you are actually using. We have no idea what Arduino or EIA-485 interface you are using.

Please spend a few extra minutes in making your post on this forum as detailed as you can, provide links or images to tell us what you have and are trying to do.

Also, do you see your table above, it's a mess, why do you not tidy it up so it looks readable ?


Paul - VK7KPA

thank you for ur answer

my project are :
-Arduino Mega 2560 act as Master
-Power Meter Schneider PM800 act as Schneider-i want to read Voltage in Input Register address 1120 and publish it in serial monitor (just for now) u can see register list in attachment

now my problem i cant read my register i just got value 0.0 like this :

test
0.0
requests: 1
successful_requests: 0
failed_requests: 0
exception_errors: 0
connection: 1

and this is my sketch

#include <SimpleModbusMaster.h>

/*
   The example will use packet1 to read a register from address 0 (the adc ch0 value)
   from the arduino slave (id=1). It will then use this value to adjust the brightness
   of an led on pin 9 using PWM.
   It will then use packet2 to write a register (its own adc ch0 value) to address 1 
   on the arduino slave (id=1) adjusting the brightness of an led on pin 9 using PWM.
*/

//////////////////// Port information ///////////////////
#define baud 38400
#define timeout 1000
#define polling 400 // the scan rate
#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 3

#define LED 9

// The total amount of available memory on the master to store data
#define TOTAL_NO_OF_REGISTERS 12

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
  PACKET1,
 // PACKET2,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

// Masters register array
unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  // Initialize each packet
  Serial1.begin(38400);
  Serial.begin(19200);
  modbus_construct(&packets[PACKET1], 1, READ_INPUT_REGISTERS, 1125, 1, 10);
 // modbus_construct(&packets[PACKET2], 1, PRESET_MULTIPLE_REGISTERS, 1, 1, 0);
  
  // Initialize the Modbus Finite State Machine
  modbus_configure(&Serial1, baud, SERIAL_8E1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
  
  
}

void loop()
{
  modbus_update();

  
  unsigned long temp;

  float Voltage;
  temp = (unsigned long) regs[10] << 16 | regs[11];
  Voltage = *(float*)&temp;
  Serial.println("test");
  Serial.println(Voltage, 1);

  Serial.print("requests: ");
    Serial.println(packets[PACKET1].requests);
    Serial.print("successful_requests: ");
    Serial.println(packets[PACKET1].successful_requests);
    Serial.print("failed_requests: ");
    Serial.println(packets[PACKET1].failed_requests);
    Serial.print("exception_errors: ");
    Serial.println(packets[PACKET1].exception_errors);
    Serial.print("connection: ");
    Serial.println(packets[PACKET1].connection);
  
}

u can see my schme in attachtment, maybe u want to fix my hardware

i buy rs485 to ttl like this :

and i also attach my power meter setting

thank you

PM800 REGISTER LIST.pdf (667 KB)

Ivino, according to your scheme1 you do not use additional pullup / pulldown resistors between line A, VCC and B, GND. Your breakoutboard has already 20k resistors, but it is highly recommended to go for somewhat like 1kOhm pullup and pulldown. See also RS-485 - Wikipedia

I would try to put in addition (to the breakout board) 1kOhm between GND and B as well as 1kOhm between VCC and A.

Termination have been taken care by the breakout board (120 Ohm on board) as well as by your slave (most likely - better check in their specs).

If you have done this, at least you are sure, that there is no issue with the electrical specifications of the RS485 bus.

thank you fiete_02

i didnt do that, i will try it

thank you fiete_02

i didnt do that, i will try it

edit :

so i must remove r5 and r6 ? and change it wit 1kohm resistor?

No, that wouldn't make much sense. According to Ohm's law, you can reduce a resistor by putting a resistor in parallel. So don't remove R5 and R6 but put additional 1kOhm in parallel between A -> VCC and B -> GND which results in about 950 Ohm (much better than 20kOhm). This is much easier than repairing the SMD resistors.

If you like to be perfectly correct, put 750 Ohm in parallel to R5 and R6 which results in 722 Ohm which is really close to the optimal 680 Ohms.

Hi all,
Congratulations for this library and for the support.
I read the previous (600 :o ) posts and I don't see answer to my question.

I will try to explain:

I have a unique slave (ID1), let call it "machine", reading address0 from master we can know if the machine is On or Off, sending a packet like:

 modbus_construct(&packets[PACKET0], 1, READ_HOLDING_REGISTERS, 0,1, 0);

The machine has a pannel, from where user can manually turn it On or Off, and this state will be monitored in the master.

But also, from the master I want to turn the machine on or off writing in the bus a packet like:

 modbus_construct(&packets[PACKET1], 1, PRESET_MULTIPLE_REGISTERS, 0,1, 1);

I need send this last packet ONLY ONCE (when I want force the machine to On or Off).

Do someone know if is this supported by the library in some way?
In other words, read an address and eventually write the same address (in the same slave)

Thanks

Hi Juanluis,

the reason, why you have not seen some answers to your issue (writing once) is pretty simple : The modbus library have been written mainly to frequently read sensors on a modbus thus there are routines in the library which make it easy to poll sensors and get their current readings.

But you can use the library to do what you want it to do :

  • Use 2 packets for reading and writing (as you have done in your example).
  • disconnect the packet1 as long as you don't need to write to the registers of your "machine"
  • call modbus_update() at least once in your loop()
  • if you want to write to your "machine", reconnect packet1 and check, if request was successful (successful_requests of packet1 increased by 1), then disconnect the packet1 again.

If you do not know, how to disconnect and reconnect, have a look at the SimpleModbusMaster manual, which comes with the latest library V2rev2.

Maro

Juanluis, welcome to the forum.

First, you say

I have a unique slave (ID1), let call it "machine", reading address0 from master we can know if the machine is On or Off, sending a packet like:

You are describing that the slave reads data from the master, this can not happen, only the master can initiate any form of data transfer, not the slave. So, only the master can send data from its address0 to the slave.

Try to remember that the master is in control of everything, with the slaves only existing as mail boxes, the master is the postman.

For what you want to do, you need the master to read the slave data and then combine the machine state with the master command machine state and send that back to another address in the slave. Then have some logic in the slave that reads the master command and act on that.

You need to work out which end, slave or master will have priority of control of machine state, which has nothing to do with Modbus and which is Modbus slave or master.

Maro, you write:

The modbus library have been written mainly to frequently read sensors on a modbus thus there are routines in the library which make it easy to poll sensors and get their current readings.

What utter rubbish, sorry to say.
The Modbus protocol was initially used in industrial automation, way back in the 1970's, which is what I do for many years now, and is used as a means to connect devices, whether that be PLCs or computers or remote I/O over a common and well defined and rugged data protocol. The Modbus protocol is now an open data protocol and has gained increasing attention and popularity in recent years, with many device manufactures now implementing some form of Modbus connectivity, usually RTU or TCP.

The use of Modbus in the Arduino world to achieve the same, and hence a Modbus library for Arduino style boards.
There are many Modbus libraries for all sorts of platforms and in many different languages, C, C++, Python, NodeJS, even PHP. There are even a number of Modbus libraries for Arduino boards, with this one being of the RTU variety.

It is not true or accurate to say it was written 'mainly' to read sensors. Sensors are sensors and on the whole, not many have this form of integrated communications as part of the sensor. Typically, you will connect a sensor to a device that accepts a sensor such as 4-20mA input and provides a communications channel, such as Modbus.


Paul - VK7KPA

Hi Maro,
Thanks for answering.

fiete_02:

  • Use 2 packets for reading and writing (as you have done in your example).
  • disconnect the packet1 as long as you don't need to write to the registers of your "machine"
  • call modbus_update() at least once in your loop()
  • if you want to write to your "machine", reconnect packet1 and check, if request was successful (successful_requests of packet1 increased by 1), then disconnect the packet1 again.

Eventually ,I will write a function for the library to do something like that ..

Regards
Juan