//####################################################################################################### //## 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 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> 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