Photon & rs-485

hello everyone!

First post, so it’ll be dumb… :slight_smile:
I haven’t received my photon yet, but I am wondering if I am missing something:
I will be connecting the photon to an rs-485 interface. Do I just connect the Rx & Tx pins or am I going to fry the board?!

Thanks

-Fred

You’ll fry it. You need an RS485 transceiver - RS485 is a differential, half-duplex bus, there are no separate TX and RX pins.

At the very least, you want one of these: https://www.sparkfun.com/products/10124 though that’s non-isolated - many RS485 installs require an isolated interface and for that you need isolated power for the bus-side too.

For those, ISO35 or ADM2587 chips do most of what you need but there’s a lack of cheap breakouts for them. The TI evalkit is a bit expensive: http://gr.mouser.com/ProductDetail/Texas-Instruments/ISO485EVM/?qs=%2Fha2pyFaduhVLiLcrVZcITASaW1Fh2sOVQywCPwiack%3D

I’m also going down the RS-485 (modbus RTU) road.

I bought a couple of these boards: http://www.ebay.ca/itm/252030206330

I haven’t got to the point of hooking them up yet, I have some questions.

  • Can anyone recommend wiring (which pins on photon?)
  • What are the limitations? While using these, is anything else restricted (other uarts, spi, i2c busses etc?)

Thanks!

Hey @vinistois,

Here is a post from a while ago with some Modbus RS485 stuff on it.
https://community.particle.io/t/rs-485-modbus-library?source_topic_id=19670

peekay123 has ported the library and it works decent. Unfortunately that specific library does not differentiate between 3x and 4x registers; but it still will cover most applications. I have seen Arduino modbus libraries that offered better register control, but I recall there being an issue with the DE/RE control.

This link should be able to help you with the wiring. Its for an Arduino with software serial, but the wiring to the MAX485 part is still helpful.
https://arduino-info.wikispaces.com/SoftwareSerialRS485Example

I used the same MAX485 chip from Ebay and it worked without any issues at all. I connected them with a Panasonic FPX PLC as a master and was able to read/write multiple slaves (both Arduino and Photon).

I deal with industrial communication from time to time during my day job and RS485 is quite common in the industrial world (Profibus for one) but always with industrial equipment and never with DIY chips. Not saying it isn’t possible though. I don’t know how I2C word work since there are many differences and SPI requires a lot more lines; including an additional line per each slave. For that you would need more of a processing gateway rather that just a level shiftier.

1 Like

thanks for the link! I hadn't seen that one and it is indeed very helpful!

I need my photon to act as a master and connect to multiple slave devices.

Have you seen this library?

https://code.google.com/archive/p/simple-modbus/

I'm not sure what is involved in porting a library, but I have come across a few modbus projects that use that library successfully.

The last part of my post was perhaps confusing. What I meant is, while I am doing modbus with a MAX485, is there anything that I now cannot do on my photon? Does it use up anything other than 2 gpios?

OK, sorry, I misunderstood your last point, I should have read it a second time… I was using one I2C sensor with one I2C text display and did not have any modbus communication issues. However it is important to note my master was an a PLC that would retry a 3 times before reporting a communication error. I did play around with an Arduino and noticed some sensors would cause issues. One I recall was a DHT22 temperature and humidity sensor. Sometimes my PLC would timeout while the Arduino was processing the DHT22. Apparently the DHT22 library requires precise timing to process the sensor; so the library may halt some processes.

I have have used that modbus library for Arduino, but only as a slave, never as a master. Sorry, but I cant tell you how hard it is to port. peekay123 would be the guy to ask since he ported the other library for us.

1 Like

thanks so much. I’ll give it a shot and report back.

Ok. Sorry, total newb here. How exactly do I use this library?

I have particle dev installed on my machine, but haven’t used it yet, I’ve only been using the web ide. I can’t seem to figure out how to import this library to the web ide.

Do I have to use particle dev to make use of a library that’s not in the web ide? If so, that’s fine, but how do I transfer all my code from the web ide to particle dev?

Thanks and sorry for the basic questions.

No problem. You can use either the web IDE or the local IDE; whatever floats your boat. But here is what I did for the Web IDE:

Because this library is not in the Particle web IDE shareable library, you will have to manually copy it into your application. From the web IDE, I just created another tab in my firmware app by pressing the little “+” button on the top right. Call it “ModbusRtu” (the .h should auto appear) and manually copy the text from the needed .h file into that tab. It will automatically create a .cpp tab as well with the same root name as the .h. For this specific application we don’t need the .cpp file.

