RS-485 modbus library

@peergum, do you have any example code for this library? I do not see in your library where you select your enable for the MAX485 driver chip?

Kind regards,

Geert

You need to define 3 functions and call methods preTransmission, postTransmission and idle with them as arguments (I called the function by the same name below)

  • preTransmission switches the MAX to TX mode
  • postTransmission switches to RX
  • idle is called while waiting for a response
// Switch to SEND mode
// WARNING: the order the TX/RX switching is IMPORTANT
// wrong order can shutdown the MAX IC.
void preTransmission(void)
{
  // FIRST enable TX
  // this should reenable the IC in SEND mode
  if (TX_ENABLE_PIN) {
      digitalWrite(TX_ENABLE_PIN, HIGH);
  }
  // THEN disable RX
  // with TX LOW, this would switch the IC to sleep mode
  if (RX_DISABLE_PIN) {
      digitalWrite(RX_DISABLE_PIN, HIGH);
  }
  delay(20);
}

// Switch to RECEIVE mode
// WARNING: the order the TX/RX switching is IMPORTANT
// wrong order can shutdown the MAX IC.
void postTransmission(void)
{
    // FIRST enable RX
    if (RX_DISABLE_PIN) {
        digitalWrite(RX_DISABLE_PIN, LOW);
    }
    // THEN disable TX
    if (TX_ENABLE_PIN) {
        digitalWrite(TX_ENABLE_PIN, LOW);
    }
    delay(20);
}

void idle(void) {
    delay(10);
}

Note that I use both TX and ^RX pins of the max. you can simplify by using one pin on the electron and connecting it to both pins on the max if you don’t plan to switch the max to sleep mode

1 Like

