Communicating with Apogee Pyranometer through Modbus / RS485

Hi all,

I'm trying to integrate a Pyranometer (Apogee SP-522) that transmits data using Modbus onto an MKR1010 with MKR 485 Shield
The Shield is placed on top of the MKR 1010 and here is the schematic for the current circuit that is connected

The Dip Switches on the shield are set to AB - On, Half Duplex, YZ - Off.

This is the code used to read information from the Sensor. Libraries used are ArduinoRS485 and ArduinoModbus

#include <ArduinoRS485.h>
#include <ArduinoModbus.h>

void setup() {
  Serial.begin(19200);

  Serial.println("Modbus Pyranometer Sensor");
  
  ModbusRTUClient.begin(19200);

  if (!ModbusRTUClient.begin(19200,SERIAL_5E1)) //Even Parity
  {
    Serial.println ("Failed to start Modbus RTU Client");
    while(1);
  }
}

void loop() 
{
  float p1 = pyrano1();
  delay(100);
  Serial.println(p1);
  delay(100);
}

float pyrano1()
{
  float p1=0;
  if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x28, 0x02))
  {
    Serial.println("Failed to read registers");
    Serial.println(ModbusRTUClient.lastError());
  }
  else
  {
    uint16_t word1 = ModbusRTUClient.read();  //read data from the buffer
    uint16_t word2 = ModbusRTUClient.read();
    Serial.println(word1);
    Serial.println(word2);
    uint32_t millivolt = word1 << 16 | word2; //bit math
    p1 = millivolt/1000.0;
  }
  return p1;
}

The output is being displayed on the Serial Monitor while the board is connected to the computer
Currently, it is stuck at "Failed to read registers" and the lasterror is Connection Timed Out.
I have tried many different variations, changing the register values according to the manual but nothing seems to work.

Some advice/ help would be greatly appreciated and thank you for taking the time to read through this!
Apologies I could only link 2 links as I'm a new user, do let me know if any other information is required.

In half duplex mode, I think you should be using the Y & Z contacts rather than A & B.

I have tried using the Y & Z contacts, switching the Dip Switches as well to YZ - On but it still has the same error of Connection Timed Out

Which way did you wire the white and blue wires to the Y & Z contacts. I think Y to white and Z to blue.

This isn't right:

The user manual you linked to in post #1 says it's 8-bits, even parity, 1 stop bit.

Yes correct, Y positive and Z negative.

Ah got you, changed it to SERIAL_8E1. Just tested but it's still showing Connection Timed Out

A couple of thoughts:

Does the company that made the sensor have any PC based tools available to talk to the sensor?

You should be able to use one of the PC based modbus tools to talk to the sensor. It's a lot easier/quicker to determine if you have the right baud rate and device address than it is to use the "burn and learn" approach on an Arduino.

Once you have comms to your sensor, you will know the baud rate and device address are correct, which will give you 2 of the variables needed for Arduino modbus.

Yes they do, here

Yea definitely it would be quicker but its extra costs haha. If possible, would be better to connect straight to the Arduino. Maybe I'll have to contact their support soon

I would think any old USB to RS485 dongle would work. They are very cheap on eBay and the like - around 3GBP or 4USD.

I would also grab their software too if it's free.

EDIT: I just grabbed their DSI software tool and it looks like it may be trying to detect their specific USB-Modbus dongle. If that's the case, then we can try and figure it out without their help as I suspect that their dongle won't be cheap.

Something else to try as I just noticed that you call ModbusRTUClient.begin() twice:

Update: I took your code and ran it on a MEGA2560 board with a couple of tweaks to the RS485 library specifically for the MEGA2560.

It successfully sends out Modbus messages to a slave device with address 1 and reads back 2 words. Word 1 should be 500 and word 2 should be 7000.

#include <ArduinoRS485.h>
#include <ArduinoModbus.h>

void setup() {
  Serial.begin(19200);

  Serial.println("Modbus Pyranometer Sensor");

  if (!ModbusRTUClient.begin(19200,SERIAL_8E1)) //Even Parity
  {
    Serial.println ("Failed to start Modbus RTU Client");
    while(1);
  }
}