In your .ino tab (where your main firmware goes), make sure you call the library by declaring it: #include “ModbusRtu.h”. Because we are referencing a library within our working directory, we don’t need to specify the library directory, just the name.

1 Like

awesome. You’re so helpful, thank you! I used the web ide, thanks for clarifying the .cpp would be empty, that threw me off as I thought “that can’t be right…”

I wired it all up, using D2 for my DE/RE pin. I changed the TXEN variable to reflect this. Now, I used D6 for data in and D7 for data out , but could not find where to set these in the code. I’m not sure if these are the “right” pins to use.

Since I need my photon to behave as master, I also changed:

Modbus slave(10,0,TXEN);

to:

Modbus master(0,0,TXEN);

And in the setup I have this:

// MODBUS
     master.begin( 19200 ); // baud-rate at 19200

And the main loop contains:

    //MODBUS
    master.poll( au16data, 16 );

So, the code compiles fine and the rest of my code is operating as normal.

Now… is there any documentation on how to proceed? Do you know of any example code I can look at?

Here is an example request I need to send to the slave device. This will read 3 registers continuous from address 100H

field name 01H
function code 03H
start address (high byte) 01H
start address (low byte) 00H
number of registers (high byte) 00H
number of registers (low byte) 03H
CRC 16 (high byte) 37H
CRC 16 (low byte) 04H

I will then get a response which I need to parse and turn the data into variables I can work with.

I’m trying to figure out if the 3x/4x limitation of this library will be an issue for my application, but I’m not quite there yet. more reading.

Thanks again!

I will wire up two Photons together tonight with the MAX485 chips and see if I can get it to work with one as master and one as a slave. I will let you know how it works

The MAX485 RD/DI pins should be connected to the Photon RX/TX pins. I am not using software serial for my application and use Serial1 instead. It is common to use Software Serial for Arduino since only one hardwired serial channel exists. In the case of the Photon we actually have 3 serial channels (one used for the USB, one for the RX/TX pins and the other for the RGB LED). The default channel used for this modbus library is serial1; which is connected to the Photon RX/TX pins.

@vinistois, @adamg, there is no Software Serial library for the Particle devices as it disables ALL interrupts too often, chocking out the system.

@peekay123, alright, that is good to know, thanks for info. I have only ever used it with Arduino and never needed to use it with a Photon because we have the extra channels :smile:

1 Like

Ahh yes, I had that brainwave very early this morning. RD-->RX DI-->TX?

I think all I need at this point is some sample code or a reference document to get me going.

@vinistois Yes, If I remember correctly RD–>RX and DI–>TX, but I will verify that tonight when I try it.

I should also share this link with you:

That is the original github repository that was used by peekay123 to port the ModbusRtu.h library. There is an examples folder with some Master examples that might be worthwhile looking at. The examples are for Arduino, so you might not be able to just copy it, but it will probably shine some light on getting it to work.

Just out of curiosity, what device is your Slave going to be?

Thanks! that’s perfect.

The slave is an industrial power supply.

I am basing my code on this:

It is compiling without errors, so that’s always a good start.

Alright, I’m getting somewhere now.

My code is based on the “advanced modbus master” code.

setup:

void setup() {
    
    // telegram 0: read registers
  telegram[0].u8id = 1; // slave address
  telegram[0].u8fct = 3; // function code (this one is registers read)
  telegram[0].u16RegAdd = 100; // start address in slave
  telegram[0].u16CoilsNo = 3; // number of elements (coils or registers) to read
  telegram[0].au16reg = au16data; // pointer to a memory array in the Arduino

  // telegram 1: write a single register
  telegram[1].u8id = 1; // slave address
  telegram[1].u8fct = 6; // function code (this one is write a single register)
  telegram[1].u16RegAdd = 2; // start address in slave
  telegram[1].u16CoilsNo = 1; // number of elements (coils or registers) to wwrite
  telegram[1].au16reg = au16data+4; // pointer to a memory array in the Arduino
	
  master.begin( 19200 ); // baud-rate at 19200
  master.setTimeOut( 5000 ); // if there is no answer in 5000 ms, roll over
  u32wait = millis() + 1000;
  u8state = u8query = 0; 

}

and loop:

void loop() {
    
    switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    master.query( telegram[u8query] ); // send query (only once)
    u8state++;
	u8query++;
	if (u8query > 2) u8query = 0;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 1000; 
    }
    break;
  }

  au16data[4] = analogRead( 0 );
    }

I connected this to an FTDI FT232RL usb-RS-485 adapter, plugged it into an OTG cable into my phone, fired up modbus monitor and got this output:

The program appends the word “error” to the name of the connection as soon as it is connected.

A similar program on my desktop, using the same usb adapter, got me this output;

@vinistois OK. I was able to get one Photon with Modbus Master firmware to communicate with another Photon with Modbus Slave firmware over RS485. I had to make some modifications to your example code because it employed multiple slaves and the timeouts for unavailable nodes was killing my updates. I also declared a TXEN variable that is used to define the pin connected to the DE/RE pins. I used D6 for my tests. I put my comments above the stuff I modified with my name and date.

One thing that I forgot to mention, is that sometimes you need a 1k resistor between A–>Vcc and a 1k resistor between B–>GND (in this case that was required).

Below is the code I used in the Master. I also added some debug code to print the register values read every 1000ms.

Tomorrow I will connect my master Photon up to a GE Multilin 469 relay and see if I can poll data from an industrial device successfully over RS485.

/**
 *  Modbus master example 2:
 *  The purpose of this example is to query several sets of data
 *  from an external Modbus slave device. 
 *  The link media can be USB or RS232.
 *
 *  Recommended Modbus slave: 
 *  diagslave http://www.modbusdriver.com/diagslave.html
 *
 *  In a Linux box, run 
 *  "./diagslave /dev/ttyUSB0 -b 19200 -d 8 -s 1 -p none -m rtu -a 1"
 *  This is:
 *   serial port /dev/ttyUSB0 at 19200 baud 8N1
 *  RTU mode and address @1
 */

#include "ModbusRtu.h"


//Adam Edit Feb 3rd.  Added TXEN declared variable.  This is pin connected to the DE/RE pins on the 485 chip.  
#define TXEN	D6 

uint16_t au16data[16]; //!< data array for modbus network sharing
uint8_t u8state; //!< machine state
uint8_t u8query; //!< pointer to message query

/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
 
//Adam Edit Feb 3rd.  Orginally (0,0,0), changed last delcared variable to TXEN, this is similar to other Modbus Libraries.
Modbus master(0,0,TXEN); // this is master and RS-232 or USB-FTDI

/**
 * This is an structe which contains a query to an slave device
 */
modbus_t telegram[2];

unsigned long u32wait;
unsigned long current_millis = 0;
unsigned long print_millis = 0;

void setup() {
  // telegram 0: read registers
  telegram[0].u8id = 10; // slave address
  telegram[0].u8fct = 3; // function code (this one is registers read)
  telegram[0].u16RegAdd = 0; // start address in slave
  telegram[0].u16CoilsNo = 16; // number of elements (coils or registers) to read
  telegram[0].au16reg = au16data; // pointer to a memory array in the Arduino

 //Adam Edit Feb 3rd.  I only have one slave****
  // telegram 1: write a single register
  //telegram[1].u8id = 1; // slave address
  //telegram[1].u8fct = 6; // function code (this one is write a single register)
  //telegram[1].u16RegAdd = 4; // start address in slave
  //telegram[1].u16CoilsNo = 1; // number of elements (coils or registers) to read
  //[1].au16reg = au16data+4; // pointer to a memory array in the Arduino
 
  master.begin( 19200 ); // baud-rate at 19200
  master.setTimeOut( 5000 ); // if there is no answer in 5000 ms, roll over
  u32wait = millis() + 1000;
  u8state = u8query = 0; 
}

void loop() {
    
    current_millis = millis();
    
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    master.query( telegram[u8query] ); // send query (only once)
    u8state++;
 u8query++;
 
 //Adam Edit Feb 3rd.  I only have one slave, therefore changed u8query to >0 so only one slave is read.  Change when adding more slaves!
 if (u8query > 0) u8query = 0;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 1000; 
    }
    break;
  }
  
  //Adam Edit Feb 3th.  Not using this right now...   
  //au16data[4] = analogRead( 0 );
  
  
  if (current_millis >= (print_millis+1000))
  {
      print_millis = current_millis;
      for (int j =0; j<16; j++)
      {
      Serial.print("H");
      Serial.print(j);
      Serial.print(":");
      Serial.print(au16data[j]);
      Serial.print(" ");
      }
      Serial.println();
  }
}

@adamg, great work! Once you get everything working, it would be great if you could do a pull request to my repo so anyone accessing can get the benefits! :wink: