element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
N-gaged Design Challenge
  • Challenges & Projects
  • Design Challenges
  • N-gaged Design Challenge
  • More
  • Cancel
N-gaged Design Challenge
N-Gaged Blog PVMonitor #3 - The great pretender (1)
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Leaderboard
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: amgalbu
  • Date Created: 4 May 2022 4:38 AM Date Created
  • Views 643 views
  • Likes 7 likes
  • Comments 0 comments
  • n-gaged design challenge
  • pvmonitor
  • iot system
  • OMEGA’s Layer N EcoSystem
  • n-gaged
  • remote monitoring
Related
Recommended

PVMonitor #3 - The great pretender (1)

amgalbu
amgalbu
4 May 2022
Blogs in this series
PVMonitor #1 - Unboxing and project description
PVMonitor #2 - Getting started
PVMonitor #3 - The great pretender (1)
PVMonitor #4 - The great pretender (2)
PVMonitor #5 - Installation and preliminary data
PVMonitor #6 - Anomaly detection
PVMonitor #7 - System performances
PVMonitor #8 - Anomaly detection (2)
PVMonitor #9 - Conclusions
 
 
This is the first of two blogs where we will see protocol converter, a device that will pretend to be an Omega OM240 device
 
1. Hardware
The protocol converter is based on a Raspberry board. The Delta Solivia inverter has an RS485 interface, so the only additional hardware required is a USB-to-RS485 converter. In my case, I am using this one, which has been successfully recognized by the Raspberry Pi board with no need for additional drivers
image
2. Modbus connectivity
As we said in a previous post, the Omega gateway can read data from another Omega device through Modbus protocol.
Modbus is a data communications protocol originally published by Modicon (now Schneider Electric) in 1979 for use with its programmable logic controllers (PLCs). Modbus has become a de facto standard communication protocol and is now a commonly available means of connecting industrial electronic devices. Modbus is popular in industrial environments because it is openly published and royalty-free. It was developed for industrial applications, is relatively easy to deploy and maintain compared to other standards, and places few restrictions on the format of the data to be transmitted.
Modbus is a master-slave protocol, meaning that one device (in this case the gateway) is in charge to send requests to a slave. Slaves never send unsolicited data: they just can answer requests sent by a master. Since multiple slaves can be present on a bus, an unique identifier (called slave id) is assigned to each slave. Slave ids range from 0 to 255 (however in some cases 0 or 255 are intended as broadcast addresses, so they are typically not used)
Modbus supports only  very few object types
  1. holding registers: these are "memory locations" that can store 16-bit values and can be read and written
  2. input registers: these are "memory locations" that can store 16-bit values and can only be read
  3. coils: these are "memory locations" that can store boolean values and can be read and written
  4. discrete inputs: these are "memory locations" that can store boolean values and can only be read
Modbus standards define also the commands (known as function codes) to operate on these objects. Main commands are
  • 3: Read multiple holding registers
  • 4: Read multiple input registers
  • 6: Write single holding registers
  • 16: Write multiple holding registers

Modbus frames can be sent over different media. When sent over a serial (typically RS485) bus, frames adhere the Modbus RTU specification. When sent over a network cable, the Modbus TCP protocol is used. Modbus RTU and Modbus TCP are very similar: we can think Modbus TCP frame as a Modbus RTU frame with an extract header (called MBAP)

image
3. Configuring Modbus protocol
The protocol converter I am going to implemented will be monitored by the gateway, so I need to add a new device. This can be done through the gateway local web interface as I already explained in a previous post. Just to recap, I added a new device with the following configuration
  1. Product family: This is the category the Omega device we want to integrate belongs to. I selected "DAQ" because, in this category, there are devices that can be configured to monitor a range of physical quantities
  2. Product model: I selected "OM240", an embedded data logger with 24 differential analog inputs
  3. Name: this is simply a string that you can use to easily identify the device. I left this filed empty
  4. Interface: I selected "TCP" because I want the protocol converter to be polled over my home Wifi network
  5. IP Address: this is the IP address of the protocol converter. I enter the static IP address of the protocol converter - 192.168.1.100
  6. Port: I left the default value (502), which the most commonly-used port for Modbus TCP communication
  7. Device ID: I entered 0, but any value is fine here, because my protocol converter will answer to all the requests
  8. Reading interval: this is how often the gateway will ask data to the protocol converter. 120 seconds (2 minutes) should be ok. Any value below this is useless because the Cloud N portal only accepts a refresh rate of up to 1 sample every 2 minutes
  9. Channel: this table contains the DAQ inputs we want to monitor. For each channel, you can enter a human-readable name, the Modbus register address (for OM240, this is calculated as 16 * <channel number>), the type (i.e. the physical quantity you are measuring) and the unit of measure (which is automatically selected when you choose the type). I added the following channels
    1. DC Power: power (in W) currently generated by the solar string (note that the "Add Device" form does not have an option for power-related measures, so I selected "Volts") 
    2. AC Power: power (in W) currently sent to the grid. This is typically less the DC power because of the losses due to conversion from DC to AC
    3. DC Temperature: temperature reading of the inverter's internal NTC sensor on the "DC side" 
    4. AC Temperature: temperature reading of the inverter's internal NTC sensor on the "AC side" 
image
Once completed, the list of devices looks like this
image

In the Cloud N portal, we see that devices configuration has been successfully propagated
image

4. Modbus registers

As I said, protocol converter will pretend to be an OM240. Thankfully, there is manual with the OM240 memory map. In particular, it is stated that for each sensor (analogs, digitals, multiplexers and virtual channels), 16 registers will be exported, namely

  1. Input A’s last acquired measure (Offset 0-1)
  2. Input b (Offset 2-3)
  3. sensor’s temperature (Offset 4-5)
  4. Acquisition’s timestamp (Offset 6-7-8-9-10-11-12-13-14)
  5. flags to show acquisition status and alarm status (Offset 15).
Measures are communicated in 32-bit float IEEE754 standard.
Timestamp is composed by 9 registers, with offset 6 (MSB) to 15 (LSB). Timestamp is an ASCII string 18 bytes long, in the following format: “dd/mm/yy hh:mm:ss"
Flag’s register is mapped as it follows:
  • bit 0: Sensor acquired
  • bit 1: A valid Input
  • bit 2: B valid Input
  • bit 3: Valid Temp input
  • bit 4: A alarm Input
  • bit 5: B alarm Input
  • bit 6: Temp alarm Input
In particular, the "Sensor acquired" bit should be set to 1 when sensor value has been acquired and a new data is available. SCADA (or Modbus Master) reads the updated register and reset it to 0 by using Coils Function code. In this way the system is ready to note the presence of a new acquisition. This bit is also accessible through read/write coils functions. Coils are mapped on flag “Sensor Acquired” having referral to base address of each sensor. To obtain Coil address of a sensor, Use the following formula:
    Coil Address = Base Address/10
    Base Address = 16 * Input channel number (0..23)

According to my test, the timestamp is actually ignored and the timestamp of the gateway (most probably) or the cloud server is used

This means that to "pretend" to be an OM240 I have at least to
  1. store the value in registers at offset 0 (MSB) and 1 (LSB)
  2. set flags register (at offset 15) to 3 (sensor acquired and input A valid)

5. Modbus implementation

The implementation is based on PyModbus library

To install the PyModbus library, you just have to use pip3 for PyModbus and Twister

pip3 install pymodbus
pip3 install twister

Then the implementation of the Modbus server is quite straightforward
The Omega gateway is the master in the Modbus communication, so the protocol converter needs to act as a slave (or server), meaning that it will sit waiting for requests from the gateway. Three steps are required to initialize the Modbus server

5.1 Initialize data store

Data store is where PyModbus library stores the values it will responds to the Modbus master.  

First of all, we need to define one or more data block. To define a data block, the ModbusSequentialDataBlock or ModbusSparseDataBlock functions are used. The difference is that the sequential has no gaps in the data while the sparse can

block = ModbusSequentialDataBlock(0x00, [0]*0xff)
block = ModbusSparseDataBlock({0x00: 0, 0x05: 1})

The first line of code creates a data block of 255 (0xff) elements starting from address 0x00. The second one creates two elements: one at address 0x00 initialized with value 0 and one at address 0x05 initialized with value 1Then, you can combine data blocks in a data store

store = ModbusSlaveContext(
    di=ModbusSequentialDataBlock(0, [17] * 100),
    co=ModbusSequentialDataBlock(0, [17] * 100),
    hr=ModbusSequentialDataBlock(0, [17] * 100),
    ir=ModbusSequentialDataBlock(0, [17] * 100))

The ModbusSlaveContext accepts a "slaves" parameter where you can define a list of unit ids the server will respond. Since we omitted that parameter, the server will respond to all the requests, thus ignoring the value of unit id

Also note that datastores only respond to the addresses that they are initialized to, therefore, if you initialize a data block to addresses of 0x00 to 0xFF, a request to 0x100 will respond with an invalid address exception. 

5.2 Initialize server information

You can configure some information about server that can eventually be queried by the Modbus master. I am not interested in this information, so I copied-and-pasted the values from one of the examples

identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = version.short()

5.3 Start the server

The Modbus server can now be launched. Several modes are supported (TCP, UDP, with TLS support, with different framers). To talk with the gateway, I need a basic server talking on a TCP channel

StartTcpServer(context, identity=identity, address=("", 502))
 

5.4 Initialization code

This is the full Modbus server initialization code

 

def run_server():
    # ----------------------------------------------------------------------- #
    # initialize data store
    # ----------------------------------------------------------------------- #
    store = ModbusSlaveContext(
        di=ModbusSequentialDataBlock(0, [0] * 100),
        co=ModbusSequentialDataBlock(0, [0] * 0xffff),
        hr=ModbusSequentialDataBlock(0, [0] * 0xffff),
        ir=ModbusSequentialDataBlock(0, [0] * 100))

    context = ModbusServerContext(slaves=store, single=True)

    # ----------------------------------------------------------------------- #
    # initialize the server information
    # ----------------------------------------------------------------------- #
    # If you don't set this or any fields, they are defaulted to empty strings.
    # ----------------------------------------------------------------------- #
    identity = ModbusDeviceIdentification()
    identity.VendorName = 'Pymodbus'
    identity.ProductCode = 'PM'
    identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
    identity.ProductName = 'Pymodbus Server'
    identity.ModelName = 'Pymodbus Server'
    identity.MajorMinorRevision = version.short()

    # ----------------------------------------------------------------------- #
    # run the TCP server
    # ----------------------------------------------------------------------- #
    log.info("Starting server")
    StartTcpServer(context, identity=identity, address=("", 502)) 

That's all for this post. In the next episode, I will talk about what's on the other side of the protocol converter: the Delta Solivia proprietary protocol

<< Prev: Getting started
Next: The great pretender (2) >>

  • Sign in to reply
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube