How to Access Live Measurement Data Using Modbus

by Paul Smart | Updated: 04/07/2016 | Comments: 8

Search the Blog


Subscribe to the Blog

Set up your preferences for receiving email notifications when new blog articles are posted that match your areas of interest.


Area / Application

Product Category

Activity

Corporate / News

Enter your email address:



Suggest an Article

Is there a topic you would like to learn more about? Let us know. Please be as specific as possible.

Leave this field empty

Modbus

Did you know that you can turn your Campbell Scientific datalogger into a Modbus TCP/IP slave? Do you know why that’s a good idea? Campbell Scientific dataloggers are commonly used as Modbus slave devices. This means that the datalogger is configured to implement the Modbus communications protocol and listen for Modbus polls from a Modbus Master, such as a SCADA (supervisory control and data acquisition) system. Turning your datalogger into a Modbus TCP/IP slave is a great way to allow systems that use the Modbus protocol to have access to your live measurement data.

To better understand how this works, we’ll go through an example exercise to show the basic concepts of how to implement the Modbus protocol on a CR1000 datalogger. We’ll use a CR1000 datalogger connected to a SCADA system. You may remember our previous discussion about SCADA systems and Modbus from the “Why Modbus Matters: An Introduction” blog article.

Modbus communication with analog measurement and datalogger to a SCADA system

Getting Started

In our example, the CR1000 is programmed to measure battery voltage, panel temperature, and an analog measurement in a simple program as shown below:


'Program for a CR1000 Series Datalogger

'Declare Public Variables
Public PTemp, batt_volt, analog_meas

'Data Table Definition
DataTable (Table1,1,-1) 
	DataInterval (0,10,Min,10)
	Minimum (1,batt_volt,FP2,0,False)
	Average (1,PTemp,FP2,False)	
	Average (1,analog_meas,FP2,False)
EndTable

'Main Program
BeginProg
	Scan (1,Sec,0,0)
		'Measure the datalogger panel temperature
		PanelTemp (PTemp,250)
		
		'Measure the battery voltage
		Battery (batt_volt)
		
		'Measure an analog voltage
		VoltSe (analog_meas,1,mV5000,1,1,0,_60Hz,1.0,0)
		
		'Call final storage table
		CallTable Table1
	NextScan
EndProg

Modbus Programming

The CR1000 datalogger in our example is connected to a SCADA system using an NL121 Ethernet Interface. Therefore, we need to program our datalogger to listen to Modbus polls on the appropriate communications port and to respond with our most current measured data. To do this, we need to use the ModbusSlave() instruction, declare a variable array to hold our Modbus data, and then update that array with our measurements.

This is accomplished using the code below:


'Program for a CR1000 Series Datalogger

'Declare Public Variables
Public PTemp, batt_volt, analog_meas

Public ModbusRegisters(3)
Public ModbusCoil As Boolean

'Data Table Definition
DataTable (Table1,1,-1) 
	DataInterval (0,10,Min,10)
	Minimum (1,batt_volt,FP2,0,False)
	Average (1,PTemp,FP2,False)    
	Average (1,analog_meas,FP2,False)
EndTable

'Main Program
BeginProg
  
	'Configure the datalogger as a Modbus Slave
	ModbusSlave (502,0,1,ModbusRegisters(),ModbusCoil,2)
    
		Scan (1,Sec,0,0)
			'Measure the datalogger panel temperature
			PanelTemp (PTemp,250)
                                
    			'Measure the battery voltage
			Battery (batt_volt)
                                
    			'Measure an analog voltage
    			VoltSe (analog_meas,1,mV5000,1,1,0,_60Hz,1.0,0)
                                
			'Populate Modbus Registers
 			ModbusRegisters(1) = PTemp
 			ModbusRegisters(2) = batt_volt
			ModbusRegisters(3) = analog_meas

			'Call final storage table
			CallTable Table1
		NextScan
EndProg

A Closer Look

Let’s take a closer look at the ModbusSlave() instruction we added to the program code. The instruction is added between the BeginProg and Scan statements. The ModbusSlave() instruction is placed between these two statements because it only needs to execute once at compile time rather than being executed during each scan. 

The ModbusSlave() instruction contains six parameters as shown below:


ModbusSlave (COMPort,BaudRate,ModbusAddr,ModbusVariable,BooleanVar,ModbusOption)

For reference, in our program code, our instruction with the parameters looks like this:


ModbusSlave (502,0,1,ModbusRegisters(),ModbusCoil,2)

Let’s take a closer look at those parameters and what they mean:

  • COMPort: This parameter defines the communications port over which the datalogger listens for Modbus polls. In our example, we use 502, because we are using an Ethernet connection and 502 is the default Modbus TCP port.

  • BaudRate: We use this parameter to set the baud rate of the communications port. In our example, this parameter (0) is ignored because our communications are over an Ethernet connection.

  • ModbusAddr: This parameter sets the Modbus address (or device ID) of the datalogger. In our example, we set our datalogger to have an address of 1.

  • ModbusVariable: We use this parameter to define the Modbus Register Map of our datalogger. (A Modbus Map defines the register addresses for the available data.)

    Measurements stored in this array are used as Modbus Input and Holding registers. (A register is a 16-bit memory location.) The datalogger does not distinguish between input and holding registers. Because we have declared our ModbusVariable as a floating point number (32 bits), it will be spread across two Modbus Registers (16 bits in each). In our example, our Modbus Map looks like this:

    Modbus Registers Measurement Description Units

    1,2

    Datalogger Panel Temperature

    Degrees Celsius

    3,4

    Datalogger Battery Voltage

    Volts

    5,6

    Analog Measurement

    Millivolts

  • ModbusBooleanVar: This parameter is used to define a Modbus Coil Map and holds the result if a Modbus master device sends a discrete on/off command to the datalogger (that is, 01 Read Coil/Port Status, 02 Read Input Status, 05 Force Single Coil/Port, 15 Force Multiple Coils/Ports).

    If a 0 is entered for this parameter, the discrete commands are mapped to the datalogger control ports C1 to C8, and a compile warning is generated. In our example, I have created a variable to act as a coil.

  • ModbusOption: This is an optional parameter that defines the data type of the Modbus variables and the byte order. In our example, I have chosen to format our Modbus Map floating point variables with the byte order ABCD.

Conclusion

The example exercise above outlines how you can accomplish a simple Modbus TCP/IP slave implementation by programming a CR1000 datalogger in CRBasic. Please note that the Short Cut program generator also has the capability to generate the code we have discussed above. By taking advantage of the concepts shown above, you can add more measurements to your program, as well as to the Modbus Register Map of your datalogger. The result is that your SCADA system can access live measurement data for a more complete weather station implementation.

You can look forward to blog articles in the future with more details on specific Modbus topics, common pitfalls, and best practices. In the meantime, share any Modbus comments or questions you have below.


Share This Article



About the Author

paul smart Paul Smart manages the Renewable Energy Group at Campbell Scientific, Inc., which focuses on providing monitoring and communication solutions for researchers, consultants, and project developers working in wind and solar energy. Paul has a bachelor’s degree in Electrical Engineering and an MBA. Away from the office, Paul enjoys the outdoors, fly fishing, and spending time with his family.

View all articles by this author.


Comments

raichlebw | 10/05/2016 at 05:41 PM

Hi,

I've been trying to set up a ModBus TCP server on a CR1000 with no success so far. I'm testing the server with an Obvius Acquisuite 8810 ModBus TCP client, which has not yet established communition with the logger/server.  We're pretty good with ModBus here, but not as good with network communication. 

The questions...

1) With the ComPort parameter of ModBusSlave set to 502, to which physical port should the NL121 be attached? I assume it's the RS-232 port. It's interestinng that, whichever of the 2 possible answers (RS-232, CS I/O) is the case, there will be ModBus TCP  transmitted over a serial cable.  

 2) With the ComPort parameter of ModBusSlave set to ComRS232, is the CR1000 a ModBus RTU server? This must be the case to avoid ambiguity with the TCP server, but I've seen no mention of RTU in any Campbell literature.

Thanks,

Brian

Paul Smart | 10/06/2016 at 03:23 PM

Hi Brian,

Are you using an NL201 rather than an NL121?  An NL121 will only connect to the peripheral port of the datalogger, whereas an NL201 can connect to the RS-232 port or the CSI/O port of the datalogger.

If you are using an NL201, then you will need to configure the NL201 either in bridge mode or as a Modbus TCP gateway.  If you configure it as a Modbus TCP gateway, then your datalogger will need to use the appropriate port (RS232 or CSI/O) and will function as an RTU.  If the NL201 is set up in bridge mode you will connect it to the CSI/O port of the datalogger and use port 502 in the ModbusSlave instruction.  RTU vs Modbus TCP/IP is determined by the port selection in the ModbusSlave instruction.  Make sure baud rates and ports in the ModbusSlave instruction match the settings of the NL201.

I would recommend taking a look at the settings for the NL201 using Device Configuration Utility. 

Please refer to the helps in Device Configuration Utility for information on the options available.  The helps explain all of this a lot better than I just did :)

I hope this helps,

-Paul

ariklee | 02/23/2018 at 04:25 PM

Hi Paul, couple questions making CR1000x function as Modbus slave over Ethernet:

1. Is port 502 set by the Modbus master? Should I verify with the client that they are indeed using 502, or can that not be changed?

2. Your ModbusCoil parameter is a single value boolean, while in Campbell's Application Note on Modbus Slave, they use an 8-value boolean array. I'm a bit confused on this. (In my application, the master should not be sending any commands 01, 02, 05, or 15. Does it matter then?)

3. Can you recommended any modbus master simulators for Windows 10 (besides Modbus Poll)?

Paul Smart | 02/28/2018 at 08:08 PM

Hi Ariklee,

Port 502 is the default port that is typically used.  The datalogger can use other ports, but you will need to confirm with the master that it is a match to the port that the datalogger is using.  

You can use the 8-value boolean array if you are expecting the master to set those coils in the datalogger.  However, in my program I was not expecting this, so I simply used a single value.  The only reason to do this was to avoid a warning when I compile the program.  If you are not using these coils, then it does not matter.

I've spoken to some of our customers that use ModScan as an alternative to Modbus Poll, but have not used it myself.  

-Paul

Geoprojektas | 03/11/2018 at 01:39 AM

Dear Paul,

Regarding ModBUS standard, measurements are stored in the Slave device in four different tables:

 

Device address:   Description:

1...10000            Coils outputs

10001...20000     Coils inputs

30001...40000     Input registers

40001...50000     Holding registers

 

Where will store the measurements ModbusRegisters() in your program?

On 40001-40006 registers?

 

Thanks,

Gintaris

Paul Smart | 03/12/2018 at 08:22 AM

Hi Gintaris,

Yes, that is correct.  The measurements will also populate the Input Registers.

-Paul

Geoprojektas | 03/13/2018 at 09:25 AM

Thanks, Paul

SteveA | 07/24/2018 at 05:42 PM

Paul,

You mention at the end of the article that the modbus code can be generated using ShortCut.  I've given it a first try, but haven't been able to see anyway to do that with ShortCut.  Is there something obvious I'm missing?

-Steve

Please log in or register to comment.


We're active on Social Media!
Stay informed with our latest updates by following us on these platforms: