Dynamic Data Acquisition

There are many methods to acquire and send data for industrial systems with a range of proprietary and community-based standards that facilitate exchange of information between instruments, a programmable logic controller (PLC), a distributed control system (DCS), and other systems that measure, analyze, or optimize the system. Exchange of information is increasingly important, particularly for optimization solutions, as availability of information is the foundation for many automation and optimization methods. Of particular interest to this course are the methods to retrieve data, run parameter estimation or optimization algorithms, and then either display advisory results or implement a solution back into the process.

Common Communication Standards


ASCII or RTU MODBUS is an older standard than OPC and has several limitations that motivated the creation of OPC. Although it is an old standard, several legacy pieces of equipment still support this protocol. There are several serial or network connection possibilities including RS232, RS422, RS485 (serial) or TCP/IP (network).

This example code is a Modbus server and client that communicate using pymodbus. The code writes 115 values to integer registers. The registers start at address 40001.

# Modbus server (TCP)
from pymodbus.server import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

def run_async_server():
    nreg = 200
    # initialize data store
    store = ModbusSlaveContext(
        di=ModbusSequentialDataBlock(0, [15]*nreg),
        co=ModbusSequentialDataBlock(0, [16]*nreg),
        hr=ModbusSequentialDataBlock(0, [17]*nreg),
        ir=ModbusSequentialDataBlock(0, [18]*nreg))
    context = ModbusServerContext(slaves=store, single=True)

    # initialize the server information
    identity = ModbusDeviceIdentification()
    identity.VendorName = 'APMonitor'
    identity.ProductCode = 'APM'
    identity.VendorUrl = 'https://apmonitor.com'
    identity.ProductName = 'Modbus Server'
    identity.ModelName = 'Modbus Server'
    identity.MajorMinorRevision = '3.0.2'

    # TCP Server
    StartTcpServer(context=context, host='localhost',\
                   identity=identity, address=("", 502))

if __name__ == "__main__":
    print('Modbus server started on localhost port 502')

# Modbus client
from pymodbus.client import ModbusTcpClient as ModbusClient
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.payload import BinaryPayloadDecoder
import time

print('Start Modbus Client')
client = ModbusClient(host='', port=502)
reg=0; address=0

# initialize data
data = [0.1,1.1,2.1,3.1,4.1]

for i in range(10):
   print('-'*5,'Cycle ',i,'-'*30)

   # increment data by one
   for i,d in enumerate(data):
      data[i] = d + 1

   # write holding registers (40001 to 40005)
   builder = BinaryPayloadBuilder(byteorder=Endian.Big,\
   for d in data:
   payload = builder.build()
   result  = client.write_registers(int(reg), payload,\
              skip_encode=True, unit=int(address))

   # read holding registers
   rd = client.read_holding_registers(reg,len(data)).registers


See MODBUS Transfer on Data-Driven Engineering for additional information and examples.


OLE (Object Linking and Embedding) for Process Control (OPC), was developed in 1996 by an industrial automation group based on the need for a common platform for exchanging data. The OPC standard details the communication of real-time or historical plant data between control devices and computers. OPC is sometimes referred to as "Oh, Please Connect" with frequent difficulty in connecting to various computers with incorrect DCOM settings. A recent effort termed OPC-UA or Unified Architecture, is a new implementation of the software that allows communication to devices other than the Windows OS platform. In November 2011, the OPC Foundation (body primarily responsible for the OPC standard) officially renamed OPC to mean "Open Platform Communications".

This example code Show OPC DA Example Code writes to an OPC Server with an OPC DA Client with OpenOPC. The code writes 16 values to 16 individual OPC tags on a Kepware Server.

# #######################################
# OPC write
# #######################################
    # OPC connection
    import OpenOPC

    Load1_avg  = opcm[0][0]
    Load2_avg  = opcm[0][1]
    Load3_avg  = opcm[0][2]
    Load4_avg  = opcm[0][3]
    Load1_max  = opcm[1][0]
    Load2_max  = opcm[1][1]
    Load3_max  = opcm[1][2]
    Load4_max  = opcm[1][3]
    Load1_min  = opcm[2][0]
    Load2_min  = opcm[2][1]
    Load3_min  = opcm[2][2]
    Load4_min  = opcm[2][3]
    Load_T12   = opcm[3][0]
    Load_T21   = opcm[3][1]
    Load_T32   = opcm[3][2]
    Load_T41   = opcm[3][3]

    opc.write( ('Channel2.Device1.T_12_Load_AVG',Load1_avg) )
    opc.write( ('Channel2.Device1.T_21_Load_AVG',Load2_avg) )
    opc.write( ('Channel2.Device1.T_32_Load_AVG',Load3_avg) )
    opc.write( ('Channel2.Device1.T_41_Load_AVG',Load4_avg) )
    opc.write( ('Channel2.Device1.T_12_Load_MAX',Load1_max) )
    opc.write( ('Channel2.Device1.T_21_Load_MAX',Load2_max) )
    opc.write( ('Channel2.Device1.T_32_Load_MAX',Load3_max) )
    opc.write( ('Channel2.Device1.T_41_Load_MAX',Load4_max) )
    opc.write( ('Channel2.Device1.T_12_Load_MIN',Load1_min) )
    opc.write( ('Channel2.Device1.T_21_Load_MIN',Load2_min) )
    opc.write( ('Channel2.Device1.T_32_Load_MIN',Load3_min) )
    opc.write( ('Channel2.Device1.T_41_Load_MIN',Load4_min) )
    opc.write( ('Channel2.Device1.T_12_Load_INST',Load_T12) )
    opc.write( ('Channel2.Device1.T_21_Load_INST',Load_T21) )
    opc.write( ('Channel2.Device1.T_32_Load_INST',Load_T32) )
    opc.write( ('Channel2.Device1.T_41_Load_INST',Load_T41) )

    print 'OPC communication failed'

The newer OPC UA (Unified Architecture) is platform independent and does not rely on the DCOM communication. It is more secure and includes functionality that is in OPC DA (Data Access) and OPC HDA (Historical Data Access).

SQL Client/Server

SQL (Structured Query Language) is a programming language popular with enterprise and web applications that requirement storage and access to historical or real-time data. It is not commonly used for process control or automation like MODBUS or OPC but the large user base of SQL programmers and requirements for big-data suggests that SQL may become a more popular platform in the future.

See the Data-Driven Engineering course for additional information on SQL in Python.

Data Acquisition and Communication

There are several hardware platforms that allow data acquisition and software platforms that facilitate communication.

Robot Operating System (ROS)

ROS is a C++ or Python library that allows inter-process communication. It can be run on anything from a desktop computer to an Arduino (at least in part). Its strength comes from the many different types of ROS packages that already exist and allow for sensors and actuators to communicate.


Several micro-processors, such as the Raspberry PI and the Beagle Bone Black, have been developed to allow on-board data acquisition and processing. These credit-card sized platforms run a full version of Linux with ARM processors several digital and analog inputs and outputs.


Instrumentation and Control Course at Notre Dame

The Arduino is a popular micro-controller that allows data acquisition, limited on-board processing, and output capabilities. With a large developer community and supported sensors, this platform is a popular choice for data acquisition and automation applications. The Temperature Control Lab (TCLab) connects to a computer with a serial USB connection with MATLAB or Python. The serial USB connection sends commands (heater level, LED level) and receives data (temperature) to enable real-time control.