Skip to content

Instantly share code, notes, and snippets.

@danielwippermann
Created April 7, 2023 04:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielwippermann/c9311d6c85c5942de5bdc15871af1921 to your computer and use it in GitHub Desktop.
Save danielwippermann/c9311d6c85c5942de5bdc15871af1921 to your computer and use it in GitHub Desktop.
/** Copyright (c) 2017 - 'FatBeard' @ www.domoticz.com/forum
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
#include "VBUSDecoder.h"
#define DEBUG 0
static uint16_t ReadUInt16(const uint8_t *Bytes)
{
return ((uint16_t) Bytes [0]) | (((uint16_t) Bytes [1]) << 8);
}
static int16_t ReadInt16(const uint8_t *Bytes)
{
return ((int16_t) ReadUInt16(Bytes));
}
static uint32_t ReadUInt32(const uint8_t *Bytes)
{
return ((uint32_t) Bytes [0]) | (((uint32_t) Bytes [1]) << 8) | (((uint32_t) Bytes [2]) << 16) | (((uint32_t) Bytes [3]) << 24);
}
static int32_t ReadInt32(const uint8_t *Bytes)
{
return ((int32_t) ReadUInt32(Bytes));
}
// Clear all maximum values
void VBUSDecoder::clearMaxValues()
{
sensor1TempMax = 0.0;
sensor2TempMax = 0.0;
sensor3TempMax = 0.0;
sensor4TempMax = 0.0;
}
bool VBUSDecoder::initialise()
{
Serial1.begin(9600);
clearMaxValues();
return true;
} // end void initialise()
float VBUSDecoder::getS1Temp()
{
return sensor1Temp;
}
float VBUSDecoder::getS2Temp()
{
return sensor2Temp;
}
float VBUSDecoder::getS3Temp()
{
return sensor3Temp;
}
float VBUSDecoder::getS4Temp()
{
return sensor4Temp;
}
bool VBUSDecoder::getP1Status()
{
return relayPump;
}
bool VBUSDecoder::getP2Status()
{
return relay3WayValve;
}
int VBUSDecoder::getP1Speed()
{
return String(Relay1, DEC).toInt();
}
int VBUSDecoder::getP2Speed()
{
return String(Relay2, DEC).toInt();
}
int VBUSDecoder::getP1OperatingHours()
{
return String(OperatingHoursRelay1, DEC).toInt();
}
int VBUSDecoder::getP2OperatingHours()
{
return String(OperatingHoursRelay2, DEC).toInt();
}
int VBUSDecoder::getScheme()
{
return String(Scheme, DEC).toInt();
}
bool VBUSDecoder::getAlertStatus()
{
return SystemAlert;
}
String VBUSDecoder::getSystemTime()
{
int hours = SystemTime / 60;
int minutes = SystemTime % 60;
String toReturn = String(String(hours) + ":" + String(minutes));
if (hours < 10)
{
toReturn = "0" + toReturn;
}
return toReturn;
}
uint32_t VBUSDecoder::getHeatQuantity()
{
return HeatQuantity;
}
bool VBUSDecoder::readSensor()
{
if (vBusRead())
{
#if DEBUG
Serial.println("------Decoded VBus data------");
Serial.print("Destination: ");
Serial.println(Destination_address, HEX);
Serial.print("Source: ");
Serial.println(Source_address, HEX);
Serial.print("Protocol Version: ");
Serial.println(ProtocolVersion);
Serial.print("Command: ");
Serial.println(Command, HEX);
Serial.print("Framecount: ");
Serial.println(Framecnt);
Serial.print("Checksum: ");
Serial.println(Checksum);
Serial.println("------Values------");
Serial.print("Sensor 1: ");
Serial.println(sensor1Temp);
Serial.print("Sensor 2: ");
Serial.println(sensor2Temp);
Serial.print("Sensor 3: ");
Serial.println(sensor3Temp);
Serial.print("Sensor 4: ");
Serial.println(sensor4Temp);
Serial.print("Relay 1: ");
Serial.println(Relay1, DEC);
Serial.print("Relay 2: ");
Serial.println(Relay2, DEC);
Serial.print("Minute of Day: ");
Serial.println(SystemTime);
Serial.print("Notifications: ");
Serial.println(SystemNotification, DEC);
Serial.println("------END------");
#endif
} //end VBusRead
/*
S1 = Sensor 1 (sensor SFB/stove)
S2 = Sensor 2 (sensor store base)
S3 = Sensor 3 (sensor store top)
S4 = Sensor 4 (system-dependent)
R1 = Pump
R2 = 3-way valve
*/
// Convert relay value to On or Off.
if (Relay1 != 0x00)
{
relayPump = true;
}
else
{
relayPump = false;
}
if (Relay2 == 0x64)
{
relay3WayValve = true;
}
else if (Relay2 == 0x00)
{
relay3WayValve = false;
}
else
{
relay3WayValve = false;
}
if (SystemNotification != 0x00 || ErrorMask != 0x00) // Not really sure what ErrorMask is, treating as system alert.
{
SystemAlert = true;
}
else
{
SystemAlert = false;
}
return true;
}
// The following is needed for decoding the data
void VBUSDecoder::InjectSeptet(unsigned char *Buffer, int Offset, int Length)
{
for (unsigned int i = 0; i < Length; i++)
{
if (Septet & (1 << i))
{
Buffer[Offset + i] |= 0x80;
}
}
PrintHex8(&Buffer[Offset], Length);
}
// CRC calculation
// From https://danielwippermann.github.io/resol-vbus/vbus-specification.html
unsigned char VBUSDecoder::VBus_CalcCrc(unsigned char *Buffer, int Offset, int Length)
{
unsigned char CRC = 0x7F;
for (int i = 0; i < Length; i++) {
CRC = (CRC - Buffer [Offset + i]) & 0x7F;
}
return CRC;
}
// The following function reads the data from the bus and converts it all
// depending on the used VBus controller.
bool VBUSDecoder::vBusRead()
{
int F;
char c;
const char sync1 = 0xAA;
bool start = true;
bool stop = false;
bool quit = false;
int Bufferlength = 0;
unsigned long lastTimeTimer = millis();
while ((!stop) and (!quit))
{
if (Serial1.available())
{
c = Serial1.read();
if (c == sync1)
{
#if DEBUG
// Serial.println("Sync found");
#endif
if (start)
{
start = false;
Bufferlength = 0;
//#if DEBUG
//#endif
}
else
{
if (Bufferlength < 20)
{
lastTimeTimer = millis();
Bufferlength = 0;
}
else
stop = true;
}
}
#if DEBUG
// Serial.println(c, HEX);
#endif
if ((!start) and (!stop))
{
Buffer[Bufferlength] = c;
Bufferlength++;
}
}
if ((timerInterval > 0) && (millis() - lastTimeTimer > timerInterval))
{
quit = true;
#if DEBUG
// Serial.print("Timeout: ");
// Serial.println(lastTimeTimer);
#endif
}
}
lastTimeTimer = 0;
if (!quit)
{
Destination_address = Buffer[2] << 8;
Destination_address |= Buffer[1];
Source_address = Buffer[4] << 8;
Source_address |= Buffer[3];
ProtocolVersion = (Buffer[5] >> 4) + (Buffer[5] & (1 << 15));
Command = Buffer[7] << 8;
Command |= Buffer[6];
Framecnt = Buffer[8];
Checksum = Buffer[9];
#if DEBUG
Serial.println("---------------");
Serial.print("Destination: ");
Serial.println(Destination_address, HEX);
Serial.print("Source: ");
Serial.println(Source_address, HEX);
Serial.print("Protocol Version: ");
Serial.println(ProtocolVersion);
Serial.print("Command: ");
Serial.println(Command, HEX);
Serial.print("Framecount: ");
Serial.println(Framecnt);
Serial.print("Checksum: ");
Serial.println(Checksum);
Serial.println("---------------");
#endif
// Only analyse Commands 0x100 = Packet Contains data for slave
// with correct length = 10 bytes for HEADER and 6 Bytes for each frame
// Filter for packet http://danielwippermann.github.io/resol-vbus/#/vsf/bytes/00_0010_7428_10_0100
if ((Destination_address == 0x0010) && (Source_address == 0x7428) && (Command == 0x0100) && (Checksum == VBus_CalcCrc(Buffer, 1, 8)) && (Framecnt >= 15)) {
// This code expects 15 frames with 4 bytes each => 60 bytes in total
static uint8_t FrameData [60];
// IsValid might be set to false later if any of the frame checksums are not correct
bool IsValid = true;
// This loop serves multiple purposes:
// - it checks for the checksums of each frame
// - it injects the septett byte back into the 4 frame bytes
// - it copies the 4 bytes into a linear buffer to make accesses easier
for (int FrameIndex = 0; IsValid && (FrameIndex < 15); FrameIndex++) {
size_t SrcIndex = 10 + FrameIndex * 6;
// Check whether the frame's checksum is correct
if (Buffer[SrcIndex + 5] != VBus_CalcCrc(Buffer, SrcIndex, 5)) {
IsValid = false;
}
if (IsValid) {
// If the checksum was correct, inject the septett byte back
Septet = Buffer[SrcIndex + 4];
InjectSeptet(Buffer, SrcIndex, 4);
// Copy this frame over to the linear frame data buffer
size_t DstIndex = FrameIndex * 4;
for (int Index = 0; Index < 4; Index++) {
FrameData [DstIndex + Index] = Buffer [SrcIndex + Index];
}
}
}
// If IsValid is still true, all 15 frames had correct checksums and were copied into the FrameData buffer
if (IsValid) {
// S1: lo-byte at offset 0 and hi-byte at offset 1
sensor1Temp = ReadInt16(FrameData + 0);
// S2: lo-byte at offset 2 and hi-byte at offset 3
sensor2Temp = ReadInt16(FrameData + 2);
// S3: lo-byte at offset 4 and hi-byte at offset 5
sensor3Temp = ReadInt16(FrameData + 4);
// S4: lo-byte at offset 6 and hi-byte at offset 7
sensor4Temp = ReadInt16(FrameData + 6);
// R1: offset 24
Relay1 = FrameData [24];
// R2: offset 25
Relay2 = FrameData [25];
// ErrorMask: lo-byte at offset 44 and hi-byte at offset 45
ErrorMask = ReadUInt16(FrameData + 44);
}
}
if ((Command == 0x0100) and (Bufferlength == 10 + Framecnt * 6) and (Checksum == VBus_CalcCrc(Buffer, 1, 8) ) )
{
//Only decode the data from the correct source address
//(There might be other VBus devices on the same bus).
if (Source_address == 0x3271)
{
#if DEBUG
Serial.println("---------------");
Serial.println("Now decoding for 0x3271");
Serial.println("---------------");
#endif
// Frame info for the Resol ConergyDT5
// check VBusprotocol specification for other products
// This library is made for the ConergyDT5 (0x3271)
//Offset Size Mask Name Factor Unit
//0 2 Temperature sensor 1 0.1 &#65533;C
//2 2 Temperature sensor 2 0.1 &#65533;C
//4 2 Temperature sensor 3 0.1 &#65533;C
//6 2 Temperature sensor 4 0.1 &#65533;C
//8 1 Pump speed pump 1 1
//9 1 Pump speed pump 2 1
//10 1 Relay mask 1
//11 1 Error mask 1
//12 2 System time 1
//14 1 Scheme 1
//15 1 1 Option PostPulse 1
//15 1 2 Option thermostat 1
//15 1 4 Option HQM 1
//16 2 Operating hours relay 1 1
//18 2 Operating hours relay 2 1
//20 2 Heat quantity 1 Wh
//22 2 Heat quantity 1000 Wh
//24 2 Heat quantity 1000000 Wh
//26 2 Version 0.01
//
// Each frame has 6 bytes
// byte 1 to 4 are data bytes -> MSB of each bytes
// byte 5 is a septet and contains MSB of bytes 1 to 4
// byte 6 is a checksum
//
//******************* Frame 1 *******************
F = FOffset;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
// 'collector1' Temperatur Sensor 1, 15 bits, factor 0.1 in C
sensor1Temp = calcTemp(Buffer[F + 1], Buffer[F]);
// 'store1' Temperature sensor 2, 15 bits, factor 0.1 in C
sensor2Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 2 *******************
F = FOffset + FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor3Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor4Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 3 *******************
F = FOffset + FLength * 2;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
PumpSpeed1 = (Buffer[F]);
PumpSpeed2 = (Buffer[F + 1]);
RelaisMask = Buffer[F + 2];
ErrorMask = Buffer[F + 3];
}
//******************* Frame 4 *******************
F = FOffset + FLength * 3;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
SystemTime = Buffer[F + 1] << 8 | Buffer[F];
Scheme = Buffer[F + 2];
OptionPostPulse = (Buffer[F + 3] & 0x01);
OptionThermostat = ((Buffer[F + 3] & 0x02) >> 1);
OptionHQM = ((Buffer[F + 3] & 0x04) >> 2);
}
//******************* Frame 5 *******************
F = FOffset + FLength * 4;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
OperatingHoursRelay1 = Buffer[F + 1] << 8 | Buffer[F];
OperatingHoursRelay2 = Buffer[F + 3] << 8 | Buffer[F + 2];
}
//******************* Frame 6 *******************
F = FOffset + FLength * 5;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
HeatQuantity = (Buffer[F + 1] << 8 | Buffer[F]) + (Buffer[F + 3] << 8 | Buffer[F + 2]) * 1000;
}
//******************* Frame 7 *******************
F = FOffset + FLength * 6;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
HeatQuantity = HeatQuantity + (Buffer[F + 1] << 8 | Buffer[F]) * 1000000;
Version = Buffer[F + 3] << 8 | Buffer[F + 2];
}
///******************* End of frames ****************
} // end 0x3271 Conenergy DT5
else if (Source_address == 0x5611)
{
#if DEBUG
Serial.println("---------------");
Serial.println("Now decoding for 0x5611");
Serial.println("---------------");
#endif
// Frame info for the Resol Deltatherm FK and Oranier Aquacontrol III
// check VBusprotocol specification for other products
//
//Offset Size Mask Name Factor Unit
// Frame 1
//0 2 Temperature sensor 1 0.1 &#65533;C
//2 2 Temperature sensor 2 0.1 &#65533;C
// Frame 2
//4 2 Temperature sensor 3 0.1 &#65533;C
//6 2 Temperature sensor 4 0.1 &#65533;C
// Frame 3
//8 1 Relay 1 1 %
//9 1 Relay 2 1 %
//10 1 Mixer open 1 %
//11 1 Mixer closed 1 %
// Frame 4
//12 4 System date 1
// Frame 5
//16 2 System time 1
//18 1 System notification 1
//
// Each frame has 6 bytes
// byte 1 to 4 are data bytes -> MSB of each bytes
// byte 5 is a septet and contains MSB of bytes 1 to 4
// byte 6 is a checksum
//
//******************* Frame 1 *******************
F = FOffset;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor1Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor2Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 2 *******************
F = FOffset + FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor3Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor4Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 3 *******************
F = FOffset + FLength * 2;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
// Some of the values are 7 bit instead of 8.
// Adding '& 0x7F' means you are only interested in the first 7 bits.
// 0x7F = 0b1111111.
// See: http://stackoverflow.com/questions/9552063/c-language-bitwise-trick
Relay1 = (Buffer[F] & 0X7F);
Relay2 = (Buffer[F + 1] & 0X7F);
MixerOpen = (Buffer[F + 2] & 0X7F);
MixerClosed = (Buffer[F + 3] & 0X7F);
}
//******************* Frame 4 *******************
F = FOffset + FLength * 3;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
// System date is not needed for Domoticz
}
//******************* Frame 5 *******************
F = FOffset + FLength * 4;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
// System time is not needed for Domoticz
// Status codes System Notification according to Resol:
//0: no error / warning
//1: S1 defect
//2: S2 defect
//3: S3 defect
//4: VFD defect
//5: Flow rate?
//6: ΔT too high
//7: Low water level
SystemNotification = Buffer[F + 2];
}
///******************* End of frames ****************
} //End 0x5611 Resol DeltaTherm FK
else if (Source_address == 0x4212)
{
#if DEBUG
Serial.println("---------------");
Serial.println("Now decoding for DeltaSol C 0x4212");
Serial.println("---------------");
#endif
//******************* Frame 1 *******************
F = FOffset;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor1Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor2Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 2 *******************
F = FOffset + FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor3Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor4Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 3 *******************
F = FOffset + FLength * 2;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
Relay1 = (Buffer[F]);
Relay2 = (Buffer[F + 1]);
ErrorMask = Buffer[F + 2];
Scheme = Buffer[F + 3];
}
//******************* Frame 4 *******************
F = FOffset + FLength * 3;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
OperatingHoursRelay1 = Buffer[F + 1] << 8 | Buffer[F];
OperatingHoursRelay2 = Buffer[F + 3] << 8 | Buffer[F + 2];
}
//******************* Frame 5 *******************
F = FOffset + FLength * 4;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
HeatQuantity = (Buffer[F + 1] << 8 | Buffer[F]) + (Buffer[F + 3] << 8 | Buffer[F + 2]) * 1000;
}
//******************* Frame 6 *******************
F = FOffset + FLength * 5;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
HeatQuantity = HeatQuantity + (Buffer[F + 1] << 8 | Buffer[F]) * 1000000;
SystemTime = Buffer[F + 3] << 8 | Buffer[F + 2];
}
//******************* Frame 7 *******************
F = FOffset + FLength * 6;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
}
///******************* End of frames ****************
} // end 0x4212 DeltaSol C
else if (Source_address == 0x2211)
{
// Frame info for the Resol DeltaSol CS Plus (Joule)
// check VBusprotocol specification for other products
// This library is made for the Resol DeltaSol CS Plus (0x2211)
//Offset Mask Name Factor Unit
//0 Temperature S1 1.0 °C
//1 Temperature S1 256.0 °C
//2 Temperature S2 1.0 °C
//3 Temperature S2 256.0 °C
//4 Temperature S3 1.0 °C
//5 Temperature S3 256.0 °C
//6 Temperature S4 1.0 °C
//7 Temperature S4 256.0 °C
//8 Pump Speed R1 1 %
//10 Operating Hours R1 1 h
//11 Operating Hours R1 256 h
//12 Pump Speed R2 1 %
//14 Operating Hours R2 1 h
//15 Operating Hours R2 256 h
//16 UnitType 1
//17 System 1
//20 1 Sensor 1 defekt 1
//20 2 Sensor 2 defekt 1
//20 4 Sensor 3 defekt 1
//20 8 Sensor 4 defekt 1
//20 Error Mask 1
//20 Error Mask 256
//22 Time 1
//23 Time 256
//24 Statusmask 1
//25 Statusmask 256
//26 Statusmask 65536
//27 Statusmask 16777216
//28 Heat Quantity 1 Wh
//29 Heat Quantity 256 Wh
//30 Heat Quantity 65536 Wh
//31 Heat Quantity 16777216 Wh
//32 SW-Version 0.01
//33 SW-Version 2.56
//
// Each frame has 6 bytes, FLength
// byte 1 to 4 are data bytes -> MSB of each bytes
// byte 5 is a septet and contains MSB of bytes 1 to 4, FSeptet
// byte 6 is a checksum
// FOffset 10, Offset start of Frames
//******************* Frame 1 *******************
#if DEBUG
Serial.println("---------------");
Serial.println("Now decoding for DeltaSol CS Plus 0x2211");
Serial.println("---------------");
#endif
//******************* Frame 1 *******************
F = FOffset;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor1Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor2Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 2 *******************
F = FOffset + FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor3Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor4Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 3 *******************
F = FOffset + FLength * 2;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
Relay1 = (Buffer[F]);
OperatingHoursRelay1 = Buffer[F + 2] + Buffer[F + 3]*256; //Numero de horas rele 1
ErrorMask = Buffer[F + 2];
Scheme = Buffer[F + 3];
}
//******************* Frame 4 *******************
F = FOffset + FLength * 3;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
Relay2 = (Buffer[F]);
OperatingHoursRelay2 = Buffer[F + 2] + Buffer[F + 3]*256;
}
//******************* Frame 5 *******************
F = FOffset + FLength * 4;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
}
//******************* Frame 6 *******************
F = FOffset + FLength * 5;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
SystemTime = Buffer[F + 3] << 8 | Buffer[F + 2];
}
//******************* Frame 7 *******************
F = FOffset + FLength * 6;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
}
//******************* Frame 8 *******************
F = FOffset + FLength * 7;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
HeatQuantity = Buffer[F] + Buffer[F + 1]*256 + Buffer[F + 2]*65536 + Buffer[F + 3]*16777216;
}
///******************* End of frames ****************
} // end 0x2211 DeltaSol CS Plus
else if (Source_address == 0x7311){ // Deltasol M, alias Roth B/W Komfort
// 6 temp frames, 12 sensors
// Only decoding the first four due to library limitations
unsigned int frame = 0;
// Frame 1:
F = FOffset + frame * FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor1Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor2Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
// Frame 2:
frame++;
F = FOffset + frame * FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor3Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor4Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
// Frame 7: Irradiation and unused
/*
frame = 7
F = FOffset + 6 * FLength;
irradiation = CalcTemp(Buffer[F + 1], Buffer[F]);
*/
// Frame 8: Pulse counter 1
// Frame 9: Pulse counter 2
// Frame 10: Sensor errors: no sensor / shorted
// Frame 11: Sensors
// Frame 12: Relays 1-4
// Frame 13: Relays 5-8
// Frame 14: Relays 9-12
frame = 11;
F = FOffset + frame * FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Relay1 = Buffer[F];
Relay2 = Buffer[F + 1];
}
// Frame 15: Not used / relays
// Frame 16: Errors / warnings
// Frame 17: Version, revision / time
#ifdef DEBUG
Serial.println("Got values");
Serial.print("Temperature: ");
Serial.print(sensor1Temp);
Serial.print(", ");
Serial.print(sensor2Temp);
Serial.print(". Relays: ");
Serial.print(Relay1);
Serial.print(", ");
Serial.println(Relay2);
#endif
}
else if (Source_address == 0x4211)
{
#if DEBUG
Serial.println("---------------");
Serial.println("Now decoding for 0x4211 SKSC1/2");
Serial.println("---------------");
#endif
//******************* Frame 1 *******************
F = FOffset;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
// 'collector1' Temperatur Sensor 1, 15 bits, factor 0.1 in C
sensor1Temp = calcTemp(Buffer[F + 1], Buffer[F]);
// 'store1' Temperature sensor 2, 15 bits, factor 0.1 in C
sensor2Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 2 *******************
F = FOffset + FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor3Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor4Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 3 *******************
// store Pump data in relay as it gets data from there.. dont know why?
// Relay1 is then the pump speed in %
F = FOffset + FLength * 2;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
Relay1 = (Buffer[F]);
Relay2 = (Buffer[F + 1]);
RelaisMask = Buffer[F + 2];
ErrorMask = Buffer[F + 3];
}
}
/* Add your own controller ID and code in the if statement below and uncomment
else if (Source_address ==0x????){
}
*/
else
{
// Default temp 1-4 extraction
// For most Resol controllers temp 1-4 are always available, so
// even if you do not know the datagram format you can still see
// these temps 1 to 4.
//
//Offset Size Mask Name Factor Unit
// Frame 1
//0 2 Temperature sensor 1 0.1 &#65533;C
//2 2 Temperature sensor 2 0.1 &#65533;C
// Frame 2
//4 2 Temperature sensor 3 0.1 &#65533;C
//6 2 Temperature sensor 4 0.1 &#65533;C
//
// Each frame has 6 bytes
// byte 1 to 4 are data bytes -> MSB of each bytes
// byte 5 is a septet and contains MSB of bytes 1 to 4
// byte 6 is a checksum
//
//******************* Frame 1 *******************
F = FOffset;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
// 'collector1' Temperatur Sensor 1, 15 bits, factor 0.1 in C
sensor1Temp = calcTemp(Buffer[F + 1], Buffer[F]);
// 'store1' Temperature sensor 2, 15 bits, factor 0.1 in C
sensor2Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
//******************* Frame 2 *******************
F = FOffset + FLength;
if ( Buffer[F + 5] == VBus_CalcCrc(Buffer, F, 5) ) { // CRC ok
Septet = Buffer[F + FSeptet];
InjectSeptet(Buffer, F, 4);
sensor3Temp = calcTemp(Buffer[F + 1], Buffer[F]);
sensor4Temp = calcTemp(Buffer[F + 3], Buffer[F + 2]);
}
///******************* End of frames ****************
} //End of Default temp 1-4 extraction
if (sensor1Temp > sensor1TempMax)
sensor1TempMax = sensor1Temp;
if (sensor2Temp > sensor2TempMax)
sensor2TempMax = sensor2Temp;
if (sensor3Temp > sensor3TempMax)
sensor3TempMax = sensor3Temp;
if (sensor4Temp > sensor4TempMax)
sensor4TempMax = sensor4Temp;
} // end if command 0x0100
} // end !quit
return !quit;
} // end VBusRead()
// This function converts 2 data bytes to a temperature value.
float VBUSDecoder::calcTemp(int Byte1, int Byte2)
{
int v;
v = Byte1 << 8 | Byte2; //bit shift 8 to left, bitwise OR
if (Byte1 == 0x00)
{
v = v & 0xFF;
}
if (Byte1 == 0xFF)
v = v - 0x10000;
if (v == SENSORNOTCONNECTED)
v = 0;
return (float)((float)v * 0.1);
}
// Prints the hex values of the char array sent to it.
void VBUSDecoder::PrintHex8(unsigned char *data, uint8_t length) // prints 8-bit data in hex with leading zeroes
{
#if DEBUG
Serial.print("0x");
for (int i = 0; i < length; i++)
{
if (data[i] < 0x10)
{
Serial.print("0");
}
Serial.print(data[i], HEX);
Serial.print(" ");
}
Serial.println();
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment