294 lines
16 KiB
C
294 lines
16 KiB
C
//#######################################################################################################
|
|
//## This Plugin is only for use with the RFLink software package ##
|
|
//## Plugin-42 UPM/Esic ##
|
|
//#######################################################################################################
|
|
/*********************************************************************************************\
|
|
* Dit protocol zorgt voor ontvangst van UPM/Esic weerstation buitensensoren
|
|
* Tevens alle sensoren die het UPM/Esic (W.H. Mandolyn International Ltd) protocol volgen waaronder:
|
|
* UPM, Esic, Emos, DVM, Clas Ohlson, Dickson
|
|
* WT260,WT260H,WT440H,WT450,WT450H,WDS500,RG700
|
|
*
|
|
* 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 UPM/Esic Weatherstation outdoor unit, (52/54 pulses, 36 bits, 433 MHz).
|
|
* Supports two packet formats
|
|
* --------------------------------------------------------------------------------------------
|
|
* FORMAT 1:
|
|
*
|
|
* ____Byte 0_____ ____Byte 1_____ ____Byte 2_____ ____Byte 3_____ _Nib4__
|
|
* 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 3 2 1 0
|
|
* x x x x c c c c d d y y b S S S s s s s s P P P p p p p p p p p z z C C
|
|
*
|
|
* x = Constant, 1100, probably preamble
|
|
* c = House Code (0 - 15)
|
|
* d = Device Code (1 - 4) ?
|
|
* y = ?
|
|
* b = Low battery indication
|
|
* S = Secondary value - Humidity/Wind direction (high bits)
|
|
* s = Secondary value - Humidity/Wind direction (low bits)
|
|
* P = Primary value - Temperature/Rain/Wind speed value (high bits)
|
|
* p = Primary value - Temperature/Rain/Wind speed value (low bits)
|
|
* z = Sequence number 0 - 2. Messages are sent in bursts of 3. For some senders this is always 0
|
|
* C = Checksum. bit 1 is XOR of odd bits, bit 0 XOR of even bits in message
|
|
*
|
|
* If HouseCode = 10 and deviceCode = 2, then p and P is Wind speed
|
|
* and h and H is Wind direction
|
|
*
|
|
* If HouseCode = 10 and deviceCode = 3, then p and P is rain
|
|
*
|
|
* Temp (C) = RawValue / 16 - 50
|
|
* Rain (total mm) = RawValue * 0,7
|
|
* Wind Speed (mph)= RawValue (* 1/3,6 for km/h)
|
|
* Humidity (%) = RawValue / 2
|
|
* Wind direction (deg) = RawValue * 22,5
|
|
* --------------------------------------------------------------------------------------------
|
|
* FORMAT 2:
|
|
*
|
|
* ____Byte 0_____ ____Byte 1_____ ____Byte 2_____ ____Byte 3_____ _Nib4__
|
|
* 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 3 2 1 0
|
|
* x x x x c c c c d d y y b h h h h h h h T T T T T T T T t t t t t t t p
|
|
*
|
|
* x = Constant, 1100, probably preamble
|
|
* c = House Code (0 - 15)
|
|
* d = Device (Channel) Code (1 - 4) ?
|
|
* y = ?
|
|
* b = ?
|
|
* h = Humidity (7 bits) (0111011 = 59 %)
|
|
* T = Temperature (8 bits) (see below)
|
|
* t = Temperature (7 bits) (see below)
|
|
* p = Parity (xor of all bits should give 0)
|
|
*
|
|
* The temperature is transmitted as (temp + 50.0) * 128, which equals (temp * 128) + 6400. Adding 50.0 °C makes
|
|
* all values positive, an unsigned 15 bit integer where the first 8 bits correspond to the whole part of the temperature
|
|
* (here 01001001, decimal 73, substract 50 = 23). Remaining 7 bits correspond to the fractional part.
|
|
* Sample:
|
|
* 20;64;DEBUG;Pulses=52;Pulses(uSec)=875,875,825,875,1725,1800,1725,1800,1725,850,825,1800,1725,875,800,850,825,1800,1725,1800,800,875,800,850,1725,1800,825,850,1725,850,825,1800,1725,1800,825,850,1725,875,800,1800,800,875,800,850,825,850,1725,1800,1750,1800,475;
|
|
* 11000001 00110000 11100100 01100000 0010
|
|
*
|
|
*20;C3;DEBUG;Pulses=52;Pulses(uSec)=950,975,850,975,1850,1975,1875,1975,1850,975,850,1975,1850,975,850,975,850,2000,1850,975,875,975,850,975,850,2000,850,975,1850,2000,850,975,1850,2000,850,975,1875,975,850,975,850,975,850,2000,1850,1975,1850,2000,1850,1975,225;
|
|
*20;C4;DEBUG;Pulses=52;Pulses(uSec)=950,975,850,975,1850,2000,1875,2000,1850,975,850,2000,1850,975,850,975,850,2000,1850,975,875,950,875,975,850,2000,850,975,1850,2000,850,975,1850,2000,850,975,1875,975,850,975,850,975,850,2000,1850,2000,1850,2000,1850,2000,225;
|
|
*20;C5;DEBUG;Pulses=52;Pulses(uSec)=950,975,850,975,1850,2000,1875,2000,1850,975,850,2000,1850,975,850,975,850,2000,1850,975,875,975,850,975,850,1975,850,975,1850,1975,850,975,1850,2000,850,975,1875,975,850,975,850,975,850,2000,1850,2000,1850,2000,1850,2000,225;
|
|
*20;32;DEBUG;Pulses=48;Pulses(uSec)=850,900,875,900,1850,1875,1850,1875,1850,900,875,1875,1850,900,875,900,875,1875,1850,900,875,1875,1850,1875,1825,900,875,1875,875,900,1850,1875,875,900,875,900,1850,1875,1825,1875,1850,1875,1850,1875,1825,1875,500;
|
|
*20;33;UPM/Esic;ID=0001;TEMP=0104;HUM=33;BAT=OK;
|
|
|
|
925,900,875,900,1825,1875,1850,1875,1850,900,
|
|
875,1875,1850,900,850,900,850,1875,1850,900,
|
|
850,1875,1850,900,850,1875,1850,1875,875,900,
|
|
1850,1875,875,900,875,900,1825,1875,1850,900,
|
|
875,1875,1850,900,875,1875,850,900,875,900,
|
|
500 51
|
|
0011111011 0011010111 0110011101 10100
|
|
\*********************************************************************************************/
|
|
#define UPM_MIN_PULSECOUNT 46
|
|
#define UPM_MAX_PULSECOUNT 56
|
|
|
|
#define UPM_PULSELOHI 1100/RAWSIGNAL_SAMPLE_RATE
|
|
#define UPM_PULSEHIHI 2075/RAWSIGNAL_SAMPLE_RATE
|
|
#define UPM_PULSEHILO 1600/RAWSIGNAL_SAMPLE_RATE
|
|
|
|
#ifdef PLUGIN_042
|
|
boolean Plugin_042(byte function, char *string) {
|
|
if (RawSignal.Number < UPM_MIN_PULSECOUNT || RawSignal.Number > UPM_MAX_PULSECOUNT) return false;
|
|
|
|
byte rc=0;
|
|
unsigned long bitstream1=0L; // holds first 10 bits
|
|
unsigned long bitstream2=0L; // holds last 26 bits
|
|
|
|
int temperature=0;
|
|
int humidity=0;
|
|
int rain=0;
|
|
int winds=0;
|
|
int windd=0;
|
|
byte battery=0;
|
|
byte units=0;
|
|
byte devicecode=0;
|
|
byte checksum=0;
|
|
byte bitcounter=0; // counts number of received bits (converted from pulses)
|
|
byte halfbit=0; // high pulse = 1, 2 low pulses = 0, halfbit keeps track of low pulses
|
|
byte msgformat=0;
|
|
//==================================================================================
|
|
for(int x=1;x <RawSignal.Number;x++) { // get bytes
|
|
if ((RawSignal.Pulses[x] > UPM_PULSEHILO) && (RawSignal.Pulses[x] < UPM_PULSEHIHI)) {
|
|
if (halfbit==1) { // UPM cant receive a 1 bit after a single low value
|
|
return false; // pulse error, must not be a UPM packet or reception error
|
|
}
|
|
if (bitcounter < 10) {
|
|
bitstream1 = (bitstream1 << 1);
|
|
bitcounter++; // only need to count the first 10 bits
|
|
} else {
|
|
bitstream2 = (bitstream2 << 1);
|
|
bitcounter++; // only need to count the first 10 bits
|
|
}
|
|
halfbit=0; // wait for next first low or high pulse
|
|
} else {
|
|
if ((RawSignal.Pulses[x] > UPM_PULSELOHI) ) return false; // Not a valid UPM pulse length
|
|
if (halfbit == 0) { // 2 times a low value = 0 bit
|
|
halfbit=1; // first half received
|
|
} else {
|
|
if (bitcounter < 10) {
|
|
bitstream1 = (bitstream1 << 1) | 0x1;
|
|
bitcounter++; // only need to count the first 10 bits
|
|
} else {
|
|
bitstream2 = (bitstream2 << 1) | 0x1;
|
|
bitcounter++; // only need to count the first 10 bits
|
|
}
|
|
halfbit=0; // wait for next first low or high pulse
|
|
}
|
|
}
|
|
if (bitcounter > 36) return false; // too many bits, it cant be the right protocol
|
|
}
|
|
//==================================================================================
|
|
if ( (bitstream1 >> 6 ) != 0x0c ) return false; // sanity check, first 4 bits should always be '1100' to be a valid UPM/Esic packet
|
|
if ( bitstream1 == 0x00 ) return false; // sanity check
|
|
if ( bitstream2 == 0x00 ) return false; // sanity check
|
|
//==================================================================================
|
|
for (byte i=0;i<9;i=i+2){ // perform a checksum check to make sure the packet is a valid UPM/Esic packet
|
|
checksum=checksum ^ ((bitstream1 >> i) &3); // Checksum - xor all odd and all even bits should match the last two bits
|
|
}
|
|
for (byte i=2;i<25;i=i+2){
|
|
checksum=checksum ^ ((bitstream2 >> i) &3);
|
|
}
|
|
if (checksum == (bitstream2 &3 )) { // did the format 1 checksum calculation match?
|
|
msgformat=1; // Yes, set it
|
|
} else { // else perform a bit parity check to see if we have format 2
|
|
checksum=checksum ^ ((bitstream2) &3); // xor the last 2 bits
|
|
units = (checksum >> 1) & 0x01; // get the odd bit of the checksum result
|
|
checksum=(checksum & 0x01) ^ units; // xor the odd with the even bit of the checksum result
|
|
if (checksum == 0) { // did the format 2 parity checksum calculation match?
|
|
msgformat=2;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
//==================================================================================
|
|
// Prevent repeating signals from showing up
|
|
//==================================================================================
|
|
if( (SignalHash!=SignalHashPrevious) || ((RepeatingTimer+1000<millis()) && (SignalCRC != bitstream1)) || (SignalCRC != bitstream1) ) {
|
|
SignalCRC=bitstream1; // not seen the RF packet recently
|
|
} else {
|
|
return true; // already seen the RF packet recently
|
|
}
|
|
//==================================================================================
|
|
// now process the various sensor types
|
|
//==================================================================================
|
|
units=bitstream1 & 0x03; // housecode format 1&2
|
|
rc=units; // housecode format 1&2
|
|
devicecode=(bitstream1 >> 2) & 0x0f; // devicecode format 1&2
|
|
//==================================================================================
|
|
if (msgformat==1) {
|
|
battery = (bitstream2 >> 23) & 1; // battery state 1=low 0=ok
|
|
if ((rc==10) && (devicecode==2)) { // wind
|
|
units = (bitstream2 >> 4) & 0x0f;
|
|
winds = (bitstream2 >> 8) & 0x7f;
|
|
windd = (bitstream2 >> 15) & 0x0f; //0xff; // wind direction
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";UPM/Esic;ID=")); // Label
|
|
PrintHexByte(rc);
|
|
PrintHexByte(devicecode);
|
|
// ----------------------------------
|
|
sprintf(pbuffer, ";WINSP=%02x;", winds);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer, "WINDIR=%04d;", windd);
|
|
Serial.print( pbuffer );
|
|
if (battery==1) { // battery status
|
|
Serial.print(F("BAT=LOW;"));
|
|
} else {
|
|
Serial.print(F("BAT=OK;"));
|
|
}
|
|
Serial.println();
|
|
//==================================================================================
|
|
} else
|
|
if ((rc==10) && (devicecode==3)) { // rain
|
|
units = (bitstream2 >> 4) & 0x0f;
|
|
rain = (bitstream2 >> 8) & 0x7f;
|
|
rain = rain * 7; // Serial.print( (float)rain * 0.7 );
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";UPM/Esic;ID=")); // Label
|
|
PrintHexByte(rc);
|
|
PrintHexByte(devicecode);
|
|
// ----------------------------------
|
|
sprintf(pbuffer, ";RAIN=%04x;", rain);
|
|
Serial.print( pbuffer );
|
|
if (battery==1) { // battery status
|
|
Serial.print(F("BAT=LOW;"));
|
|
} else {
|
|
Serial.print(F("BAT=OK;"));
|
|
}
|
|
Serial.println();
|
|
//==================================================================================
|
|
} else { // temperature & Humidity
|
|
units = (bitstream2 >> 4) & 0x0f; // temperature
|
|
temperature = (bitstream2 >> 8) & 0x7f;
|
|
temperature = temperature-50;
|
|
temperature = (temperature*10) + units;
|
|
if (temperature > 0x3e8) return false;
|
|
humidity = (bitstream2 >> 15) & 0xff; // humidity
|
|
humidity = humidity / 2;
|
|
//if (humidity==0) return false; // dont accept Bad humidity status
|
|
//if (temperature > 1000) return false; // dont accept bad temperature
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";UPM/Esic;ID=")); // Label
|
|
PrintHexByte(rc);
|
|
PrintHexByte(devicecode);
|
|
// ----------------------------------
|
|
sprintf(pbuffer,";TEMP=%04x;", temperature);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer,"HUM=%02d;", humidity); // Humidity 0x15 = 21% decimal
|
|
Serial.print( pbuffer );
|
|
if (battery==1) { // battery status
|
|
Serial.print(F("BAT=LOW;"));
|
|
} else {
|
|
Serial.print(F("BAT=OK;"));
|
|
}
|
|
Serial.println();
|
|
//==================================================================================
|
|
}
|
|
} else {
|
|
units = (bitstream2 >> 1) & 0x7f; // temperature
|
|
temperature = (bitstream2 >> 8) & 0xff;
|
|
temperature = temperature-50;
|
|
temperature = (temperature*100) + units;
|
|
temperature = temperature/10;
|
|
if (temperature > 0x3e8) return false;
|
|
humidity = (bitstream2 >> 16) & 0x7f; // humidity
|
|
//==================================================================================
|
|
// Output
|
|
// ----------------------------------
|
|
Serial.print("20;");
|
|
PrintHexByte(PKSequenceNumber++);
|
|
Serial.print(F(";UPM/Esic F2;ID=")); // Label
|
|
PrintHexByte(rc);
|
|
PrintHexByte(devicecode);
|
|
// ----------------------------------
|
|
sprintf(pbuffer,";TEMP=%04x;", temperature);
|
|
Serial.print( pbuffer );
|
|
sprintf(pbuffer,"HUM=%02x;", humidity);
|
|
Serial.print( pbuffer );
|
|
Serial.println();
|
|
//==================================================================================
|
|
}
|
|
//==================================================================================
|
|
RawSignal.Repeats=true; // suppress repeats of the same RF packet
|
|
RawSignal.Number=0;
|
|
return true;
|
|
}
|
|
#endif // PLUGIN_042
|