RS485

RS-485 interface

The RS485 is commonly used to connect many devices (usually up to 32 unit loads) in a multidrop line in harsh environments.

What is a RS485 ?   What is a DMX512 ? What is a Modbus ?

Two RS485 lines are available on CM3-Home model. The lines are opto-isolated from the CPU and available on screw terminals as shown below.

 
How to use the RS485 ports

The ports are visible by Linux on /dev/ttyUSB0 (left port) and /dev/ttyUSB2 (right port) and can be managed by software like any other serial line. The DE signals required to get the bus are managed by the hardware on FTDI FT4232 chip.

The examples supplied refer to the three main uses of the RS485 bus:

  • Generic serial devices
  • Modbus
  • DMX512

Generic serial device

The example supplied drives a KMTronic eight channel RS485 relay controllerwith no protocol at all.

The CM3-Home left RS485 bus can be directly connected to one or more KMTronic or similar devices via a twisted pair.
The program is configured to drive a device with the lower address with Classic UI (or mobile App):

With the Basic UI:

or with a panel:

Configuration

It uses the Serial Binding to communicate. The port to use must be added to the java environment (in this case /dev/ttyUSB2) in /etc/defaults/openhab2

EXTRA_JAVA_OPTS="-Dgnu.io.rxtx.SerialPorts=/dev/ttyUSB0:/dev/ttyUSB2:/dev/ttyS0:/dev/ttyS2:/dev/ttyACM0:/dev/ttyAMA0"

The items/kmt.items file defines the items to be used

Switch kmt1 "KMTronic A1" (kmtA)
Switch kmt2 "KMTronic A2" (kmtA)
Switch kmt3 "KMTronic A3" (kmtA)
Switch kmt4 "KMTronic A4" (kmtA)
Switch kmt5 "KMTronic A5" (kmtA)
Switch kmt6 "KMTronic A6" (kmtA)
Switch kmt7 "KMTronic A7" (kmtA)
Switch kmt8 "KMTronic A8" (kmtA)

String toSerial "To Serial [%s]" {serial="/dev/ttyUSB2@9600"}

Group:Switch:OR(ON, OFF) kmtA "KMTronic"

The switch item is needed to create the push button and trigger the rules.
The string item is needed to transmit serial data.

To show them in standard UI, configure the sitemaps/cm3home.sitemap:

Frame label="RS485 KMTronic"
{
   Text label="Relays" icon=switch
   {
      Switch item=kmt1
      Switch item=kmt2
      Switch item=kmt3
      Switch item=kmt4
      Switch item=kmt5
      Switch item=kmt6
      Switch item=kmt7
      Switch item=kmt8
      Switch item=kmtA
   }
}

This RS485 relays controller device is driven simply by sending three ASCII characters strings

Relay commands:
OFF command : FF xx 00 (HEX) or 255 xx 0 (DEC)
ON command : FF xx 01 (HEX) or 255 xx 1 (DEC)

to be composed according to this table

KMTronic commands

java uses unicode character set. The output string must be formatted using 16 bit \u format.

in rules/kmt.rules file are defined all the rules to send the ASCII command string corresponding to the button pushed

rule "kmtA1_ON"
when
   Item kmt1 received command ON
then
   toSerial.sendCommand("\u00FF\u0001\u0001")
end

rule "kmtA1_OFF"
when
   Item kmt1 received command OFF
then
   toSerial.sendCommand("\u00FF\u0001\u0000")
end

rule "kmtA2_ON"
when
   Item kmt2 received command ON
then
   toSerial.sendCommand("\u00FF\u0002\u0001")
end

rule "kmtA2_OFF"
when
   Item kmt2 received command OFF
then
   toSerial.sendCommand("\u00FF\u0002\u0000")
end

rule "kmtA3_ON"
when
   Item kmt3 received command ON
then
   toSerial.sendCommand("\u00FF\u0003\u0001")
end

rule "kmtA3_OFF"
when
   Item kmt3 received command OFF
then
   toSerial.sendCommand("\u00FF\u0003\u0000")
end

rule "kmtA4_ON"
when
Item kmt4 received command ON
then
   toSerial.sendCommand("\u00FF\u0004\u0001")
end

rule "kmtA4_OFF"
when
   Item kmt4 received command OFF
then
   toSerial.sendCommand("\u00FF\u0004\u0000")
end

rule "kmtA5_ON"
when
   Item kmt5 received command ON
then
   toSerial.sendCommand("\u00FF\u0005\u0001")
end

rule "kmtA5_OFF"
when
   Item kmt5 received command OFF
then
   toSerial.sendCommand("\u00FF\u0005\u0000")
end

rule "kmtA6_ON"
when
   Item kmt6 received command ON
then
   toSerial.sendCommand("\u00FF\u0006\u0001")
end

rule "kmtA6_OFF"
when
   Item kmt6 received command OFF
then
   toSerial.sendCommand("\u00FF\u0006\u0000")
end

rule "kmtA7_ON"
when
   Item kmt7 received command ON
then
   toSerial.sendCommand("\u00FF\u0007\u0001")
end

rule "kmtA7_OFF"
when
   Item kmt7 received command OFF
then
   toSerial.sendCommand("\u00FF\u0007\u0000")
end

rule "kmtA8_ON"
when
   Item kmt8 received command ON
then
   toSerial.sendCommand("\u00FF\u0008\u0001")
end

rule "kmtA8_OFF"
when
   Item kmt8 received command OFF
then
   toSerial.sendCommand("\u00FF\u0008\u0000")
end

The character set installed by default on Openhabian doesn’t manage characters beyond 0x3F code. It must therefore be changed in en_US ISO-8859-1.
This operation can be executed with openhabian-config, but this operation doesn’t work immediately in the current version. You must delete the /etc/default/locale before using the menu item:

30 | System Settings
32 | Set System Locale
Links

Modbus

The example supplied with the SD card is set to read an Eastron SDM120 energy meter with Modbus protocol and show the main line electric parameters.

The energy values can be displayed on:

Classic UI

Basic UI

Or in the Habpanel

The system is preconfigured to blink the RGB led with green light if the power consumption is below 2.5kWh, blue between 2.5kWh and 3.0kWh and red if the power exceeds the usual 3kWh contractual limit. In this alarm condition the left relay is activated giving the possibility to switch on a beeper, a siren or whatever for a better notification of the possible near future fault of the main power line.

The power limits can be modified editing the rules/modbus.rules file.

Configuration

Once the Modbus binding is installed, the usual three files are available .

The default configuration of the SDM120 has not been modified. The details about the configuration and the registers exposed by the device are linked below. The system device used in this example is /dev/ttyUSB0, related to the most left RS485 socket on the board.

services/modbus.cfg

#the serial object has been attached to the RS485 port through the /dev/ttyUSB0 device
#the SDM120 default speed is 2400bps
#the handshake is in RTU mode
serial.Voltage.connection=/dev/ttyUSB0:2400:8:none:1:rtu:35:1500:none:none
#this is an input device
serial.Voltage.type=input

#device ID = 1
serial.Voltage.id=1

#the starting register for the Voltage variable is 0 (device dependant)
serial.Voltage.start=0

#the variable is two 16bit world lenght (4 byte)
serial.Voltage.length=2

#it's a float32 variable
serial.Voltage.valuetype=float32

#for each other variable it needs a similar configuration set

serial.Current.connection=/dev/ttyUSB0:2400:8:none:1:rtu:35:1500:none:none
serial.Current.type=input
serial.Current.id=1
serial.Current.start=6
serial.Current.length=2
serial.Current.valuetype=float32

serial.Power.connection=/dev/ttyUSB0:2400:8:none:1:rtu:35:1500:none:none
serial.Power.type=input
serial.Power.id=1
serial.Power.start=12
serial.Power.length=2
serial.Power.valuetype=float32

serial.CosFi.connection=/dev/ttyUSB0:2400:8:none:1:rtu:35:1500:none:none
serial.CosFi.type=input
serial.CosFi.id=1
serial.CosFi.start=30
serial.CosFi.length=2
serial.CosFi.valuetype=float32

serial.Freq.connection=/dev/ttyUSB0:2400:8:none:1:rtu:35:1500:none:none
serial.Freq.type=input
serial.Freq.id=1
serial.Freq.start=70
serial.Freq.length=2
serial.Freq.valuetype=float32

serial.Ener.connection=/dev/ttyUSB0:2400:8:none:1:rtu:35:1500:none:none
serial.Ener.type=input
serial.Ener.id=1
serial.Ener.start=72
serial.Ener.length=2
serial.Ener.valuetype=float32

On this example we read the parameters for voltage, current, active power, power factor (CosFi), frequency and energy for the phase 1 from registers 0, 6, 12, 30, 70 e 72 .

Now we can associate the items to these parameters, the key is the name of the objects created in modbus.cfg.
The items related to the desired variables must be formatted for the printout similarly to the printf syntax.

items/modbus.items

Number L1V "Tensione [%.1f V]" {modbus="Voltage:0"}
Number L1A "Corrente [%.1f A]" {modbus="Current:0"}
Number L1P "Potenza A [%.1f W]" {modbus="Power:0"}
Number L1C "Cos Fi [%.2f]" {modbus="CosFi:0"}
Number L1F "Frequenza [%.1f Hz]" {modbus="Freq:0"}
Number L1E "Energia A [%.1f kWh]" {modbus="Ener:0"}

To show the values on the GUI, the variables must be associated to the sitemap. The association is possible through the name of the items.

sitemaps/cm3home.sitemap

sitemap knx label="GUIOTT home" 
{
...
Frame label="Energia"
{
   Text label="Energia" icon=pressure
   {
      Text item=L1V valuecolor=[>240="red",>230="orange",>220="green",<=220="orange"] Text item=L1A Text item=L1P valuecolor=[>3300="red",>3000="orange",>2500="green",<=2500="blue"] Text item=L1C valuecolor=[>0.8="green",<=0.8="orange"]
      Text item=L1F valuecolor=[<49="orange",>49="green",>51="orange"]
      Text item=L1E
   }
}
}

Now the service starts polling the energy meter to collect data with the polling time configured on services.cfg file, updating the values on the various User Interfaces.

The knob widget can be used as instruments, disabling interaction:

Can also be enabled the measure range of the instrument, so the bar change its color depending on some alarm thresholds:


The values collected can be used to obtain, for example, some audible or visible alarms as in the example below:

rules/modbus.rules

var Cmd="python /etc/openhab2/scripts/led.py "
var Timer set_timer = null
rule "Led"
when
   Time cron " 0/3 * * ? * * *"
then
   var Power = (L1P.state as DecimalType).intValue
   if (Power <= 2500) 
{
//val results = executeCommandLine(Cmd+"G",5000)
//logInfo("Exec",results) sendCommand(LedG, OFF)
sendCommand(LedB, ON)
sendCommand(LedR, ON)
set_timer = createTimer(now.plusSeconds(0.1))
[
sendCommand(LedG, ON)
set_timer = null
]
}
else if (Power > 2500 && Power <= 3000)
{
//val results = executeCommandLine(Cmd+"B",5000)
//logInfo("Exec",results)
sendCommand(LedB, OFF)
sendCommand(LedG, ON)
sendCommand(LedR, ON)
set_timer = createTimer(now.plusSeconds(0.1))
[
sendCommand(LedB, ON)
set_timer = null
]
}
else if (Power > 3000)
{ //val results = executeCommandLine(Cmd+"R",5000) //logInfo("Exec",results) sendCommand(LedR, OFF) sendCommand(LedB, ON) sendCommand(LedG, ON) sendCommand(Rele1, ON) set_timer = createTimer(now.plusSeconds(0.1)) [ sendCommand(LedR, ON) set_timer = null ] set_timer = createTimer(now.plusSeconds(1)) [ sendCommand(Rele1, OFF) set_timer = null ] } end

In this case the RGB LED blinks with different color when some electrical power thresholds have been exceeded. Furthermore one of the two relays is used to switch a buzzer, alerting with more evidence that the contractual power threshold has been exceeded. This is one of the probably most useful function, hearing the beep one can immediately switch off the appliance in use, avoiding to go downstairs to reset the main breaker.

Links

Driving a DMX-512 bus with a CM3-Home RS485 port

The DMX512, often shortned as DMX (Digital MultipleX), is a digital communication standard mainly used to control scene lighting in the entertainment industry, to control numerous lights and effects from a central console. Recently it was introduced also in the civil field for architectural lighting.

Here an example about realizing such a protocol with some Python code that can be launched by OpenHAB with an executeCommandLine instruction in a rule.

The DMX512 bus is basically an RS485 serial comm @ 250 kbps 8N2

Python code

It opens the /dev/ttyUSB2 RS485 port and sends three commands sequences towards all the 512 possible devices addressable on a DMX512 bus.
This example has been tested with a VDPL300CB device that is configured to reply to addresses from 0 to 5 (A001)

The test program lights on in sequence the Red, Green and Blue LEDs.

import serial
import time
ser = serial.Serial("/dev/ttyUSB2")
ser.baudrate = 250000
ser.bytesize = serial.EIGHTBITS
ser.parity = serial.PARITY_NONE
ser.stopbits = serial.STOPBITS_TWO
ser.xonoff = False

def sendCmd(red,green,blue):
    ser.send_break(duration=0.25)
    ser.write(chr(0))

    ser.write(chr(0))
    ser.write(chr(red))
    ser.write(chr(green))
    ser.write(chr(blue))
    ser.write(chr(0))
    ser.write(chr(0))

    for i in range(512-6):
        ser.write(chr(0))

    ser.flush()

while True:
   for red in range (0,255,50):
      sendCmd(red,0,0)

   for green in range (0,255,50):
      sendCmd(0,green,0)

   for blue in range (0,255,50):
      sendCmd(0,0,blue)

ser.close()

Share