Thomas Buyukkilic
Thomas Buyukkilic

Reputation: 21

Serial communication with Uponor Smatrix Base X-245

I'm trying to communicate with a Uponor Smatrix Base X-245 using a use a Raspberry Pi 3B+ over the serial port (UART), without much luck.

Here is a sample output from the UART (/dev/ttyS0) using pyserial:

import serial
ser = serial.Serial("/dev/ttyS0", baudrate=19200)
while True:
    received_data = ser.read()
    sleep(0.025)
    data_left = ser.inWaiting()
    received_data += ser.read(data_left)
    print (received_data.hex()) 

Data without any thermostats (8) connected:

00  00  00  00  00  00  00  00  00  00  00  00  00  00  c0
00  00  00  00  00  00  00  00  00  00  00  00  00  00  c0
00  00  00  00  00  00  00  00  00  00  00  00  00  00  c0
dd  e7  c9  6b  01  0a  2e  fe
dd  e7  af  83  01  56  89  ff
dd  e7  f5  33  01  bf  36  fe
dd  e7  f7  6d  01  ce  f6  fe
dd  e7  f7  2b  01  aa  36  fe
dd  e7  f7  75  01  da  f6  fe
dd  e7  f7  49  01  fe  76  ff
dd  e7  f5  45  01  99  f6  fe

Data with all thermostats (8) connected:

00  00  00  00  00  00  00  00  00  00  00  00  00  00  c0
00  00  00  00  00  00  00  00  00  00  00  00  00  00  c0
00  00  00  00  00  00  00  00  00  00  00  00  00  00  c0
dd  e7  c9  6b  01  0a  2e  fe  77  5e  6e  e3  ed  db  83  ff  ff  81  e7  fb  87  ff  6f  89  fb  3d  7b  99  ff  75  53  fe
dd  e7  c9  6b  a5  01  01  85  ff  ff  e7  ff  b7  91  fd  cb  8f  f9  93  89  fb  3d  87  ff  6f  95  ff  ff  8d  2f  ff  
dd  e7  af  83  01  56  89  ff  77  de  6d  e4  ed  cb  83  ff  ff  81  e7  fb  87  ff  6f  89  fb  85  7b  99  ff  0b  07  ff
dd  e7  af  83  a5  01  01  85  ff  ff  e7  ff  b7  91  fd  cb  8f  f9  93  89  fb  85  87  ff  6f  95  ff  ff  25  3b  fe  
dd  e7  f5  33  01  bf  36  fe  77  5e  df  d3  ed  eb  83  ff  ff  81  e7  fb  87  ff  6f  89  fb  85  7b  99  ff  71  0f  ff
dd  e7  f5  33  a5  01  01  85  ff  ff  e7  ff  b7  91  fd  cb  8f  f9  93  89  fb  85  87  ff  6f  95  ff  ff  d3  fb  ff  
dd  e7  f7  6d  01  ce  f6  fe  f7  fe  fc  fe                                                                              
dd  e7  f7  6d  a5  01  01  85  ff  ff  e7  ff  b7  91  fd  cb  8f  f9  93  89  fb  79  87  ff  6f  95  ff  ff  15  d3  fe  
dd  e7  f7  2b  01  aa  36  fe                                                                                              
dd  e7  f7  2b  a5  01  01  85  ff  ff  e7  ff  b7  91  fd  cb  8f  f9  93  89  fb  67  87  ff  6f  95  ff  ff  43  91  ff  
dd  e7  f7  75  01  da  f6  fe  77  de  5f  c7  ed  97  83  ff  ff  81  e7  fb  87  ff  6f  89  fb  07  7b  99  ff  cf  8f  fe
dd  e7  f7  75  a5  01  01  85  ff  ff  e7  ff  b7  91  fd  cb  8f  f9  93  89  fb  07  87  ff  6f  95  ff  ff  77  9b  ff  
dd  e7  f7  49  01  fe  76  ff  77  de  5f  e2  ed  7b  83  ff  ff  81  e7  fb  87  ff  6f  89  fb  79  7b  99  ff  95  ff  ff
dd  e7  f7  49  a5  01  01  85  ff  7f  e7  ff  b7  91  fd  cb  8f  f9  93  89  fb  79  87  ff  6f  95  ff  ff  c1  3   ff  
dd  e7  f5  45  01  99  f6  fe  77  5e  5f  d4  ed  bb  83  ff  ff  81  e7  fb  87  ff  6f  89  fb  3d  7b  99  ff  bb  d1  ff
dd  e7  f5  45  a5  01  01  85  ff  ff  e7  ff  b7  91  fd  cb  8f  f9  93  89  fb  3d  87  ff  6f  95  ff  ff  bf  0d  ff  

Included here the same data, but color coded to recognize patterns.

Also the raw bytes that were captured:

00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 
dd e7 c9 k  01 \n .  fe w  ^  n  e3 ed f7 83 ff ff 81 e7 fb {  99 ff -  1f fe 
dd e7 c9 k  a5 01 01 85 ff ff e7 ff b7 91 fd cb 8f f9 93 89 fb =  87 ff o  95 ff ff 8d /  ff 

dd e7 af 83 01 V  89 ff w  de m  e4 ed cb 83 ff ff 81 e7 fb {  99 ff e9 d1 ff 
dd e7 af 83 a5 01 01 85 ff ff e7 ff b7 91 fd cb 8f f9 93 89 fb 85 87 ff o  95 ff ff %  ;  fe 

dd e7 f5 3  01 bf 6  fe w  ^  df d3 ed e5 83 ff ff 81 e7 fb {  99 ff 93 a7 fe 
dd e7 f5 3  a5 01 01 85 ff ff e7 ff b7 91 fd cb 8f f9 93 89 fb 85 87 ff o  95 ff ff d3 fb ff 

dd e7 f7 m  01 ce f6 fe ff ff ff ff `  ff ff 
dd e7 f7 m  a5 01 01 85 ff ff e7 ff b7 91 fd cb 8f f9 93 89 fb y  87 ff o  95 ff ff 15 d3 fe 

dd e7 f7 +  01 aa 6  fe 
dd e7 f7 +  a5 01 01 85 ff ff e7 ff b7 91 fd cb 8f f9 93 89 fb w  87 ff o  95 ff ff Q  11 ff 

dd e7 f7 u  01 da f6 fe w  de _  c7 ed ?  87 ff ff 81 e7 fb {  99 ff bd i  ff 
dd e7 f7 u  a5 01 01 85 ff ff e7 ff b7 91 fd cb 8f f9 93 89 fb 07 87 ff o  95 ff ff w  9b ff 

dd e7 f7 I  01 fe v  ff w  de _  e2 ed u  83 ff ff 81 e7 fb {  99 ff cf a1 ff 
dd e7 f7 I  a5 01 01 85 ff ff e7 ff b7 91 fd cb 8f f9 93 89 fb y  87 ff o  95 ff ff eb 9d ff 

dd e7 f5 E  01 99 f6 fe w  ^  _  d4 ed a7 83 ff ff 81 e7 fb {  99 ff 85 0f ff 
dd e7 f5 E  a5 01 01 85 ff ff e7 ff b7 91 fd cb 8f f9 93 89 fb =  87 ff o  95 ff ff bf \r ff

Base station

Uponor Smatrix Base X-245. Some sites claim it communicates over RS485. Do not confuse with the PRO version that supports Modbus.

Thermostat

The thermostats are Uponor Smatrix Base T-146 Bus, which have a Renesas RL78/L13 chipset which is used for serial communication (I think). From the datasheet:

[80-pin]
• CSI: 1 channel/UART (UART supporting LIN-bus): 1 channel/simplified I2C: 1 channel
• CSI: 1 channel/UART: 1 channel/simplified I2C: 1 channel
• UART: 2 channels

Setup

Raspberry Pi 3B+         X-245        T-146
GPIO 14 (UART TX)  <-->    A    <-->    A
GPIO 15 (UART RX)  <-->    B    <-->    B
                           +    <-->    +
                           -    <-->    -

Who recognizes a protocol or can help me decode it? I've tried:

Upvotes: 1

Views: 644

Answers (2)

Thomas Buyukkilic
Thomas Buyukkilic

Reputation: 21

So like Marcos suggested, the problem with sending was me not realising that for RX the RE should have been low and for TX the DE should have been high. Now I can read/write at the same time using this small class:

import time
import serial
import serial.rs485
import RPi.GPIO as GPIO
from crc import crc16

TX_DE = 11
RX_RE = 13

baud = 19200
T1_5 = 16500000 / baud / 1000
T3_5 = 38500000 / baud / 1000

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(TX_DE, GPIO.OUT, initial=GPIO.HIGH)  # HIGH enables TX
GPIO.setup(RX_RE, GPIO.OUT, initial=GPIO.LOW)  # LOW enables RX

ser = serial.rs485.RS485(port="/dev/serial0", baudrate=baud, timeout=T3_5)
ser.rs485_mode = serial.rs485.RS485Settings(
    rts_level_for_tx=False,
    rts_level_for_rx=False,
    loopback=True,
    delay_before_tx=T3_5,
    delay_before_rx=T3_5,
)

def write(command, direct=False):
    command_bytes = bytes.fromhex(command)
    if not direct:
        crc = crc16(command_bytes.decode("latin-1"))
        ser.write(command_bytes + bytes.fromhex(crc[-2:]) + bytes.fromhex(crc[:2]))
    else:
        ser.write(command_bytes)


def read():
    while True:
        x = ser.read(4095)
        print(x.hex())

How ever now I can see the commands on the serial bus e.g. write(b'\x01\x04\x00\x01\x00\x0c\xA1\xCF'), but nothing happens (i.e. no Modbus response), what am I missing?

Upvotes: 0

Thomas Buyukkilic
Thomas Buyukkilic

Reputation: 21

So it turns out that I was missing a TTL > RS485 converter, so I added that and now I can clearly read the data, and it turns out to be Modbus after all. This article helped me to setup a stable serial reader and this one helped me to get to the Modbus result. This is the output now:

110c28beff6abb
110c28be4002753e00003f0c0242b3003f71
110c28be2d7fff3d00000c002437019a3803b63b023c3c0048358000fd22
110c0566ffa0b2
110c05664002623e00003f0c0242b30082ca
110c05662d7fff3d00000c002437019a3803b63b024e3c004835800054c5
110c04c9ff8c82
110c04c94002c53e00003f0c0242b300a1fd
110c04c92d7fff3d00000c002437019a3803b63b02c43c00483580008356
110c04eaff95b2
110c04ea4002c43e00003f0c0242b3008a9b
110c04ea2d7fff3d00000c002437019a3803b63b02c43c0048358000d777
110c04c5ff8982
110c04c54002a03e00003f0c0242b300a39f
110c04c52d7fff3d00000c002437019a3803b63b027c3c00483580004432
110c04dbff8022
110c04db4002c73e00003f0c0242b30039e5
110c04db2d7fff3d00000c002437019a3803b63b02c33c00483580000ab1
110c055dffb382
110c055d4002843e00003f0c0242b3003df8
110c055d2d7fff3d00000c002437019a3803b63b027c3c0048358000edf8

Now that I have a readable result, I do have another problem: The serial bus keeps sending the event log for all thermostats i.e. function code 12 (0x0C) Get Comm Event Log. I have no idea how to get rid of this as all the responses start with \x11\x0c minimalmobus can't match a request to the right slave (\x11 will be slave number 17, while I request data from slave number 1). What to do here?

minimalmodbus config

import minimalmodbus
from gpiozero import OutputDevice
de = OutputDevice(23)
re = OutputDevice(24)
de.off()
re.off()
instrument = minimalmodbus.Instrument('/dev/serial0', 1, minimalmodbus.MODE_RTU)
instrument.serial.timeout = None
instrument.debug = True

read_register output

>>> instrument.read_register(40006,0,3,True)
MinimalModbus debug mode. Will write to instrument (expecting 7 bytes back): 01 03 9C 46 00 01 4B 8F (8 bytes)
MinimalModbus debug mode. Clearing serial buffers for port /dev/serial0
MinimalModbus debug mode. No sleep required before write. Time since previous read: 2965.81 ms, minimum silent period: 2.01 ms.
MinimalModbus debug mode. Response from instrument: 11 0C 1B CA FF BD B4 (7 bytes), roundtrip time: 7.0 ms. Timeout for reading: 0.0 ms.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/dist-packages/minimalmodbus.py", line 486, in read_register
    payloadformat=_Payloadformat.REGISTER,
  File "/usr/local/lib/python3.7/dist-packages/minimalmodbus.py", line 1245, in _generic_command
    payload_from_slave = self._perform_command(functioncode, payload_to_slave)
  File "/usr/local/lib/python3.7/dist-packages/minimalmodbus.py", line 1330, in _perform_command
    response, self.address, self.mode, functioncode
  File "/usr/local/lib/python3.7/dist-packages/minimalmodbus.py", line 1875, in _extract_payload
    responseaddress, slaveaddress, response
minimalmodbus.InvalidResponseError: Wrong return slave address: 17 instead of 1. The response is: '\x11\x0c\x1bÊÿ½´'

Do you guys know what to do? Is it possible that the Uponor Smatrix Base X-245 only has a subset of the Modbus functions implemented? If so, what would my best shot be to write something back?

I tried to capture a possible request to change the temperature by adjusting it on the thermostat, but all I can see is the log where I went from 21,5°C (707°K, \x02\xc4) to 25,0°C (770°K, \x03\x02)

110c04eaff95b2
110c04ea4002c43e00003f0c02 42b3008a9b
110c04ea2d7fff3d00000c002437019a3803b63b02c43c0048358000d777

110c04eaff95b2
110c04ea4002c43e00003f0c02 3c00483b030242b3004466
110c04ea2d7fff3d00000c002437019a3803b63b03023c0048358000b0b7

110c04eaff95b2
110c04ea4002c43e00003f0c023c00483b030242b3004466
110c04ea2d7fff3d00400c002437019a3803b63b03023c0048358000a578

110c04eaff95b2
110c04ea4002c43e00003f0c0242b3008a9b
110c04ea2d7fff3d00400c002437019a3803b63b03023c0048358000a578

Upvotes: 1

Related Questions