314 lines
16 KiB
C
314 lines
16 KiB
C
//#######################################################################################################
|
|
//## This Plugin is only for use with the RFLink software package ##
|
|
//## Plugin-34 Cresta ##
|
|
//#######################################################################################################
|
|
/*********************************************************************************************\
|
|
* Dit protocol zorgt voor ontvangst van Cresta temperatuur weerstation buitensensoren
|
|
* Tevens alle sensoren die het Cresta (Hideki) protocol volgen waaronder:
|
|
* Hideki, TFA Nexus, Mebus, Irox, Irox-Pro X, Honeywell, Cresta TE923, TE923W, TE821W,
|
|
* WXR810, DV928, Ventus W906
|
|
*
|
|
* Author : StuntTeam
|
|
* Support : http://sourceforge.net/projects/rflink/
|
|
* License : This code is free for use in any open source project when this header is included.
|
|
* Usage of any parts of this code in a commercial application is prohibited!
|
|
*********************************************************************************************
|
|
* Changelog: v1.0 initial release
|
|
*********************************************************************************************
|
|
* Technical Information:
|
|
* Decodes signals from a Cresta Weatherstation outdoor unit, (29 pulses, 28 bits, 433 MHz).
|
|
* Message Format can be found at: http://members.upc.nl/m.beukelaar/Crestaprotocol.pdf
|
|
* Thermo/Hygro: 10 bytes, seen as 128, 132, 134, 136, 138 pulses
|
|
* Anemometer: 14 bytes
|
|
* UV Index: 11 bytes
|
|
* Rain: 9 bytes, seen as 132, 136 pulses
|
|
\*********************************************************************************************/
|
|
#define CRESTA_MIN_PULSECOUNT 124 // unknown until we have a collection of all packet types but this seems to be the minimum
|
|
#define CRESTA_MAX_PULSECOUNT 284 // unknown until we have a collection of all packet types
|
|
#define CRESTA_PULSEMID 700/RAWSIGNAL_SAMPLE_RATE
|
|
|
|
#ifdef PLUGIN_034
|
|
byte Plugin_034_reverseBits(byte data);
|
|
byte Plugin_034_WindDirSeg(byte data);
|
|
|
|
boolean Plugin_034(byte function, char *string){
|
|
if ((RawSignal.Number < CRESTA_MIN_PULSECOUNT) || (RawSignal.Number > CRESTA_MAX_PULSECOUNT) ) return false;
|
|
|
|
int sensor_data=0;
|
|
int windtemp=0;
|
|
int windchill=0;
|
|
int windgust=0;
|
|
int windspeed=0;
|
|
int winddirection=0;
|
|
int uv=0;
|
|
|
|
byte checksum=0;
|
|
byte data[18];
|
|
byte length=0;
|
|
byte channel=0;
|
|
int units=0;
|
|
byte battery=0;
|
|
|
|
byte bytecounter=0; // used for counting the number of received bytes
|
|
byte bitcounter=0; // counts number of received bits (converted from pulses)
|
|
int pulseposition=1; // first pulse is always empty
|
|
byte halfbit=0; // high pulse = 1, 2 low pulses = 0, halfbit keeps track of low pulses
|
|
byte parity=0; // to calculate byte parity
|
|
// ==================================================================================
|
|
// get bytes and determine if byte parity is set correctly for the cresta protocol on the fly
|
|
do {
|
|
//if(RawSignal.Pulses[pulseposition]*RawSignal.Multiply > 700) { // high value = 1 bit
|
|
if(RawSignal.Pulses[pulseposition] > CRESTA_PULSEMID) { // high value = 1 bit
|
|
if (halfbit==1) { // cant receive a 1 bit after a single low value
|
|
return false; // pulse error, must not be a Cresta packet or reception error
|
|
}
|
|
if (bitcounter==8) {
|
|
if (parity != 1) { // now receiving parity bit
|
|
return false; // parity error, must not be a Cresta packet or reception error
|
|
} else {
|
|
bitcounter=0; // reset for next byte
|
|
parity=0; // reset for next byte
|
|
halfbit=0; // wait for next first low or high pulse
|
|
bytecounter++; // 1 byte received
|
|
}
|
|
} else {
|
|
data[bytecounter] = (data[bytecounter] << 1) | 0x1; // 1 bit
|
|
parity=parity ^1; // update parity
|
|
bitcounter++; // received a bit
|
|
halfbit=0; // waiting for first low or a new high pulse
|
|
}
|
|
} else {
|
|
if (halfbit == 0) { // 2 times a low value = 0 bit
|
|
halfbit=1; // first half received
|
|
} else {
|
|
if (bitcounter==8) {
|
|
if (parity != 0) { // now receiving parity bit
|
|
return false; // parity error, must not be a Cresta packet or reception error
|
|
} else {
|
|
bitcounter=0; // reset for next byte
|
|
parity=0; // reset for next byte
|
|
halfbit=0; // wait for next first low or high pulse
|
|
bytecounter++; // 1 byte received
|
|
}
|
|
} else {
|
|
data[bytecounter] = (data[bytecounter] << 1); // 0 bit
|
|
parity=parity ^0; // update parity
|
|
bitcounter++; // received a bit
|
|
halfbit=0; // wait for next first low or high pulse
|
|
}
|
|
}
|
|
}
|
|
pulseposition++; // point to next pulse
|
|
if (pulseposition > RawSignal.Number) break; // reached the end? done processing
|
|
} while(bytecounter < 16); // receive maximum number of bytes from pulses
|
|
// ==================================================================================
|
|
// all bytes received, make sure checksum is okay
|
|
// ==================================================================================
|
|
for (byte i=0;i<bytecounter;i++) {
|
|
data[i] = Plugin_034_reverseBits( data[i] );
|
|
}
|
|
// get packet length
|
|
length=data[2] & 0x3f; // drop bits 6 and 7
|
|
length >>= 1; // drop bit 0
|
|
if (length > 20) return false; // Additional check for illegal packet lengths to protect against false positives.
|
|
if (length == 0) return false; // Additional check for illegal packet lengths to protect against false positives.
|
|
// Checksum: XOR of all bytes from byte 1 till byte length+2, should result in 0
|
|
checksum=0;
|
|
for (byte i=1;i< length+2 ;i++){
|
|
checksum=checksum^data[i];
|
|
}
|
|
if (checksum != 0) return false;
|
|
// ==================================================================================
|
|
// now process the various sensor types
|
|
// ==================================================================================
|
|
if ( data[1] > 0x1f && data[1] < 0x40) channel=1;
|
|
if ( data[1] > 0x3f && data[1] < 0x60) channel=2;
|
|
if ( data[1] > 0x5f && data[1] < 0x80) channel=3;
|
|
if ( data[1] > 0x7f && data[1] < 0xa0) channel=1; // no channel settings on Anemometer/rainmeter and uvsensor
|
|
if ( data[1] > 0x9f && data[1] < 0xc0) channel=4;
|
|
if ( data[1] > 0xbf && data[1] < 0xE0) channel=5;
|
|
data[3]=(data[3])&0x1f;
|
|
//==================================================================================
|
|
// Prevent repeating signals from showing up
|
|
//==================================================================================
|
|
unsigned long tempval=data[3];
|
|
tempval=((tempval)<<16)+((data[1])<<8)+channel;
|
|
if((SignalHash!=SignalHashPrevious) || (RepeatingTimer<millis() && SignalCRC != tempval) || (SignalCRC != tempval) ) {
|
|
SignalCRC=tempval; // not seen the RF packet recently
|
|
} else {
|
|
return true; // already seen the RF packet recently
|
|
}
|
|
// ----------------------------------
|
|
battery=(data[2])>>6;
|
|
// ----------------------------------
|
|
if (data[3] == 0x0c ) { // Anemometer
|
|
units=((data[4]>>4)*10) + (data[4]&0x0f) ;
|
|
sensor_data=((data[5]&0x3f) * 100) + units;
|
|
if ((data[5] & 0x80) != 0x80) {
|
|
sensor_data = sensor_data | 0x8000; // set highest bit (minus bit)
|
|
}
|
|
windtemp=sensor_data;
|
|
|
|
units=((data[6]>>4)*10) + (data[6]&0x0f) ;
|
|
sensor_data=((data[7]&0x3f) * 100) + units;
|
|
if ((data[7] & 0x80) != 0x80) {
|
|
sensor_data = sensor_data | 0x8000; // set highest bit (minus bit)
|
|
}
|
|
windchill=sensor_data;
|
|
|
|
windspeed=((data[9]&0x0F)*100) + ((data[8]>>4)*10) + (data[8]&0x0F);
|
|
windgust=((data[10]>>4)*100) + ((data[10]&0x0F)*10) + (data[9]>>4);
|
|
|
|
//windspeed = (data[9] << 8) + data[8];
|
|
//windgust = (data[10] << 4) + ( (data[9] &0xf0) >> 4 );
|
|
|
|
winddirection = Plugin_034_WindDirSeg( ((data[11] &0xf0) >> 4) );
|
|
winddirection = winddirection & 0x0f; // make sure we dont get overflows
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";Cresta;ID=")); // Label
|
|
PrintHexByte(data[1]);
|
|
PrintHexByte(channel);
|
|
// ----------------------------------
|
|
sprintf(pbuffer, ";WINDIR=%04d;", winddirection);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer, "WINSP=%04x;", windspeed);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer, "WINGS=%04x;", windgust);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer, "WINTMP=%04x;", windtemp);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer, "WINCHL=%04x;", windchill);
|
|
Serial.print( pbuffer );
|
|
if ( battery != 0) {
|
|
Serial.print(F("BAT=OK;")); // Label
|
|
} else {
|
|
Serial.print(F("BAT=LOW;")); // Label
|
|
}
|
|
Serial.println();
|
|
//==================================================================================
|
|
} else
|
|
// ----------------------------------
|
|
if (data[3] == 0x0d ) { // UV Sensor
|
|
units=((data[4]>>4)*10) + (data[4]&0x0f) ;
|
|
sensor_data=((data[5]&0x3f) * 100) + units;
|
|
if ((data[5] & 0x80) != 0x80) {
|
|
sensor_data = sensor_data | 0x8000; // set highest bit (minus bit)
|
|
}
|
|
// UV sensor reports the temperature but does not report negative values!, skip temperature info?
|
|
uv = ((data[8] & 0x0f)<<8)+data[7];
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";Cresta;ID=")); // Label
|
|
PrintHexByte(data[1]);
|
|
PrintHexByte(channel);
|
|
// ----------------------------------
|
|
sprintf(pbuffer, ";TEMP=%04x;", sensor_data);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer, "UV=%04x;", uv);
|
|
Serial.print( pbuffer );
|
|
if ( battery != 0) {
|
|
Serial.print(F("BAT=OK;")); // Label
|
|
} else {
|
|
Serial.print(F("BAT=LOW;")); // Label
|
|
}
|
|
Serial.println();
|
|
//==================================================================================
|
|
} else // 9F 80 CC 4E 00 00 66 64
|
|
// ---------------------------------- // 9f 80 cc 4e 01 00 66 65
|
|
if (data[3] == 0x0e ) { // Rain meter // 9F 80 CC 4E 76 00 66 12
|
|
sensor_data = (data[5]<<8)+data[4]; // 80=rain 4e=>0E = rain
|
|
sensor_data=sensor_data*7; // 66 = always 66 rain units * 0.7 = mm.
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";Cresta;ID=")); // Label
|
|
PrintHexByte(data[1]);
|
|
PrintHexByte(channel);
|
|
// ----------------------------------
|
|
sprintf(pbuffer, ";RAIN=%04x;", sensor_data);
|
|
Serial.print( pbuffer );
|
|
if ( battery != 0) {
|
|
Serial.print(F("BAT=OK;"));
|
|
} else {
|
|
Serial.print(F("BAT=LOW;"));
|
|
}
|
|
Serial.println();
|
|
//==================================================================================
|
|
} else
|
|
// ----------------------------------
|
|
if (data[3] == 0x1e ) { // Thermo/Hygro
|
|
units=((data[4]>>4)*10) + (data[4]&0x0f) ;
|
|
sensor_data=((data[5]&0x3f) * 100) + units;
|
|
if ((data[5] & 0x80) != 0x80) {
|
|
sensor_data = sensor_data | 0x8000; // set highest bit (minus bit)
|
|
}
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";Cresta;ID=")); // Label
|
|
PrintHexByte(data[1]);
|
|
PrintHexByte(channel);
|
|
// ----------------------------------
|
|
sprintf(pbuffer, ";TEMP=%04x;", sensor_data);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer, "HUM=%02x;", data[6]);
|
|
Serial.print( pbuffer );
|
|
if ( battery != 0) {
|
|
Serial.print(F("BAT=OK;"));
|
|
} else {
|
|
Serial.print(F("BAT=LOW;"));
|
|
}
|
|
Serial.println();
|
|
//==================================================================================
|
|
} else {
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";Cresta;DEBUG;ID=")); // Label
|
|
PrintHexByte(data[1]);
|
|
PrintHexByte(channel);
|
|
// ----------------------------------
|
|
PrintHex8( data, length+2);
|
|
Serial.print(F(";"));
|
|
Serial.println();
|
|
//==================================================================================
|
|
}
|
|
RawSignal.Repeats=true; // suppress repeats of the same RF packet
|
|
RawSignal.Number=0;
|
|
return true;
|
|
}
|
|
|
|
// *********************************************************************************************
|
|
// * Reverse all bits in a byte
|
|
// *********************************************************************************************
|
|
byte Plugin_034_reverseBits(byte data) {
|
|
byte b = data;
|
|
for (byte i = 0; i < 8; ++i) {
|
|
data = (data << 1) | (b & 1);
|
|
b >>= 1;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
byte Plugin_034_WindDirSeg(byte data) {
|
|
// Encrypted using: a=-a&0xf; b=a^(a>>1);
|
|
data ^= (data & 8) >> 1; /* Solve bit 2 */
|
|
data ^= (data & 4) >> 1; /* Solve bit 1 */
|
|
data ^= (data & 2) >> 1; /* Solve bit 0 */
|
|
return -data & 0xf;
|
|
}
|
|
#endif // PLUGIN_034
|