Bonjour,
Dans cet article je propose le partage du développement que j’ai réalisé pour décoder les trames Vbus. toutes les spécification se trouvent ici: https://danielwippermann.github.io/resol-vbus/vbus-specification.html
tout à disparu de son site. Je ne l’ai hélas pas téléchargé……
Le code ci-après est dérivé du travail de M. Wipperman. Vous pouvez échanger sur le sujet en me contactant via formulaire contact
Version raspberry pi: vbus_decode
Version arduino:
/*
nouvelle version de Vbusread
* permet de selectionner un DFA
* serveur web fichiers json
* Prise en compte du CRC
* version ok pas toucher
*
* Legal Notices
* RESOL, VBus, VBus.net and others are trademarks or registered trademarks of RESOL - Elektronische Regelungen GmbH.
* Le sketch d'origine provient de M. Wiperman, http://danielwippermann.github.io
*
* Vbus 9600 bauds 8 bits pas de parité un stop bit
* Vbus ttl avec un opto coupleur SFH6206 une resistance de 4,7k en série avec led, coté transistor, collecteur sur +5V,emeteur une résistance pulldown de 3,3K vers la masse
* | C E | 40 @ Ic/Iin Iin 1mA
* | |
* .| in in|
* si pas de vbus -> affiche timeout -> ok
* sonde dht22 pour température et humidité local
* test: curl http://192.168.1.140/vbus.json
* switch debug rs232: curl http://192.168.1.141/disp_rs232.json
http://192.168.1.141/temperatures.json
modif ligne 230 inversion poid fort et faible et _ manquant
*/
#include <SoftwareSerial.h>
#include <EtherCard.h> //doc: https://www.aelius.com/njh/ethercard/
#include <DHT.h>
#define VERSION "0.1.04"
#define DHTPIN 7
#define soft_rxd_pin 8
#define LED 2
#define DHTTYPE DHT22 // DHT 22
DHT dht(DHTPIN, DHTTYPE);
#define FLength 6 // Framelength
#define FOffset 10 // Offset start of Frames
#define FSeptet 4 // Septet byte in Frame
#define LED 2
#define timerInterval 1500
SoftwareSerial Soft_serial(soft_rxd_pin, 9,false);
#define debug 1
//ENC28J60 ethernet
// Ethernet uses 10,11,12,13
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x34 };
#define STATIC 1 // DHCP=0 , static=1
#define version 0.1.0
#if STATIC
static byte myip[] = { 192,168,1,141 }; // static ip address
#endif
byte Ethernet::buffer[400]; // tcp ip send and receive buffer
BufferFiller bfill;
#define FLOATEMIT
const char header_json[] PROGMEM =
"HTTP/1.0 200 OK\r\n"
"content-Type: application/json;charset=utf-8\r\n"
"\r\n";
const char header_json2[] PROGMEM =
"HTTP/1.0 200 OK\r\n"
"content-Type: application/json;charset=utf-8\r\n"
"Refresh: 5\r\n"
"\r\n";
const char header_refresh[] PROGMEM = "Refresh: 5\r\n";
const char page_help[] PROGMEM =
"HTTP/1.0 200 OK\r\n"
"content-Type: application/json;charset=utf-8\r\n"
"\r\n"
"{\"help\":{ "
"\"compil_date\": \"" __DATE__"\","
"\"compil_time\": \"" __TIME__"\","
"\"src_file\": \"" __FILE__"\","
"\"version\": \""VERSION"\""
"}}\r\n";
const char error_json[] PROGMEM =
"HTTP/1.0 404 Not Found\r\n"
"content-Type: application/json;charset=utf-8\r\n"
"\r\n"
"{\"vbus\":{ \"error\": 404}}"
"\r\n";
bool all;
bool disp_rs232 = false;
bool refresh = false;
unsigned char Buffer[75];
char char_buffer[80];
long lastTimeTimer;
//long timerInterval =600;
//int temp;
byte i;
uint16_t networkaddress;
byte nb_crc_error;
char sensor1_tempc [6];
char sensor2_tempc [6];
char sensor3_tempc [6];
char sensor4_tempc [6];
char refresh_string [] = "false";
//char * refresh_string ;
char temp_VFD [6];
char PumpSpeed1; // in %
char PumpSpeed2; // in %
char RelaisMask;
char ErrorMask;
char Scheme;
uint16_t OperatingHoursRelais1;
uint16_t OperatingHoursRelais2;
uint32_t HeatQuantity;
uint16_t Version;
uint16_t OperatingHoursRelais1Today;
uint16_t SystemTime;
uint16_t flow_VFD;
unsigned int Destination_address;
unsigned int Source_address;
unsigned char ProtocolVersion;
unsigned int Command;
unsigned char Framecnt;
unsigned char Septet;
unsigned char Checksum;
unsigned char calc_Checksum;
unsigned char tot_crc_error =0;
void setup() {
pinMode(LED, OUTPUT);
digitalWrite(LED, HIGH);
dht.begin();
Serial.println("Arduino debugging started");
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Go!");
// set the data rate for the SoftwareSerial port
Soft_serial.begin(9600);
//ether.begin(sizeof Ethernet::buffer, mymac);
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
Serial.println( "Failed to access Ethernet controller");
#if STATIC
ether.staticSetup(myip);
Serial.println("Static IP");
#else
if (!ether.dhcpSetup())
Serial.println("DHCP failed");
#endif
ether.printIp("IP: ", ether.myip);
digitalWrite(LED, LOW);
}
void loop() {
//String refresh_string = " ";
word pos = ether.packetLoop(ether.packetReceive());
// check if valid tcp data is received
if (pos) {
char* data = (char *) Ethernet::buffer + pos;
#if debug
Serial.println(data);
#endif
if (strncmp("GET / ", data, 6) == 0) {
//Serial.println("http Get /");
ether.httpServerReplyAck(); // send ack to the request
memcpy_P(ether.tcpOffset(), page_help, sizeof page_help);//only the first part will sended
ether.httpServerReply_with_flags(sizeof page_help - 1,TCP_FLAGS_ACK_V|TCP_FLAGS_FIN_V);
}
else
if (strncmp("GET /vbus.json", data, 14) == 0) {
//Serial.println("http GET /vbus.json");
//digitalWrite(LED, HIGH);
if (VBusRead(0x10)){
//Serial.println(temp);
nb_crc_error = extract_septep();
tot_crc_error += nb_crc_error;
bfill = ether.tcpOffset();
if (refresh) {
bfill.emit_p(PSTR("$F"),header_json2);
strcpy(refresh_string, "true");
}
else {
bfill.emit_p(PSTR("$F"),header_json);
strcpy(refresh_string, "false");
}
bfill.emit_p(PSTR(
"{\"Vbus\":{"
"\"src_address\":\"0x$H$H\","
"\"t_1\":$S,"
"\"t_2\":$S,"
"\"t_3\":$S,"
"\"t_4\":$S,"
"\"t_VFD\":$S,"
"\"VFD_flow\":$D,"
"\"Vpump1\":$D,"
"\"heures_P1\":$D,"
"\"Vpump2\":$D,"
"\"heures_P2\":$D,"
"\"heure\":\"$D:$D\","
"\"P_tot\":$L,"
"\"ErrorMask\":\"0x$H\","
"\"nb_crc_error\":$D,"
"\"tot_crc_error\":$D,"
"\"refresh\":\"$S\""
"}}"
),Buffer[4],Buffer[3],sensor1_tempc,sensor2_tempc,sensor3_tempc,sensor4_tempc,temp_VFD,flow_VFD,
PumpSpeed1,OperatingHoursRelais1,PumpSpeed2,OperatingHoursRelais2, SystemTime/60,SystemTime%60,
HeatQuantity, ErrorMask,nb_crc_error, tot_crc_error,refresh_string);
ether.httpServerReply(bfill.position());
}
else {
bfill = ether.tcpOffset();
bfill.emit_p(PSTR("{\"Vbus\":{\"error\" :\"timeout\" }}"));
ether.httpServerReply(bfill.position());
}
}
else
if (strncmp("GET /infos_10.json", data, 17) == 0) {
//Serial.println("GET /infos_15.json");
byte vbusread_code;
vbusread_code = VBusRead(0x10);
if (vbusread_code){
extract_septep();
bfill = ether.tcpOffset();
bfill.emit_p(PSTR(
"$F{\"Vbus\":{"
"\"vbusread code\":$D,"
"\"sync\":\"0x$H\","
"\"Src address\":\"0x$H$H\","
"\"Dst address\":\"0x$H$H\","
"\"22\":$D,"
"\"23\":$D,"
"\"24\":$D,"
"\"25\":$D,"
"\"28\":$D,"
"\"29\":$D,"
"\"30\":$D,"
"\"31\":$D,"
"\"34\":$D,"
"\"35\":$D,"
"\"35\":$D,"
"\"37\":$D"
"}}"
),header_json,vbusread_code,Buffer[0],Buffer[2],Buffer[1],Buffer[3],Buffer[4],
Buffer[22],Buffer[23],Buffer[24],Buffer[25],
Buffer[28],Buffer[29],Buffer[30],Buffer[31],
Buffer[34],Buffer[35],Buffer[36],Buffer[37]
);
ether.httpServerReply(bfill.position());
}
}
else
if (strncmp("GET /infos.json", data, 14) == 0) {
byte vbusread_code;
vbusread_code = VBusRead(0x10);
if (vbusread_code){
//Serial.println(temp);
bfill = ether.tcpOffset();
bfill.emit_p(PSTR(
"$F{\"Vbus\":{"
"\"vbusread code\":$D,"
"\"sync\":\"0x$H\","
"\"Src address\":\"0x$H$H\","
"\"Dst address\":\"0x$H$H\","
"\"Protocol Version\":\"0x$H\","
"\"Command:=\":\"0x$H$H\","
"\"Framecount\":$D,"
"\"Checksum\":\"0x$H\","
"\"calc Checksum\":\"0x$H\""
"}}"
),header_json,vbusread_code,Buffer[0],Buffer[2],Buffer[1],Buffer[3],Buffer[4],ProtocolVersion,Buffer[6],Buffer[7],Framecnt,temp_VFD,Buffer[9],VBus_CalcCrc(Buffer, 1, 8));
ether.httpServerReply(bfill.position());
}
}
else
if (strncmp("GET /temperatures.json", data, 22) == 0) {
bfill = ether.tcpOffset();
char char_value[5];
bfill.emit_p(PSTR("$F{\"dht22\":"),header_json);
dtostrf(dht.readHumidity(),5,1 ,char_value);
bfill.emit_p(PSTR("{\"humidity\":$S,"),char_value);
dtostrf(dht.readTemperature(),5,1 ,char_value);
bfill.emit_p(PSTR("\"temperature\":$S"), char_value);
bfill.emit_p(PSTR("}}"));
ether.httpServerReply(bfill.position());
}
else
if (strncmp("GET /disp_rs232.json", data, 20) == 0) {
bfill = ether.tcpOffset();
bfill.emit_p(PSTR("$F{\"disprs232\":"),header_json);
disp_rs232 = (! disp_rs232);
if (disp_rs232)
bfill.emit_p(PSTR("\"true\"}"));
else
bfill.emit_p(PSTR("\"false\"}"));
ether.httpServerReply(bfill.position());
}
else
if (strncmp("GET /refresh.json", data, 17) == 0) {
bfill = ether.tcpOffset();
bfill.emit_p(PSTR("$F{\"refresh\":"),header_json);
refresh = (! refresh);
if (refresh)
bfill.emit_p(PSTR("\"true\"}"));
else
bfill.emit_p(PSTR("\"false\"}"));
ether.httpServerReply(bfill.position());
}
else {
ether.httpServerReplyAck(); // send ack to the request
memcpy_P(ether.tcpOffset(), error_json, sizeof error_json);//only the first part will sended
ether.httpServerReply_with_flags(sizeof error_json - 1,TCP_FLAGS_ACK_V|TCP_FLAGS_FIN_V);
}
} //if (strncmp("GET /vbus.json"
}
byte VBusRead (byte DFA) {
/*code sortie 0 no data
* 3 sortie sur deuxieme Sync
* 2 trop de données
*/
byte c;
bool start;
bool DFA_ok = false;
lastTimeTimer = millis();
int index;
index = 0;
while (index < 73) {
if (Soft_serial.available()) {
c= (byte) Soft_serial.read();
lastTimeTimer = millis();
if (c == 0xAA) {
if (DFA_ok) {
digitalWrite(LED, HIGH);
return 3;
}
index = 0;
Buffer[0]=c;
#if debug
Serial.println("Sync found");
#endif
digitalWrite(LED, LOW);
}
else
if (index == 1){
if (c == DFA)
DFA_ok = true;
}
if (DFA_ok) {
Buffer[index]=c;
#if debug
char s[7];
sprintf(s, "%02X ",c);
Serial.print(s);
#endif
}
index ++;
}
if ((timerInterval > 0) && (millis() - lastTimeTimer > timerInterval ) ) {
if (disp_rs232) {
Serial.print("time_out ");
Serial.println(lastTimeTimer);
}
return 0;
}
}
digitalWrite(LED, LOW);
return 2;
} //byte VBusRead (byte DFA)
byte extract_septep () {
byte F;
byte nb_crc_error =0;
//0 SYNC byte 0XAA
//1 2 dest address
//3 4 source address
//5 protocol verion
//6 7 command
//8 Number of payload frames
//9 Checksum for offset 1-8
Destination_address = Buffer[2] << 8 | Buffer[1];
Source_address = Buffer[4] << 8 | Buffer[3];
Command = Buffer[7] << 8 | Buffer[6];
ProtocolVersion = (Buffer[5]>>4) + (Buffer[5] &(1<<15));
Framecnt = Buffer[8];
Checksum = Buffer[9];
calc_Checksum = VBus_CalcCrc(Buffer, 1, 8);
///******************* Frame 1 *******************
F=FOffset;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum)
nb_crc_error ++;
else {
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
dtostrf(CalcTemp(Buffer[F+1], Buffer[F]) ,5,1 ,sensor1_tempc);
dtostrf(CalcTemp(Buffer[F+3], Buffer[F+2]),5,1 ,sensor2_tempc);
}
///******************* Frame 2 *******************
F=FOffset+FLength;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum)
nb_crc_error ++;
else {
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
dtostrf(CalcTemp(Buffer[F+1], Buffer[F]) ,5,1 ,sensor3_tempc);
dtostrf(CalcTemp(Buffer[F+3], Buffer[F+2]),5,1 ,sensor4_tempc);
}
///******************* Frame 3 *******************
F=FOffset+FLength*2;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum) {
nb_crc_error ++;
PumpSpeed1 =255;
}
else {
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
PumpSpeed1 = ((Buffer[F] & 0X7F));
OperatingHoursRelais1=Buffer[F+3] << 8 | Buffer[F+2];
}
///******************* Frame 4 *******************
F=FOffset+FLength*3;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum) {
nb_crc_error ++;
PumpSpeed2 =255;
}
else {
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
PumpSpeed2 = (Buffer[F+0] & 0X7F);
OperatingHoursRelais2=Buffer[F+3] << 8| Buffer[F+2];
}
///******************* Frame 5 *******************
F=FOffset+FLength*4;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum)
nb_crc_error ++;
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
Scheme = Buffer[F+1];
///******************* Frame 6 *******************
F=FOffset+FLength*5;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum)
nb_crc_error ++;
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
ErrorMask= Buffer[F];
SystemTime = Buffer[F+3] << 8 | Buffer[F+2];
///******************* Frame 7 *******************
F=FOffset+FLength*6;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum)
nb_crc_error ++;
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
///******************* Frame 8 *******************
F=FOffset+FLength*7;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum)
nb_crc_error ++;
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
//HeatQuantity = (Buffer[F + 3] << 24| Buffer[F+2] << 16 | Buffer[F + 1] << 8 | Buffer[F]);
HeatQuantity = (Buffer[F+2] * 65535) + word ((Buffer[F + 1] << 8 | Buffer[F]));
//HeatQuantity = (Buffer[F + 3] << 8 | Buffer[F+2]) + word ((Buffer[F + 1] << 8 | Buffer[F]));
///******************* Frame 9 *******************
F=FOffset+FLength*8;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum)
nb_crc_error ++;
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
Version=Buffer[F+1] << 8| Buffer[F];
///******************* Frame 10 *******************
F=FOffset+FLength*9;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
if (Buffer[F+5] != calc_Checksum)
nb_crc_error ++;
Septet=Buffer[F+FSeptet];
InjectSeptet(Buffer,F,4);
dtostrf(CalcTemp(Buffer[F+1], Buffer[F]) ,5,1 ,temp_VFD);
flow_VFD=Buffer[F+3] << 8| Buffer[F+2];
///******************* End of frames ****************
if (disp_rs232) {
for (int i = 0; i < 10; i++) {
F=FOffset+FLength * i;
serial_print_buffer(Buffer,F,i);
}
}
return nb_crc_error;
} //byte extract_septep ()
// This function converts 2 data bytes to a temperature value.
float 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;
return (float)((float) v * 0.1);
}
unsigned char VBus_CalcCrc(const unsigned char *Buffer, int Offset, int Length) {
//source: VBus Protocol Specification page 9
unsigned char Crc = 0x7F;
for (int i = 0; i < Length; i++) {
Crc = (Crc - Buffer [Offset + i]) & 0x7F;
}
return Crc;
}
// The following is needed for decoding the data
void InjectSeptet(unsigned char *Buffer, int Offset, int Length) {
for (unsigned int i = 0; i < Length; i++) {
if (Septet & (1 << i)) {
Buffer [Offset + i] |= 0x80;
}
}
}
void serial_print_buffer (unsigned char *Buffer,int F, int i) {
byte calc_Checksum;
calc_Checksum = VBus_CalcCrc(Buffer, F, 5);
sprintf(char_buffer,"frame %02d: %03d %03d %03d %03d",i,Buffer[F],Buffer[F+1],Buffer[F+2],Buffer[F+3]);
Serial.print(char_buffer);
if (Buffer[F+5] == calc_Checksum)
Serial.println(" checksum: OK");
else {
Serial.print(Buffer[F+5]);
Serial.print(" ");
Serial.println(calc_Checksum);
}
}
// Frame info for the Resol resol CS4 and CS plus
// modidied by Dimi
// voir doc deltasol bs+ id: 2211
// http://danielwippermann.github.io/resol-vbus/#/#0010_1122_0100
// protocole: les bits de poids forts sont à zéro, il sont contenus dans le 5 e octet
// T° exemple 19,9° 199 D 0 D 28,6° 30 D 1 D 30 * 0.1 + 2.26 * 30
//-1,2° 244D 255D
//Offset Size Mask Name Factor Unit
//frame 0
//0 SYNC byte 0xAA
//1 LSB dest address appelé DFA valeur 0x10 ou 0x15
//2 MSB dest address
//3 LSB src address
//4 MSB src address
//5 protocol version
//6 LSB command
//7 MSB command
// 0x0100 Packet contains data for slave
// 0x0100 Packet contains data for slave
// 0x0300 Request answer of slave
//Frame 1 pour dest adress 0x0010
//10 1 Temperature sensor 1 0.1 C°
//11 1 Temperature sensor 1 25.6 C°
//12 1 Temperature sensor 2 0.1 C°
//13 1 Temperature sensor 2 25.6 C°
//frame 2
//16 1 Temperature sensor 3 0.1 C°
//17 1 Temperature sensor 3 25.6 C°
//18 1 Temperature sensor 4 0.1 C°
//19 1 Temperature sensor 4 25.6 C°
//frame 3
//22 1 Pump speed pump %
//23 1 ?
//24 1 heure relai 1 1h
//25 1 heure relai 1 256
//frame 4
//28 2 vitesse relai 2 %
//29 ?
//30 1 heure relai 2 1
//31 1 heure relai 2 256
//frame 5
//34 1 unit type 1
//35 1 system 1
//37
//37
//frame 6
//40 0x01 sensor 1 defekt 1
//40 0x02 sensor 2 defekt 1
//40 0x04 sensor 3 defekt 1
//40 0x08 sensor 4 defekt 1
//40 0x10 sensor GFD 1
//41 error mask 256
//42 temps en minutes 1
//43 temps 256
//frame 7
//46 status mask 1
//47 status mask 256
//48 status mask 65536
//49 status mask 16777216
//frame 8
//52 compteur energie 1 wh
//53 compteur energie 256 wh
//54 compteur energie 65536 wh
//55 compteur energie 1 wh
//frame 9
//58 sw version 0.01
//59 sw version 2.56
//60
//61
//frame 10 a verifier -> ok
//64 Temp GFD 0.1
//65 temp GFD 2.56
//66 debit1 1 l/h
//67 debit1 256 l/h
// 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