Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Modbus Plugin
#1
Due to the long distance and a higher number of stations, I would like to control the valves via modbus. I already have Arduino boards ready that can be controlled via modbus. From the Raspberry I can switch them on and of via modbus. Now I would like to use modbus commands (which are python function calls) instead of GPIO pins. What would be the best way? The most clean way would be to write a plugin. But I am not sure if that would be appropriate here as it would influence a central part of the control architecture.
Reply
#2
Are you using the minimalmodbus python module on the Raspi side?
https://minimalmodbus.readthedocs.io/en/master/

I am using this with great success on a raspberry Pi along with an RS485 USB adapter:
https://www.amazon.com/Gikfun-Serial-Con...85+adapter

It only needs one USB port on the Pi.

How are your Arduinos connecting to the serial bus?

I use an RS485 to RS232 adapter on the arduino side:
https://www.amazon.com/MAX485-Module-RS-...to+arduino

I am using the SimpleModbusSlave library on the arduino side:
https://drive.google.com/drive/folders/0...U1RQVBfSzg

It should be pretty easy to make a plugin for SIP using minimalmodbus.

Dan
Reply
#3
Yes, I am using minimalmodbus at the Raspi and SimpleModbusSlave at the Arduino.
The plugin actually only need to use the function calls instrument.write_register() instead of setting the GPIO pins.
For reading input values like the moisture sensor, it would need to use the function call instrument.read_register().

To be honest, I am not sure where to start. My python knowledge is more at beginners level. There are a lot of function calls in the OpenSprincler code at which the GPIO is referred.

My goal would be to use the minimalmodbus python functions instead of GPIO pins to enable/disable the valves.
In addition I also would like to add moisture sensors via minimalmodbus, but I guess up to now only rain sensors are supported so it would need a second plugin I guess, right?
Reply
#4
I will be happy to help you get this working.

What version of SIP (OpenSprinkler) are you using?  Newer versions of the software include a signalling capability that can send information to a plugin to tell it when stations are switched on or off.

I have been using MODBUS on a different but related project so I am familiar with MinimalModbus and SimpleModbusSlave.

Are you using relays to control your valves? I have some code I can share with you to help you get things working.

You are right that moisture sensors would need a separate plugin. I have an Arduino sketch that can read up to 8 sensors including moisture, temperature and humidity. The Arduino code I have is written for Arduino Micro/Leonardo but the only thing that would need to be changed for other Arduinos is the reference to the serial port (serial instead of serial1).

You can find the Arduino code on GitHub:
https://gist.github.com/Dan-in-CA

There is also some Python code for controlling relays over MODBUS.


Dan
Reply
#5
Thanks for your great input. I was quickly reading through your sample Arduino codes. They look very advanced :-) I think you did very similar things that I am planning to do.

Up to now I used the Raspberry controlling a 4 channel SainSmart Relay directly (via transistors) through GPIO pins and a simple time based shell script. But as I need to increase the number of valves and want to include sensors, GPIO does not scale as I need too many cables and the garden is big. I was looking for a robust 2 wire bus. While for a short time thinking about 1-wire, I finally ended up with RS485 and modbus. The last weeks I spent sorting out the proper library for both the Arduino and Raspberry. There are many different ones. But I like the minimal approach of MinimalModbus and SimpleModbusSlave.

I am actually just starting playing around with SIP. I am planning to use the current GIT version of the python port: https://github.com/Dan-in-CA/SIP

I am using SainSmart relays to control the valves. As I read they can be connected directly to the Arduino (200mA).

The signaling capability to the plugin sounds interesting. Is the number of valves limited by the number of GPIO pins by default? But I guess as a starting point that should be fine.
Reply
#6
Actually the number of valves is adjustable in SIP.
If you go to Options > Station handling and change the number of expansion boards. Each "expansion board" adds 8 stations so if you change the number of expansion boards from 0 to 1 you will now have 16 stations...

The Signaling capability is shown in the signalling examples plugin. Look in SIP/plugins/signaling_examples.py and you will see simple functions for each signal that SIP sends.

The function under ### valves ### receives a signal when SIP tries to start or stop a station/valve. That can be used in a plugin to send the modbus command to your slave arduinos.

The power_node.ino arduino code is used for controlling 4 or 8 relays. You will need to set the code for "active low" relays for the sainsmart relays (lines 17 and 18 in the code)

There is a relay_board plugin for SIP that you can use with your sainsmart relays:
https://github.com/KanyonKris/relay_board/wiki

You can install the plugin from Plugins > manage plugins > Browse more plugins. This will get you started with the valves close to your Pi.

Let me know how things are going.

Dan

 [url=https://gist.github.com/Dan-in-CA/c873883d734caea5ff52ad11d5875b36][/url]
Reply
#7
I played around with the signal plugin and finally got it working so far. Before I needed to resolve some issues with the cabling and some strange interaction with the serial monitor within the simplemodbus library.

What I did:

I only added one new line to the valves section of signalling_examples.py:

Code:
### valves ###
def notify_zone_change(name, **kw):
   print "zones changed"
   print gv.srvals
   #This is the new part for modbus interaction
   #gv.srvals is an array which contains the state of the valves
   modbus_control.write_to_slave(gv.srvals)


I creaded a module named modbus_control.py:

Code:
#!/usr/bin/env python
import minimalmodbus

#The Arduino library SimpleModbusSlave only supports function codes 3 and 16, which is reading (code 3)
#and writing (code 16) to Analog Output Holding Registers (register number 40001-49999)
#Therefore we will only use these two function codes:
#Within minimalmodbus we are using method "read_register" to read from the register, it will use
#function code 3 by default: read_register(REGISTERADDRESS,DECIMALNUMBER)
#To write we are using method "write_register" which is using function code 16 by default

#Initialize the modbus interface
instrument = minimalmodbus.Instrument('/dev/ttyUSB1', 1, 'rtu') # port name, slave address (in decimal)

# The following function writes the values received from the parent script to the modbus slave
# The variable ventil is an array with the status of the valves called from the parent script (e.g. signalling_examples.py)
def write_to_slave(ventil):
   print ("Function write_to_slave triggered with value: {}".format(ventil))

   #The following loop assumes a default amount of 8 valves inside the array ventil (default)
   #If the number has been changed from the default the loop need to be changed as well
   #Either implement a detection of array length (how many values are in the ventil array)
   #or exchange the number (here 8) manually
   i=8 #number of valves (equals to the number of entries in the array "ventil")
   x=i-1 #Counting starts from 0 in the array, therefore we need this additional step
   while i!=0:
       v=i-1
       #The following function writes the values to the modbus slave
       #i equals to the register number (starting with 1),
       #ventil[v] is the value for each valve, however this array starts with 0, therefore v instead of i
       instrument.write_register(i, ventil[v], 0) # Registernumber, value, number of decimals for storage
       i=i-1 #Decrement i by 1
Reply
#8
Looks like you have made a good start.

A few suggestions:
1. You may find that the address of the USB port on the Pi changes from time to time. This can cause some strange problems. You can set a persistent address for the port:
hintshop.ludvig.co.nz/show/persistent-names-usb-serial-devices/

2. Instead of looping to get 8 values from the ventil array you can use "slice".
http://stackoverflow.com/questions/50921...e-notation

for example if you have 16 values in the array you can do:
first_8 = ventil[:9]
second_8 = ventil[9:]

3. you can send all 8 values at once using modbus function 16:
instrument.write_registers(0, first_8). Also, simpleMosbusSlave v10 supports function 6.

The MODBUS specification calls for using twisted pair cable like phone or network cable. I have been using non-twisted pair wire (sprinkler wire) without a problem with lengths of over 100 feet (30 M).

Dan
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)