


2. Delta Solivia protocol description
Messages on the RS485-bus look like this:
| Size | Name | Value | Description |
| 1 byte | STX | 0x02 | Start of message |
| 1 byte | ENQ | Enquiry type (0x05 for requests, 0x06 for responses) | |
| 1 byte | Inverter ID | ||
| 1 byte | LEN | Number of bytes to follow, excluding CRC and ETX | |
| 2 bytes | CMD | command and subcommand e.g. 0x10 0x02 to request the current DC voltage, 0x10 0x02 to request the current DC current, etc. | |
| LEN-2 bytes | data bytes | ||
| 2 bytes | CRC | CRC-16, over preceding bytes excluding STX, LSB first (little-endian) | |
| 1 byte | ETX | 0x03 | End of message |


| Request | Response | Value | Meaning |
| [02][05][01][02][00][01][AD][FC][03] | [02][06][01][0C][00][01][30][30][30][30][30][30][37][38][39][34][2D][D0][03] | 0000007894 | Serial number |
| [02][05][01][02][10][02][E0][3D][03] | [02][06][01][04][10][02][01][88][32][E7][03] | 0x0188 | DC voltage (actual) |
| [02][05][01][02][11][02][E1][AD][03] | [02][06][01][04][11][02][01][78][33][5F][03] | 0x0178 | DC voltage (average) |
|
[02][05][01][02][10][01][A0][3C][03]
|
[02][06][01][04][10][01][00][09][03][17][03] | 0x0009 | DC current (actual). Unit is 0.1 A |
|
[02][05][01][02][11][01][A1][AC][03]
|
[02][06][01][04][11][01][00][05][02][EE][03] | 0x0005 | DC current (average). Unit is 0.1 A |
|
[02][05][01][02][11][03][20][6D][03]
|
[02][06][01][04][11][03][00][D7][23][73][03] | 0x00d7 | DC power |
|
[02][05][01][02][21][04][75][AF][03]
|
[02][06][01][04][21][04][07][D0][DE][40][03] | 0x07d0 | Isolation (actual, MOhm) |
|
[02][05][01][02][11][0F][20][68][03]
|
[02][06][01][04][11][0F][07][DA][20][85][03] | 0x07da | Isolation (average, MOhm) |
|
[02][05][01][02][10][08][60][3A][03]
|
[02][06][01][04][10][08][00][EC][12][9E][03] | 0x00ec | AC Voltage (actual) |
|
[02][05][01][02][11][08][61][AA][03]
|
[02][06][01][04][11][08][00][EA][93][60][03] | 0x00ea | AC Voltage (average) |
|
[02][05][01][02][10][07][20][3E][03]
|
[02][06][01][04][10][07][00][11][E3][1C][03] | 0x0011 | AC current (actual). Unit is 0.1 A |
|
[02][05][01][02][11][07][21][AE][03]
|
[02][06][01][04][11][07][00][0B][63][2B][03] | 0x000b | AC current (average). Unit is 0.1 A |
|
[02][05][01][02][10][09][A1][FA][03]
|
[02][06][01][04][10][09][01][72][C3][66][03] | 0x0172 | AC power (actual) |
|
[02][05][01][02][11][09][A0][6A][03]
|
[02][06][01][04][11][09][00][EE][C3][63][03] | 0x00ee | AC power (average) |
|
[02][05][01][02][21][06][F4][6E][03]
|
[02][06][01][04][21][06][00][1B][3C][27][03] | 0x001b | DC NTC Temperature |
|
[02][05][01][02][20][05][B5][FF][03]
|
[02][06][01][04][20][05][00][18][8D][DA][03] | 0x0018 | AC NTC Temperature |
| [02] | STX |
| [05] | ENQ |
| [01] | Inverter ID |
| [02] | Number of data bytes |
| [21][06] | Command and subcommand (DC NTC Temperature in this case) |
| [F4][6E] | CRC-16 |
| [03] | ETX |
| [02] | STX |
| [06] | RESPONSE |
| [01] | Inverter ID |
| [04] | Number of data bytes |
| [20][06] | Command and subcommand (the same of the request) |
| [00][1B] | 16-bit value of the requested parameter |
| [3C][27] | CRC-16 |
| [03] | ETX |

| Request | Response | Value | Meaning |
| [02][05][01][02][13][03][21][0D][03] | [02][06][01][04][13][03][00][AE][E3][29][02] | 0x00AE (174) | Daily yield (Wh) |
To implement the Delta Solivia protocol, I started from this project. I had to make some changes to use python3 (the library was written to support python2) and to use the correct commands.
Here is a brief description of the functions involved
3.1 solivia_readData
This function reads a specific value from the Delta Solivia inverter (DC power, AC power, etc). The only caveat here is that I had to add a check to remove some trailing 0xff characters that are probably mistakenly read by the USB-to-RS485 converter when inverter drives RS485 from RX to TX.
def solivia_readData(s):
log.debug("Reading %s"%(s))
cmd = solivia_inverter.getCmdStringFor(s)
log.debug("Command: %s"%binascii.hexlify(cmd))
solivia_connection.write(cmd)
rawResponse = solivia_connection.read(100)
cntr = 0
while (cntr < len(rawResponse) and rawResponse[cntr] == 0xff):
cntr += 1
response = rawResponse[cntr:]
floatValue = float("0")
if response:
log.debug("Received response %s\n"%binascii.hexlify(response))
value = solivia_inverter.getValueFromResponse(response)
#log.debug("Value %s"%value)
if solivia_isFloat(value):
floatValue = float(value)
return floatValue
The code leverages functions in the deltaInv.py file to extract a value as a floating point number from the inverter response
3.2 solivia_updateChannel
This function updates all the Modbus registers required to mimic one of Omega OM240 channels. The following Modbus registers are initialized
- 2 16-bits registers with the value
- 8 16-bits registers with the timestamp string
- 1 16-bits registers with the flags
Here is the code
def solivia_updateChannel(ch, value, ts):
""" Registers are as follow
0-1 Input A (IEE754)
2-3 Input B
4-5 Temperature
6-14 Timestamp "dd/mm/yy hh:mm:ss"
15 Flags
"""
vHex = solivia_floatToHex(value)[2:]
log.debug("value=%f, vHex=%s"%(value,vHex))
stime = ts.strftime("%d/%m/%y %H:%M:%S").encode('utf-8').hex().ljust(36, '0')
values = [int(vHex[0:4],16), int(vHex[4:8],16),
0, 0,
0, 0,
int(stime[0:4],16), int(stime[4:8],16), int(stime[8:12],16), int(stime[12:16],16), int(stime[16:20],16), int(stime[20:24],16), int(stime[24:28],16), int(stime[28:32],16), int(stime[32:36],16),
3]
return values
3.3 solivia_reader
This functions reads all the measures and update the corresponding channels
def solivia_reader(a):
""" A worker process that runs every so often and
updates live values of the context. It should be noted
that there is a race condition for the update.
:param arguments: The input arguments to the call
"""
log.info("Updating inverter data\n")
dcVolts = solivia_readData('DC Volts1')
dcCur = solivia_readData('DC Cur1')
dcPow = solivia_readData('DC Pwr1')
acVolts = solivia_readData('AC Volts')
acCur = solivia_readData('AC Current')
acPow = solivia_readData('AC Power')
acTemp = solivia_readData('AC Temp')
dcTemp = solivia_readData('DC Temp')
context = a[0]
register = 3
slaveId = 0x00
address = 0
values = solivia_updateChannel(0, dcPow, datetime.now())
context[slaveId].setValues(register, address, values)
address = 16
values = solivia_updateChannel(0, acPow, datetime.now())
context[slaveId].setValues(register, address, values)
address = 32
values = solivia_updateChannel(0, acTemp, datetime.now())
context[slaveId].setValues(register, address, values)
address = 48
values = solivia_updateChannel(0, dcTemp, datetime.now())
context[slaveId].setValues(register, address, values)
currDate = datetime.now().strftime("%d/%m/%y").encode('utf-8').hex().ljust(10, '0')
currTime = datetime.now().strftime("%H:%M:%S").encode('utf-8').hex().ljust(8, '0')
log.debug(currDate+"-"+currTime+" %.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f OK\n" %(dcVolts, dcCur, dcPow, acVolts, acCur, acPow, dcTemp, acTemp ))
log.info("Scheduling task")
time = 5.0
loop = LoopingCall(f=solivia_reader, a=(context,))
loop.start(time, now=True) # initially delay by time #!/bin/sh
# launcher.sh
# navigate to home directory, then to this directory, then execute python script, then back home
cd /home/pi/nmodbus
sudo python3 server.py
cd /
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
@reboot sh /home/pi/launcher.sh >/home/pi/logs/server.log 2>&1
-
Jan Cumps
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
-
amgalbu
in reply to Jan Cumps
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
Comment-
amgalbu
in reply to Jan Cumps
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
Children