RS485 sensor communication connection

Hi,

I’m totally newbie for particle and IOT.
I just passed the getting started processes.
What I’m trying to achieve is to collect sensor output in the data log but I’m not quite sure how to do it. My sensor signal interface is Modbus RS-485 with 2 bare wires A and B. Now what I have is photon and related kits with max485 module (https://www.banggood.com/5V-MAX485-TTL-To-RS485-Converter-Module-Board-For-Arduino-p-912674.html?rmmds=search).
I tried to find useful articles in the forum (this is probably the most helpful article Photon and Modbus 485 RTU - Connections & interface requirements for newbie like me) but to be honest, I’m not sure how to wire or what else or any more necessary devices do I need. Do I need to use USB-RS485 cable? Do I need to include any library? I just want to test it first that sensor’s working before correctly collect the data.

Here are the steps I’ve done.

  1. Ground has been connected completely.
  2. I connected 5V power source to max485 module and my sensor
  3. I connected A and B port of sensor to A and B port of max485 module
  4. I connected RD/DI pins of max 485 to the Photon RX/TX pins.
  5. I connected DE/RE pins to Photon D3 pin and use the following code.

Apology for my stupidity, but I’m really new in this field.

String readString = "";

void setup() {
    digitalWrite(D3, LOW);
    digitalWrite(D0, HIGH);
    Serial.begin(9600);
    Serial1.begin(9600);
    Particle.publish("Setup");

    Particle.function("digitalread", tinkerDigitalRead);
  	Particle.function("digitalwrite", tinkerDigitalWrite);
  	Particle.function("analogread", tinkerAnalogRead);
  	Particle.function("analogwrite", tinkerAnalogWrite);
  	Particle.function("readsendor", readSensor);
}

void loop() {

  while (Serial1.available()) // While receiving characters over serial...
  {
    delay(1000); // Necessary delay
    char c = Serial1.read(); // Read the character
    readString += c; // Add the character to the string
  }

  readString.trim();

  if (readString.length() > 0) // If a string has been read...
  {
    // Serial.println("Received: " + readString); // Send the parsed string to Serial for debugging
    Particle.publish("Read", readString);
    readString = ""; // Clear the string
  }

}

int readSensor(String x)
{
  Particle.publish("readsensor start");
  while (Serial1.available()) // While receiving characters over serial...
  {
    delay(1000); // Necessary delay
    char c = Serial1.read(); // Read the character
    readString += c; // Add the character to the string
  }

  readString.trim();

  if (readString.length() > 0) // If a string has been read...
  {
    // Serial.println("Received: " + readString); // Send the parsed string to Serial for debugging
    Particle.publish("Read", readString);
    readString = ""; // Clear the string
  }
  Particle.publish("readsensor out");

  return 1;
}

/*******************************************************************************
 * Function Name  : tinkerDigitalRead
 * Description    : Reads the digital value of a given pin
 * Input          : Pin
 * Output         : None.
 * Return         : Value of the pin (0 or 1) in INT type
                    Returns a negative number on failure
 *******************************************************************************/
int tinkerDigitalRead(String pin)
{
	//convert ascii to integer
	int pinNumber = pin.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	if(pin.startsWith("D"))
	{
		pinMode(pinNumber, INPUT_PULLDOWN);
		return digitalRead(pinNumber);
	}
	else if (pin.startsWith("A"))
	{
		pinMode(pinNumber+10, INPUT_PULLDOWN);
		return digitalRead(pinNumber+10);
	}
	return -2;
}

/*******************************************************************************
 * Function Name  : tinkerDigitalWrite
 * Description    : Sets the specified pin HIGH or LOW
 * Input          : Pin and value
 * Output         : None.
 * Return         : 1 on success and a negative number on failure
 *******************************************************************************/
int tinkerDigitalWrite(String command)
{
	bool value = 0;
	//convert ascii to integer
	int pinNumber = command.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	if(command.substring(3,7) == "HIGH") value = 1;
	else if(command.substring(3,6) == "LOW") value = 0;
	else return -2;

	if(command.startsWith("D"))
	{
		pinMode(pinNumber, OUTPUT);
		digitalWrite(pinNumber, value);
		return 1;
	}
	else if(command.startsWith("A"))
	{
		pinMode(pinNumber+10, OUTPUT);
		digitalWrite(pinNumber+10, value);
		return 1;
	}
	else return -3;
}

/*******************************************************************************
 * Function Name  : tinkerAnalogRead
 * Description    : Reads the analog value of a pin
 * Input          : Pin
 * Output         : None.
 * Return         : Returns the analog value in INT type (0 to 4095)
                    Returns a negative number on failure
 *******************************************************************************/
int tinkerAnalogRead(String pin)
{
	//convert ascii to integer
	int pinNumber = pin.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	if(pin.startsWith("D"))
	{
		return -3;
	}
	else if (pin.startsWith("A"))
	{
		return analogRead(pinNumber+10);
	}
	return -2;
}

/*******************************************************************************
 * Function Name  : tinkerAnalogWrite
 * Description    : Writes an analog value (PWM) to the specified pin
 * Input          : Pin and Value (0 to 255)
 * Output         : None.
 * Return         : 1 on success and a negative number on failure
 *******************************************************************************/
int tinkerAnalogWrite(String command)
{
	//convert ascii to integer
	int pinNumber = command.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	String value = command.substring(3);

	if(command.startsWith("D"))
	{
		pinMode(pinNumber, OUTPUT);
		analogWrite(pinNumber, value.toInt());
		return 1;
	}
	else if(command.startsWith("A"))
	{
		pinMode(pinNumber+10, OUTPUT);
		analogWrite(pinNumber+10, value.toInt());
		return 1;
	}
	else return -2;
}

@mekunka, can you provide a link to the sensor you are using?

I suggest you use CAT3 or CAT5 cable using one twisted pair for communications. Twisted pair cabling reduces “common-mode” noise. A common ground is not necessary since RS485 uses a “balanced pair” without a ground reference. How far is the sensor from the Photon?

As for your code, in setup(), prior to doing digitalWrite(), you will need to call pinMode() to configure the pins as outputs. In loop(), I am not sure why you need a delay(1000); while reading Serial1. Can you explain? I would also try and avoid String objects to build your received message if possible and use char arrays but that can wait.

The same items apply toreadSensor(). Your calls to Particle.publish() may exceed the 1 per second rate if data is received from the sensor since you also have a second unconditional publish right after it. :smiley:

1 Like

Here is my reference link of sensor http://www.ponsel-web.com/cbx/_ftp/datasheet_digisens_optod.pdf
My apology, I think the cable is already CAT3 or CAT5 type. Sensor is connecting with the circuit board, so basically within 1m range (cable of sensor is 1.5m long). Here’s the image of how everything has been wired. Now I change from D3 pin to D0 pin.

The first code, delay(1000) is not significant, just for test and observation.
And here’s the code I adjusted.

String readString = "";

void setup() {
    pinMode(D0, OUTPUT);
    digitalWrite(D0, LOW); // RS485 receving mode

    Serial.begin(9600);
    Serial1.begin(9600);

    // Particle.function("digitalread", tinkerDigitalRead);
      // Particle.function("digitalwrite", tinkerDigitalWrite);
      // Particle.function("analogread", tinkerAnalogRead);
      // Particle.function("analogwrite", tinkerAnalogWrite);
}

void loop() {
  while (Serial1.available()) // While receiving characters over serial...
  {
    delay(3); // Necessary delay
    char c = Serial1.read(); // Read the character
    readString += c; // Add the character to the string
  }

  readString.trim();

  if (readString.length() > 0) // If a string has been read...
  {
    // Serial.println("Received: " + readString); // Send the parsed string to Serial for debugging
    Serial.print("Read: " + readString); // Print to console
    readString = ""; // Clear the string
  }
}

@mekunka, the sensor you have uses the ModBus protocol. You may want to look at the modbus library in my repo. The probe’s modbus documentation is available here.

Thanks a lot :slight_smile: , I will have a deeper look in it.

1 Like

@mekunka is that not a 5V 485 converter? Are you connecting the signals directly with your Photon which is a 3.3V? I am just asking the question as I have the same converter, which I want to use on my electron. Damage to the ports is the first thing that comes to mind.

The pins are 5V tolerant, so no worries from that side.

I just read it in the serial tutorial. :slight_smile:

Thanks for the reply @ScruffR

Hi

I am also working on a RS485 project using the RS485 transceiver SN65HVD1780P

I found that the “necessary delay” raised by @mekunka is necessary to be able to read the entire message.
Anybody understand why ?

In my case if I don’t use it I only read the first byte.

Here is my current code :

digitalWrite(_RSSwitchPin, LOW); // read mode
digitalWrite(Led,HIGH); // switch on the led while waiting for answer
while (Serial1.available() == 0) {} //Wait for serial answer
delay(20); // hate this 
byte Header[8]= {};
int i = 0;
byte inChar;
while(Serial1.available() > 0 && i<8) // read header
{
    if (Serial1.available() > 0 ) // if buffer si not empty
    {
        inChar = Serial1.read(); // Read a character
        Header[i] = inChar; // Store it
        i++; // Increment where to write next
    }
}

@Matgate, i wonder if this is a case of leftover data in the Serial receive buffer, either from a previous transaction or from switching the receiver mode on. This would cause the while (Serial1.available() == 0) {} //Wait for serial answer to pass immediately even though valid data is not yet available. The delay allows the (real) data arrive for the next read sequence. You may want to flush the receive buffer by (read until Serial.available() is false) prior to switching to read mode (ie at the end of your last full transaction). If you had an oscilloscope or logic analyzer, you could look at the Serial RX line when you switch the _RSSwitchPin to read mode to get an idea of the timing between the two.

1 Like

I tried to add Serial1.flush(); before and then after digitalWrite(_RSSwitchPin, LOW); and remove the delay.
It’s not working and I have the same behavior as no flush and no delay.

In my code, I have a TestRead() function which I call when I press a switch (we can see it on right of the breadboard)
No communication have been done before this function.

I publish the result for debugging.
The message I’m expecting is : 0200EE000403000B001C05
It’s working fine with the delay but without the delay I have this : 0200000000000000 (Zero are due to empty array I believe)
then If I press again the switch I get the rest of the message : 00EE000403000B001C05

Unfortunately I don’t have an oscilloscope or logic analyzer