2 * Copyright (c) 2010-2023 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.herzborg.internal.dto;
15 import java.nio.ByteBuffer;
16 import java.nio.ByteOrder;
19 * Herzborg binary protocol
21 * @author Pavel Fedin - Initial contribution
24 public class HerzborgProtocol {
25 public static class Function {
26 public static final byte READ = 0x01;
27 public static final byte WRITE = 0x02;
28 public static final byte CONTROL = 0x03;
29 public static final byte REQUEST = 0x04;
32 public static class ControlAddress {
33 public static final byte OPEN = 0x01;
34 public static final byte CLOSE = 0x02;
35 public static final byte STOP = 0x03;
36 public static final byte PERCENT = 0x04;
37 public static final byte DELETE_LIMIT = 0x07;
38 public static final byte DEFAULT = 0x08;
39 public static final byte SET_CONTEXT = 0x09;
40 public static final byte RUN_CONTEXT = 0x0A;
41 public static final byte DEL_CONTEXT = 0x0B;
44 public static class DataAddress {
45 public static final byte ID_L = 0x00;
46 public static final byte ID_H = 0x01;
47 public static final byte POSITION = 0x02;
48 public static final byte DEFAULT_DIR = 0x03;
49 public static final byte HAND_START = 0x04;
50 public static final byte MODE = 0x05;
51 public static final byte EXT_SWITCH = 0x27;
52 public static final byte EXT_HV_SWITCH = 0x28;
55 public static class Packet {
56 private static final int HEADER_LENGTH = 5;
57 private static final int CRC16_LENGTH = 2;
58 public static final int MIN_LENGTH = HEADER_LENGTH + CRC16_LENGTH;
60 private static final byte START = 0x55;
62 private ByteBuffer dataBuffer;
63 private int dataLength; // Packet length without CRC16
65 public Packet(byte[] data) {
66 dataBuffer = ByteBuffer.wrap(data);
67 dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
68 dataLength = data.length - CRC16_LENGTH;
71 private void setHeader(short device_addr, byte function, byte data_addr, int data_length) {
72 dataLength = HEADER_LENGTH + data_length;
74 dataBuffer = ByteBuffer.allocate(dataLength + CRC16_LENGTH);
75 dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
77 dataBuffer.put(START);
78 dataBuffer.putShort(device_addr);
79 dataBuffer.put(function);
80 dataBuffer.put(data_addr);
83 private void setCrc16() {
84 dataBuffer.putShort(crc16(dataLength));
87 public Packet(short device_addr, byte function, byte data_addr) {
88 setHeader(device_addr, function, data_addr, 0);
92 public Packet(short device_addr, byte function, byte data_addr, byte value) {
93 int dataLength = (function == Function.WRITE) ? 2 : 1;
95 setHeader(device_addr, function, data_addr, dataLength);
96 if (function == Function.WRITE) {
97 // WRITE command also requires length of data to be written
98 dataBuffer.put((byte) 1);
100 dataBuffer.put(value);
104 public byte[] getBuffer() {
105 return dataBuffer.array();
108 public boolean isValid() {
109 return dataBuffer.get(0) == START && crc16(dataLength) == dataBuffer.getShort(dataLength);
112 public byte getFunction() {
113 return dataBuffer.get(3);
116 public byte getDataAddress() {
117 return dataBuffer.get(4);
120 public byte getDataLength() {
121 return dataBuffer.get(HEADER_LENGTH);
124 public byte getData(int offset) {
125 return dataBuffer.get(HEADER_LENGTH + offset);
128 // Herzborg uses modbus variant of CRC16
129 // Code adapted from https://habr.com/ru/post/418209/
130 private short crc16(int length) {
132 for (int i = 0; i < length; i++) {
133 crc = crc ^ Byte.toUnsignedInt(dataBuffer.get(i));
134 for (int j = 0; j < 8; j++) {
135 int mask = ((crc & 0x1) != 0) ? 0xA001 : 0x0000;
136 crc = ((crc >> 1) & 0x7FFF) ^ mask;