Modbus Protocol for RS-232 with 32 bit values

I’m trying to use a particle photon (and eventually electron) in my graduate research project as a DIY IoT system for some Campbell Scientific CR300 data loggers of mine that are being used to log soil moisture at a remote research site. I will connect the photon to the data logger via RS-232 connection, specifically pins 2,3 and 5 (Rx, Tx, and Gnd).

I would like to use Modbus protocol to setup my data logger as a slave and the particle photon as the master. Unfortunately I don’t know enough about modbus to create my own library for the photon for this. I can setup my data logger just fine to be a slave with the functions of 01, 02, 03, 04, 05, 15, and 16 (see: Modbus Protocol Standards)

To add to the complication, the data logger sends values in 32 bit, so I was told by Campbell that I would need to use two registers per value to transfer data. I’m not savvy enough to know what this exactly means, but I think it’s important to mention here.

Can anyone point me to a modbus library that can be used on my photon to communicate with my datalogger?

Input is always appreciated!

Here’s a picture of my data logger deployed last season for you just for fun :slight_smile:

AJ

Have you searched this forum for the keyword “modbus”?

Yes I’ve looked, and everything I’ve found seems to be for an RS-485 connection and not an RS-232. I’m not sure if there’s a difference as far as the library is concerned though. I’m hoping that I can use one of those libraries, but I still don’t know how to handle the 32 bit value issue with multiple registers.

The difference between 485 and 232 will have very little impact on the libary. It will actually make it easier because you don’t have to worry about the “clear to send” aspect of 485. All you will need is a standard TTL to RS-232 chip to connect the Photon to your device. Don’t connect the 232 directly to your Photon’s RXTX as you will likely damage your Photon…

As for the 32 bit question, that just means they are using two Modbus registers for a single tag. I did not read the data loggers manual, but this is likely just a floating point number. Floats are always transferred by two consecutive 16 bit words with Modbus.

2 Likes

Wow that’s super helpful @adamg! Thank you for that information. Would something like this converter be what you’re talking about? I found some nice documentation on Sparkfun that really helped me understand RS-232 to TTL as well.

This is what I found in the modbus slave description of a program I made for the datalogger:

`-Sensor Documentation-

Modbus Slave
Modbus address set to 1
Campbell Scientific dataloggers do not differentiate between holding and input registers.
A Campbell Scientific datalogger will respond to a Modbus poll for a holding register (function code 03) or Input register (function code 04) with the same result
(e.g. if BattVolts is located in registers 1 & 2, the same value will be returned whether the Modbus poll is for registers 30001/30002 or 40001/40002).
The same goes for 16 bit values only instead of returning two registers one is returned.

Registers 1,2 assigned value from VWC
Registers 3,4 assigned value from EC
Registers 5,6 assigned value from T
Registers 7,8 assigned value from P
Registers 9,10 assigned value from PA
Registers 11,12 assigned value from VR`

VWC, EC, T, P, PA, and VR are the values of interest that I’m measuring. It would appear that you’re right, that it’s just a floating point number.

So now, how do I know if a modbus library will work with this? I don’t understand what I’d have to change in the code to make it assign the correct registers and such. I’ve found two libraries so far that I’m thinking about trying on my photon:

Number 1 by @peekay123
Number 2 by @peergum

Thank you so much for all the input.

“Mine” (derived from one for Arduino’s) has been working fine so far for Troll and Nuflo devices. I wish I had more time to document, but usage is quite straightforward.

Quick example:

ModbusMaster _node;

in setup():

_node.begin(1, Serial1); // if using slave address 1 and Serial1 to communicate
_node.setSpeed(9600); // or anything you want (usually 9600/19200 are the defaults)

in loop():

// example reading 2 words:
uint8_t result = _node.readHoldingRegisters(address,2); // address is holding register number
// result will be 0 is it works, an error code otherwise (see .h)
uint16_t a = _node.getResponseBuffer(0);
uint16_t b = _node.getResponseBuffer(1);

I wrote 2 libs for Nuflo and Troll, that I may publish later, depending on my client’s approval.

If you have any question/difficulty with the lib, let me know.

-phil

PS: if you read floats or decimals, check your fabricant’s manual on how they encode them. Chances are high you can use the FloatData structure defined in header file, you store each word in it, and read back the float value.

PPS: if you’re using RS232 instead of RS485, then you’re full duplex and you don’t need to switch between read and write as required for RS485, so you won’t need any specific preTransmission and postTransmission functions. You can define an idle function though, if you want to call Particle.process() while waiting for comm data for instance, to keep the particle connection from closing.

2 Likes

FYI, just updated the lib with the last version I’m using :slight_smile:

1 Like

Wow that’s incredibly helpful @peergum! I guess the only thing left to do is to order my TTL to RS232 converter and give it a try. I’ll be sure to keep updating this post as far as my progress and any other issues I may encounter. Once I have something working, I’ll be sure to upload it for everyone’s benefit as well.

Much appreciated :smile:

Hey everyone,

Here’s an update of what I’ve accomplished so far, and I have ran into trouble again!

I’ve decided to use the ModbusMaster library (found on GitHub) from the recommended libraries from particle simply due to the fact that it had a nice example already built for me. It too was adapted from an arduino library and updated for particle photon use. I’ve connected my photon to my data logger via an rs232 to TTL converter and have the wiring configuration as follows:

Datalogger Pin --> Photon Pin
RX --> TX
TX --> RX
GND --> GND
VCC --> 3V3

And this is the code that I’ve written for reading the modbus registers from the datalogger:

#include "ModbusMaster.h"

// instantiate ModbusMaster object as slave ID 1
ModbusMaster node(1);


void setup() {
	// initialize Modbus communication baud rate
	node.begin(115200);
	//node.enableTXpin(D7); //D7 is the pin used to control the TX enable pin of RS485 driver, not needed for my RS232 port
	//node.enableDebug();  //Print TX and RX frames out on Serial. Beware, enabling this messes up the timings for RS485 Transactions, causing them to fail.

	Serial.begin(9600);
	while(!Serial.available()) Particle.process();
	Serial.println("Starting Modbus Transaction:");
}


void loop() {
	static uint32_t i;
	uint8_t j, result;
	uint16_t data[10];

	i++;

	result = node.readHoldingRegisters(0x1,8);

	Serial.println("Done.");

	// do something with data if read is successful
	if (result == node.ku8MBSuccess) {
		Serial.print("Success, Received data: ");
		for (j = 0; j < 2; j++) {
			data[j] = node.getResponseBuffer(j);
			Serial.print(data[j], HEX);
			Serial.print(" ");
		}
		Serial.println("");
	} else {
		Serial.print("Failed, Response Code: ");
		Serial.print(result, HEX);
		Serial.println("");
		delay(5000); //if failed, wait for bit longer, before retrying!
	}
	delay(1000);
}

This code compiles just fine and uploads to my photon no problem, but doesn’t print anything on my serial monitor at all. I’m using TeraTerm as my monitor, and I’m able to get other programs to write to the monitor (such as a simple “Hello World” loop).

Does anyone see what could cause this to happen?
And as per usual, here are some pictures of updates for your information :slight_smile:

Just to make sure, you are sending some bytes to the Photon via TeraTerm in order to get out of this loop, right?

Thanks for the quick reply! As for an answer to you, I’m really not sure. I don’t think I am sending anything from TeraTerm back to the photon per say. I didn’t even know that TeraTerm could do that.

Would that mean that I could be caught in the particle.process() function because it’s waiting for information to call for data?

That could explain why it’s not even printing anything in the serial monitor, because it’s stuck in that loop. What kind of data needs to be sent to the photon to get it out of the particle.process() loop?

Not in Particle.process() but in the while() loop that keeps calling Particle.process() over and over. The reason for this line is to make sure the user has got time to connect to the device and look at the output.
But if you are not absolutely interested in the first few outputs you can just remove that line.

Anything, it's just as the "press any key" message known from computer programs :wink:

Thank you for that help, deleting the “while(!Serial.available()) Particle.process();” from setup allowed me to see my serial monitor again. From there I was able to start understanding what’s going on, but I’m stuck again unfortunately!

After turning on the debugging to see what was being sent, serial monitor returns this (note that I changed my “Done” print line into “Reading Registers…”):

And so I understand that error 02 is an illegal address, but I don’t understand what needs to change per say to make it acceptable.
Here is what my datalogger serial is recieving:


Here are the settings for the slave:

So there’s a total of 8 registers being read, and so in the example I use the function:
node.readHoldingRegisters(0x01,8);

Is this correct? What do you think might be causing my issue?

Sorry for being ignorant of so much of this stuff, I’m a soil scientist by trade, not a programmer! I’m barely scratching the surface with most of this programming.

Thanks for the help

Your response code is 0xE2 not 0x02.
0xE2 means this

	/**
	ModbusMaster response timed out exception.
	
	The entire response was not received within the timeout period, 
	ModbusMaster::ku8MBResponseTimeout. 
	
	@ingroup constant
	*/
	static const uint8_t ku8MBResponseTimedOut		   = 0xE2;

I was finally able to establish some connection between my photon and my data logger. I’ve made everything run at baud rate of 9600 as well (node and serial). I’ve adjusted my datalogger accordingly.

I have a new issue however: my connection is giving me a pattern of errors each reading. Here is the serial output on the photon:

TX: 1 3 0 1 0 8 15 CC
RX: 1 0 8 15 CC
Reading Registers...
Failed, Response Code: E1
TX: 1 3 0 1 0 8 15 CC
RX: 3 0 1 0 8
Reading Registers...
Failed, Response Code: E0
TX: 1 3 0 1 0 8 15 CC
RX: CC 1 3 0 1
Reading Registers...
Failed, Response Code: E0
TX: 1 3 0 1 0 8 15 CC
RX: 8 15 CC 1 3
Reading Registers...
Failed, Response Code: E0
TX: 1 3 0 1 0 8 15 CC
RX: 1 0 8 15 CC
Reading Registers...
Failed, Response Code: E1

This pattern repeats indefinitely. It would appear that the baud rates don’t match perhaps? I’m not exactly sure what that issue could be. Here is the serial output from the photon strung together horizontally for you to help identify a pattern. I’ve separated each request with a pipe symbol “|”

TX (from Photon): 1 3 0 1 0 8 15 CC | 1 3 0 1 0 8 15 CC | 1 3 0 1 0 8 15 CC | 1 3 0 1 0 8 15 CC |

RX (to Photon)  : 1 0 8 15 CC | 3 0 1 0 8 CC | 1 3 0 1 8 15 CC | 1 3 1 0 8 15 CC |

Here is the serial output from my datalogger so you can see what it’s sending and receiving:

It looks like it is receiving the message clearly, but for some reason send back the wrong function address to the photon (the “83”).

What do you think?
-AJ

From my own experience, what you’re getting is noise in your signal. Try using a shielded twisted pair between your RS and the the sensor/device. You may possibly also need to device the parity for Serial1.

Just my 2 cents. No guarantees :slight_smile:

I haven’t thought about using parity. That could be something to look into. I think I found a potential cause of my issue however.

I checked my line for shorts and there are none, so that’s a good thing. I powered the RS232 connector with a 5 volt source at the VCC and GND instead of drawing 3.3v power from the photon. I think my RS232 to TTL needed to use 5v logic. Here is the link to it if you’re interested in confirming that for me. This really helped! Now, both my datalogger and my photon serial outputs show the same thing consistently.

My datalogger shows this:
RX: 01 03 00 01 00 08 15 CC
TX: 01 83 02 C0 F1

And Photon shows this:
TX: 1 3 0 1 0 8 15 CC
RX: 1 83 2 C0 F1

And that gives me an error 0x02 which is a Modbus protocol illegal data address exception. So perhaps my datalogger is not receiving the correct format of numbers to read registers? Could this be why it’s generating the “00’s” in front of the “01” and “08”?

If your reads are consistent, you solved the main issue. the illegal address can come from you trying to read a word where the register contains a 32bit number (then you may need to read 2 words instead), or generally if you read part of the register (e.g. 1 word for a float on 32 bits).

Some modbus implementations seem to accept if you ask for 1 register and send you 2 words back if the register is 32 bits, some require you to ask for 2 words instead. check the device manual for clues.

if you read a string, you may have to request the exact number of words the string is implemented in. Trick is, some device implement 2 chars in a word, some implement a UTF-8 in a word… good luck with that :confused:

also, you may use the wrong address (hex addresses are usually given 0-based, whereas their decimal counterpart, e.g. 8001, 9001,… are 1-based). modbus is pretty tricky for that. At least that’s what I have learned by experience since last summer :slight_smile:

1 Like

Regarding the wiring part, you have to be careful with applying the RX from your 5v-powered converter to the electron’s RX, because you may damage the electron. at the minimum, you should use 2 resistors to limit the voltage to 3.3.

the issue that arises when you power with 3.3 is that the signal may be too weak for your external device to recognize properly. RS-232 signals can go as high as 15V if I remember well…

Great News! I was finally able to establish a successful connection between my photon and my data logger. I learned from the manufacturer of the data logger that their device manuals indicate that the first register that is mapped is number 1, but that’s actually not the true case.

When I use my library, I need to start at register 0 and not 1 because that is real way that the data logger maps the registers. They don’t say that in their manual because they’ve tried to make it easier for their customers with their loggers acting as slaves and masters, and when you use their equipment, it takes care of this for you.

So I changed readHoldingRegisters(0x01,8) to readHoldingRegisters(0x00, 8) and now I’m able to have a successful transmission. I’ve even removed my 5v power supply and went back to 3.3v via photon usb and I’m still able to have successful communications with no noise.

My next step is to begin arranging the data and uploading it to a cloud database where I can access my data. This part will be relatively easy and straightforward comparatively.

Further improvements may be:
-Changing the data logger to send 32 bit values instead of 16 bit (as currently) and trying to put these two separate register values together with high and low word functionality for my data.
-Reading and partitioning 12 or 24 registers instead of just 8 (as I will like to have 3 to 6 sensors at each data logger)
-Testing to see if I can power my electron via the usb from the data logger instead of buying a battery shield for each one

Thanks so much! I’ve made so much progress with all of your help.

I’ll upload the final code to my github as soon as polish it some :slight_smile:

2 Likes