I am having some trouble controlling a slave device using my Photon. The specs of a slave device I am trying to communicate with says that it uses 4800 odd parity. I was wondering how I can achieve this using the existent modbus master libraries for Particle (theres 2 at the moment, one of which is @peergum and the other is @peekay123 I believe . Is there a setting somewhere I can set? I’ve used modbus simulators on my PC and the commands work fine as long as I set it at odd parity. Any help is much appreciated.

Unfortunately the Modbus library I adapted only uses RTU mode, which is binary/8 bits. I believe @peekay123’s version also only handles RTU. To communicate using parity, you’ll have to find/develop a lib that uses ASCII mode (7 bits + parity).

Alternatively you should check your device’s doc, they most probably handle RTU mode as well.

Cheers
Phil


Thanks peergum for the quick response. I’ve attached the specifications. It is in RTU mode I believe. I’ve tried to fiddle with a bunch of settings in my modbus simulator and the only thing that seems to work is 4800 baud rate with odd parity. Very weird.

Try to setup the parity on the Serial interface manually after calling begin on your ModbusMaster instance:

ModbusMaster sensor;

setup() {
  sensor.begin(1, Serial1); // using slave ID 1 on Serial
  Serial1.begin(4800, SERIAL_8O1);
  // ... test a comm here
}

Also what chip do you use to communicate with your device?

1 Like

@peergum you’re a genius man. The solutions so intuitive too. I was too focused on the library. Thanks alot!

If that works, I’ll probably add the option to pass parity (e.g. setParity method).

1 Like

Yep, your solution worked great. :smile:
I also noticed that you added the set slave ID functionality. I was thinking about daisy chaining a few of these devices together but I am confused about how slave IDs work for multiple devices. I’ve only been working with one device at a time so that is easy to address when calling the constructor. However, having multiple slave devices seems more complicated. How do I set which device to have which slaveID? After that’s established, I am guessing I can easily call your setSlave function to switch slaves. One last thing is, I’m not sure how you would wire these devices up either, since all the connections are just A and B.

you have to configure the IDs on the devices themselves. Then you code will address each device by using corresponding ID. There’s no automatic configuration/discovery, although you can scan for devices using successive IDs, if you know a common register they use (e.g. serial number), so you have to know in advance what IDs are used on your bus.

As far as chaining them, you should check some modbus doc online, but note you will have to use terminations on each end of your chain. Note that having more than one device on the bus is generally a source of noise and require more care. Also any device you connect to the bus as to use short wires. In other words, do short "T"s, not long "Y"s :wink:

1 Like

I updated the library on github and particle, both with a speed improvement and an optional additional parity parameter to the setSpeed() method.

I also added a simple example on how to use the lib.

2 Likes

Thanks for all the help! I think I’ll have to scan through Slave ID’s and see if that works for me. However, forgive me if I’m wrong, wouldn’t putting two identical devices on the same modbus line have conflicting slave ID’s (since they come pre configured with it).

In terms of your library, is there a reason why you went with the custom pre/post transmission functionality for 485 chips as opposed to the other Particle IDE library (found at https://github.com/lithiumhead/ModbusMaster/blob/master/src/ModbusMaster.cpp) which simply has an enableTXPin function which handles the pre/post transmission stuff. Sorry for all the questions.

You'll have to connect them one by one and configure their ID with a program, unless they have a panel to configure them.

As mentioned earlier, I wanted to be able to act on the TX and ^RX pins of the MAX separately, so I could lower both simultaneously and put the MAX in sleep mode, which you can't do if you wire them together. I use the pre- and post- transmission function to act on the two pins. If saving battery is not a string requirement for you you could use the other library I guess.

I like your library because I would like the ability to set the slave ID which your library offers. The only issue I’m having right now is receiving messages back, they’re all timing out (error E2). When I write to a single register, it seems to work as the Slave will do the command but the return value will always be a timeout. Any suggestions on why this may be the case?

The only thing my pre and post transmission functions do is digitalWrite max485 enable pin high and low respectively. Is this incorrect?

I suggest you enable debugging and monitor on serial usb. That should give you some clues. Also add something like delay(10) in your idle function. It will wait 10ms before checking received bytes. If that works you can always tweak later to a shorter delay. If you want to try longer timeouts change the value in the modbusmaster.h file.

Thank you for your suggestions and I tried them all but did not work. However, after looking at the differences between the library I used before and your one, I noticed that previously the pin setup was done for me (as in pinMode) and all I had to do to fix the issue was pinMode(enablepin, OUTPUT). How silly of me and sorry for the bother. Again, thanks for the library, very useful :smiley:

@DriftingShadows, @peergum,
Thanks for the discussion and library. I am sure it will help me getting modbus working on my project.
Do you have any example sketches showing good practices using this library to respond to commands, or to copy registers to structures/variables and vice versa, etc?

My slave device has EEPROM registers defined for some config variables and I need to be able to update them by Modbus as well as through a web app. I also have a couple of sensor readings that I want to make available on modbus.

FWIW, I am using serial4 (pins C2, C3) rather than serial1, with DE,RE tied together to C4.

peergum has an example sketch in his library. Have you checked that out?

@peergum thanks for the port!

I have some questions on how to use the library correctly. :stuck_out_tongue:

Doing this:

uint8_t result = slave.readHoldingRegisters(3914,4);

    if (!result) {
        uint16_t value = slave.getResponseBuffer(0);
        Log.info("Received: %0x",value);
    } else {
        Log.warn("Read error");
    }

and the serial info is:

0000346294 [app] TRACE: RX:
0000346304 [app] TRACE: - 1
0000346304 [app] TRACE: - 3
0000346304 [app] TRACE: - 8
0000346304 [app] TRACE: - 2c
0000346304 [app] TRACE: - 81
0000346305 [app] TRACE: - 42
0000346305 [app] TRACE: - 48
0000346305 [app] TRACE: - 0
0000346305 [app] TRACE: - 0
0000346305 [app] TRACE: - 0
0000346306 [app] TRACE: - 0
0000346306 [app] TRACE: - e9
0000346306 [app] TRACE: - be
0000346306 [app] INFO: Status: 0
0000346306 [app] INFO: Received: 2c81

Which is cool cos i need the 0x42482C81 bytes and they are available!

Just wondering how I can extract the float32 value instead of just 2c81?

My initial suggestion, until someone smarter replies:
If it is just the formatting, instead of
Log.info(“Received: %0x”,value);
use
Log.info(“Received: %f”,value); // for float

If you need the full 32 bits, replace the
uint16_t value = …
with
float value = …