void loop() 
{
  float p1 = pyrano1();
  delay(100);
  Serial.println(p1);
  delay(2000);
}

float pyrano1()
{
  Serial.print(F("\n\n"));
  float p1=0;
  if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x28, 0x02))
  {
    Serial.println("Failed to read registers");
    Serial.print(F("Last Error = "));
    Serial.println(ModbusRTUClient.lastError());
  }
  else
  {
    uint16_t word1 = ModbusRTUClient.read();  //read data from the buffer
    uint16_t word2 = ModbusRTUClient.read();
    Serial.print(F("Word 1 = "));
    Serial.println(word1);
    Serial.print(F("Word 2 = "));
    Serial.println(word2);
    uint32_t millivolt = (uint32_t)(word1) << 16 | word2; //bit math
    Serial.println( millivolt );
    p1 = millivolt/1000.0;
  }
  return p1;
}

The output on the serial monitor is now:

Word 1 = 500
Word 2 = 7000
32775000
32775.00


Word 1 = 500
Word 2 = 7000
32775000
32775.00

Give it a go with your real sensor and report back your progress.

Yes i believe so too, might be asking a quote from them to check their pricing on the dongle if its of reasonable pricing

ok got it, will change that in the code

Thank you so much for your help, i really appreciate it. I'll check again with the sensor and update you when i do

Update: Just tried it out on the sensor and it's still showing connection timed out.
Serial monitor output:

Failed to read registers
Last Error = Connection timed out
0.00

At this point, have a feeling it's more to the sensor rather than the code. Might be contacting Apogee for their technical support to make sure I'm interfacing the sensor correctly

You have 2 MKR boards stacked together so I think we can discount internal wiring. Your fritzing drawing shows a common ground with the sensor.

I guess it's an oversight but I can't see a supply to your MKR boards in your Fritzing diagram.

If you can get hold of a cheap USB-RS485 dongle you can monitor the RS485 bus and then determine if the MKR is transmitting correctly, or if the sensor is responding correctly.

oh, it is being powered by USB from the computer, also able to see the serial monitor through the connection

Yes definitely, will get a hold of one and look into it. Thank you for the help so far!

Hello, the USB-RS485 dongle came in and I managed to communicate with the sensor and its giving me a reading which is accurate.


Tx - Transmitting, Rx - Receiving
Tx values being transmitted are Slave addr: 0x01, read holding reg: 0x03, start addr: 0x28, no. of registers: 0x01

When i transferred these values over to Arduino

if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x28, 0x01))
 {
    Serial.println("Failed to read registers");
    Serial.print(F("Last Error = "));
    Serial.println(ModbusRTUClient.lastError());
  }

and

if (!ModbusRTUClient.requestFrom(0x01, 0x03, 0x28, 0x01))
  {
    Serial.println("Failed to read registers");
    Serial.print(F("Last Error = "));
    Serial.println(ModbusRTUClient.lastError());
  }

The first gave me an error of "Illegal Function" and the second gave an error of "Invalid CRC" even though the values transmitted are the same as the ones on the dongle.

Do you have any suggestions to solve this issue?

That's great. You now have confidence that the sensor is working as it should.

Are you sure that they are. Sometimes the register address is out by 1 - it's a Modbus "feature" from way back! You should be able to use your USB RS485 dongle to listen in on the RS485 bus. Any simple terminal program that can display Hex values should work nicely. There are several out the but for simplicity I prefer Hercules.

You should be able to determine if the Arduino is sending the same command message as the PC program did and if the sensor is responding to it or not.

Hello, an update, I got the Arduino to read the sensor values!

Followed your advice to listen to the values from the Arduino using the USB RS485 dongle and turns out it was missing a few bits at the back. Tried different formats to request the information but didn't work.
Out of options, I increased the baudrate and it started to receive the values from the sensor.

Thank you so much for your help with everything! Really appreciate it loads!

Great news! Glad you got it working.

The USB-RS485 dongle is a real time saver when trying to figure out what is or isn't happening on the RS485 bus.

If you are happy with the outcome, please mark the discussion as solved as others may benefit from the solution in the future.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.