2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.nibeheatpump.internal.protocol;
15 import org.apache.commons.lang.ArrayUtils;
16 import org.openhab.binding.nibeheatpump.internal.NibeHeatPumpException;
19 * This class contains useful Nibe heat pump protocol utils.
21 * @author Pauli Anttila - Initial contribution
23 public class NibeHeatPumpProtocol {
25 public static final byte FRAME_START_CHAR_FROM_NIBE = (byte) 0x5C;
26 public static final byte FRAME_START_CHAR_TO_NIBE = (byte) 0xC0;
28 public static final byte OFFSET_START = 0;
29 public static final byte OFFSET_ADR = 2;
30 public static final byte OFFSET_CMD = 3;
31 public static final byte OFFSET_LEN = 4;
32 public static final byte OFFSET_DATA = 5;
34 public static final byte CMD_RMU_DATA_MSG = (byte) 0x62;
35 public static final byte CMD_MODBUS_DATA_MSG = (byte) 0x68;
36 public static final byte CMD_MODBUS_READ_REQ = (byte) 0x69;
37 public static final byte CMD_MODBUS_READ_RESP = (byte) 0x6A;
38 public static final byte CMD_MODBUS_WRITE_REQ = (byte) 0x6B;
39 public static final byte CMD_MODBUS_WRITE_RESP = (byte) 0x6C;
41 public static final byte ADR_SMS40 = (byte) 0x16;
42 public static final byte ADR_RMU40 = (byte) 0x19;
43 public static final byte ADR_MODBUS40 = (byte) 0x20;
45 public static boolean isModbus40DataReadOut(byte[] data) {
46 if (data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
47 && data[OFFSET_ADR] == ADR_MODBUS40) {
48 return data[OFFSET_CMD] == CMD_MODBUS_DATA_MSG && data[OFFSET_LEN] >= (byte) 0x50;
54 public static boolean isModbus40ReadResponse(byte[] data) {
55 if (data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
56 && data[OFFSET_ADR] == ADR_MODBUS40) {
57 return data[OFFSET_CMD] == CMD_MODBUS_READ_RESP && data[OFFSET_LEN] >= (byte) 0x06;
63 public static boolean isRmu40DataReadOut(byte[] data) {
64 if (data[0] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00 && data[OFFSET_ADR] == ADR_RMU40) {
65 return data[OFFSET_CMD] == CMD_RMU_DATA_MSG && data[OFFSET_LEN] >= (byte) 0x18;
71 public static boolean isModbus40WriteResponsePdu(byte[] data) {
72 return data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
73 && data[OFFSET_ADR] == ADR_MODBUS40 && data[OFFSET_CMD] == CMD_MODBUS_WRITE_RESP;
76 public static boolean isModbus40WriteTokenPdu(byte[] data) {
77 return data[0] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00 && data[OFFSET_ADR] == ADR_MODBUS40
78 && data[OFFSET_CMD] == CMD_MODBUS_WRITE_REQ && data[OFFSET_LEN] == 0x00;
81 public static boolean isModbus40ReadTokenPdu(byte[] data) {
82 return data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
83 && data[OFFSET_ADR] == ADR_MODBUS40 && data[OFFSET_CMD] == CMD_MODBUS_READ_REQ
84 && data[OFFSET_LEN] == 0x00;
87 public static boolean isModbus40WriteRequestPdu(byte[] data) {
88 return data[0] == FRAME_START_CHAR_TO_NIBE && data[1] == CMD_MODBUS_WRITE_REQ;
91 public static boolean isModbus40ReadRequestPdu(byte[] data) {
92 return data[OFFSET_START] == FRAME_START_CHAR_TO_NIBE && data[1] == CMD_MODBUS_READ_REQ;
95 public static byte calculateChecksum(byte[] data) {
96 return calculateChecksum(data, 0, data.length);
99 public static byte calculateChecksum(byte[] data, int startIndex, int stopIndex) {
101 // calculate XOR checksum
102 for (int i = startIndex; i < stopIndex; i++) {
108 public static byte getMessageType(byte[] data) {
109 byte messageType = 0;
111 if (data[NibeHeatPumpProtocol.OFFSET_START] == NibeHeatPumpProtocol.FRAME_START_CHAR_FROM_NIBE) {
112 messageType = data[NibeHeatPumpProtocol.OFFSET_CMD];
113 } else if (data[NibeHeatPumpProtocol.OFFSET_START] == NibeHeatPumpProtocol.FRAME_START_CHAR_TO_NIBE) {
114 messageType = data[1];
120 public static byte[] checkMessageChecksumAndRemoveDoubles(byte[] data) throws NibeHeatPumpException {
125 if (NibeHeatPumpProtocol.isModbus40ReadRequestPdu(data)
126 || NibeHeatPumpProtocol.isModbus40WriteRequestPdu(data)) {
127 msglen = 3 + data[2];
131 msglen = 5 + data[OFFSET_LEN];
136 final byte checksum = calculateChecksum(data, startIndex, stopIndex);
137 final byte msgChecksum = data[msglen];
139 // if checksum is 0x5C (start character), heat pump seems to send 0xC5 checksum
141 if (checksum == msgChecksum || (checksum == FRAME_START_CHAR_FROM_NIBE && msgChecksum == (byte) 0xC5)) {
142 // if data contains 0x5C (start character), data seems to contains double 0x5C characters
144 // let's remove doubles
145 for (int i = 1; i < msglen; i++) {
146 if (data[i] == FRAME_START_CHAR_FROM_NIBE) {
147 data = ArrayUtils.remove(data, i);
155 throw new NibeHeatPumpException(
156 "Checksum does not match. Checksum=" + (msgChecksum & 0xFF) + ", expected=" + (checksum & 0xFF));