/
Plugin_043.c
211 lines (202 loc) · 12.7 KB
/
Plugin_043.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//#######################################################################################################
//## This Plugin is only for use with the RFLink software package ##
//## Plugin-43 LaCrosse ##
//#######################################################################################################
/*********************************************************************************************\
* This plugin takes care of decoding LaCrosse weatherstation outdoor sensors
* It also works for all non LaCrosse sensors that follow this protocol.
* Lacrosse TX3-TH Thermo/Humidity, Lacrosse TX4, Lacrosse TX4U
* WS7000-15: Anemometer, WS7000-16: Rain precipitation, WS2500-19: Brightness Luxmeter, WS7000-20: Thermo/Humidity/Barometer
* TFA 30.3125 (temperature + humidity), TFA 30.3120.90 (temperature)
*
* 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 LaCrosse Weatherstation outdoor unit, (88 pulses, 44 bits, 433 MHz).
* Partially based on http://www.f6fbb.org/domo/sensors/tx3_th.php
*
* Temperature sensor (TX3)
* Each frame is 44 bits long. It is composed of:
* • 2 blocks of four bits = 0A (start sequence)
* • 8 blocks of four bits (data)
* • 1 block of four bits (checksum)
*
* The active values of the frames are explained below:
*
* Example
* 0000 1010 0000 0000 1110 0111 0011 0001 0111 0011 1101
* aaaa aaaa bbbb cccc cccd eeee ffff gggg hhhh iiii jjjj
* 0 A 0 0 7 0 7 3 1 7 3 D
*
* 000a0004070705030705
* 20;d8;LaCrosse;ID=0401;TEMP=00fd;
* 000a0e04070608000608
* 20;d9;LaCrosse;ID=0403;HUM=68;
*
* • a = Start sequence (always 0000 1010)
* • b = Packet type (0=Thermo E=hygro)
* • c = Address of sensor (changes when inserting batteries)
* • d = Parity bit (c+d+e bits sum is even)
* • e-i = Measured values:
* e = tens (x 10)
* f = ones (x 1)
* g = digits (x 0.1) (is zero in case of humidity)
* h = copy of e value
* i = copy of f value
* • j = Checksum (Lower four bits of the sum of all words)
*
* Checksum: (0 + A + 0 + 0 + E + 7 + 3 + 1 + 7 + 3) and F = D D
* Sample:
* 20;11;DEBUG;Pulses=88;Pulses(uSec)=1200,875,1125,875,1125,875,1125,900,400,900,1150,875,400,900,1150,875,1125,875,1125,875,1150,875,1150,875,400,900,400,875,375,900,1150,875,1125,875,400,900,1150,875,1125,875,1125,875,400,900,400,875,1125,900,400,875,1150,875,1150,900,1125,875,1150,875,400,900,400,875,400,900,1150,875,400,900,400,875,1125,875,400,900,1150,900,1125,875,1150,875,375,900,400,900,400,900,400;
* 20;9E;DEBUG;Pulses=88;Pulses(uSec)=1300,925,1225,925,1225,925,1200,925,425,925,1225,925,425,925,1225,925,1225,925,1225,925,1225,925,1225,925,1225,925,425,925,1225,925,1225,925,1225,925,425,925,425,925,1225,925,1225,925,425,925,425,925,425,925,1225,925,425,925,425,925,1225,925,425,925,1225,925,1225,925,1225,925,1225,925,425,925,425,925,425,925,1200,925,425,925,425,925,1225,925,1225,925,425,925,425,925,1225;
* 20;9F;LaCrosse;ID=0403;TEMP=010c;
* 20;A1;DEBUG;Pulses=88;Pulses(uSec)=1325,925,1225,925,1225,925,1225,925,425,925,1225,925,425,925,1225,925,425,925,425,925,425,925,1225,925,1225,925,425,925,1225,925,1225,925,1225,925,425,925,425,925,1225,925,1225,925,425,925,1225,925,425,925,1225,925,425,950,425,925,1225,925,1225,925,1225,925,1225,925,1225,925,1225,925,425,925,1225,925,425,925,1200,925,425,925,425,925,1225,925,425,925,1225,925,1225,925,1225;
* 20;A2;LaCrosse;ID=0403;HUM=56;
* 1275,925,1225,925,1225,925,1200,925,425,925,1225,925,425,925,1225,900,1225,925,1200,925,1225,925,1225,925,1225,925,425,925,1225,925,1200,925,1225,925,425,925,425,900,1225,900,1225,925,425,925,425,925,425,925,1225,900,425,925,1225,925,1225,925,1225,925,425,950,1225,900,425,950,1225,925,425,925,425,900,425,950,1200,900,425,925,1225,925,1225,925,425,925,425,925,425,925,425
20;76;DEBUG;Pulses=88;Pulses(uSec)=810,1440,1200,930,1200,960,1200,930,390,930,1200,930,420,960,1200,930,1200,930,1200,930,1200,930,1200,930,1200,930,420,930,1200,960,1200,930,1200,930,420,930,420,930,1200,930,1200,960,390,930,420,930,420,960,1200,930,390,930,1200,930,420,930,1200,960,1170,930,1200,930,420,930,1200,930,420,930,420,930,420,930,1200,930,420,930,1200,930,420,930,420,930,420,930,1200,930,420,6990;
0000 1010 0000 0100 0110 0111 0101 0001 0111 0101 1101
20;78;DEBUG;Pulses=88;Pulses(uSec)=240,1980,1200,960,1200,960,1200,960,390,930,1200,930,390,960,1200,960,390,930,420,930,420,930,1200,930,1200,960,390,930,1200,930,1200,930,1200,930,420,930,420,930,1200,930,1200,930,420,930,420,930,1200,930,1200,930,420,960,1200,960,390,960,1200,930,1200,930,1200,930,1200,930,1200,930,420,960,390,930,1200,930,1200,960,390,930,1200,930,420,930,420,930,1200,930,1200,930,1200,6990;
0;29;DEBUG;Pulses=86;Pulses(uSec)=1260,930,1200,930,1200,930,420,960,1200,930,420,930,1200,930,1200,930,1200,930,1200,930,1200,930,1200,930,390,930,1200,960,1200,960,1200,930,420,930,390,960,1200,960,1200,930,420,930,420,930,390,960,1200,960,390,930,1200,930,1200,930,1200,930,420,930,420,960,1200,960,1200,930,390,960,390,960,390,930,1200,960,420,930,1200,930,1200,930,1200,930,1200,930,1200,930,1200,6990;
20;2A;DEBUG;Pulses=50;Pulses(uSec)=1200,900,1140,870,390,900,360,900,390,900,1140,930,420,930,1200,930,1200,930,1200,930,420,930,390,960,1200,930,1200,930,390,930,420,960,390,930,1200,930,390,960,1200,960,1200,930,1200,960,1170,930,1200,930,1200,6990;
20;2B;DEBUG;Pulses=86;Pulses(uSec)=1230,960,1200,960,1200,930,390,960,1200,930,420,930,1200,930,420,930,390,960,420,930,1200,930,1200,930,420,930,1200,960,1170,960,1200,930,390,930,420,930,390,930,1200,930,420,930,390,930,1200,960,1200,930,420,930,420,960,390,930,1200,930,1200,930,1200,930,1200,930,1200,930,390,960,390,930,1200,960,1200,930,420,930,420,960,390,930,420,960,390,930,1200,930,420,6990;
* --------------------------------------------------------------------------------------------
* Rain Packet:
* Each frame is 46 bits long. It is composed of:
* 10bits of 0 (start sequence)
* 7 blocks of four bits separated by a bit 1 to be checked and skipped
*
* The 1st bit of each word is LSB, so we have to reverse the 4 bits of each word.
* Example
* 0000 0000 0010 1111 1011 0010 1011 1111 1101
* aaaa bbbb ccc1 ccc2 ccc3 dddd eeee
* 2 F B 2 B F D
*
* a = sensor type (2=Rain meter)
* b = sensor address
* c = rain data (LSB thus the right order is c3 c2 c1)
* d = Check Xor : (2 ^ F ^ B ^ 2 ^ B ^ F) = 0
* e = Check Sum : (const5 + 2 + F + B + 2 + B + F) and F = D
\*********************************************************************************************/
#define LACROSSE43_PULSECOUNT 88
#define LACROSSE43_PULSEMID 750/RAWSIGNAL_SAMPLE_RATE
#define LACROSSE43_PULSEMAX 1350/RAWSIGNAL_SAMPLE_RATE
#define LACROSSE43_PULSEMINMAX 550/RAWSIGNAL_SAMPLE_RATE
#define LACROSSE43_MIDLO 800/RAWSIGNAL_SAMPLE_RATE
#define LACROSSE43_MIDHI 1000/RAWSIGNAL_SAMPLE_RATE
#ifdef PLUGIN_043
boolean Plugin_043(byte function, char *string) {
boolean success=false;
if ( (RawSignal.Number < LACROSSE43_PULSECOUNT - 4) || (RawSignal.Number > LACROSSE43_PULSECOUNT + 4) ) return false;
unsigned long bitstream1=0L; // holds first 16 bits
unsigned long bitstream2=0L; // holds last 28 bits
int temperature=0;
int humidity=0;
byte checksum=0;
byte bitcounter=0; // counts number of received bits (converted from pulses)
byte data[10];
//==================================================================================
// get bytes
for(int x=1;x<RawSignal.Number;x+=2) {
if ((RawSignal.Pulses[x+1] < LACROSSE43_MIDLO) || (RawSignal.Pulses[x+1] > LACROSSE43_MIDHI)) {
if (x < 2) { // Make sure the first bit is correct..
RawSignal.Pulses[1]=1200/RAWSIGNAL_SAMPLE_RATE;
} else {
if (x+1 < RawSignal.Number) return false; // in between pulse check
}
}
if (RawSignal.Pulses[x] > LACROSSE43_PULSEMID) {
if (RawSignal.Pulses[x] > LACROSSE43_PULSEMAX) return false;
if (bitcounter < 16) {
bitstream1 = (bitstream1 << 1);
bitcounter++; // only need to count the first 16 bits
} else {
bitstream2 = (bitstream2 << 1);
}
} else {
if (RawSignal.Pulses[x] > LACROSSE43_PULSEMINMAX) return false;
if (bitcounter < 16) {
bitstream1 = (bitstream1 << 1) | 0x1;
bitcounter++; // only need to count the first 16 bits
} else {
bitstream2 = (bitstream2 << 1) | 0x1;
}
}
}
//==================================================================================
// all bytes received, make sure checksum is okay
//==================================================================================
if ((bitstream1 == 0) && (bitstream2 == 0)) return false;
data[0] = (bitstream1 >> 12) & 0x0f; // prepare nibbles from bit stream
if (data[0] != 0x00) return false;
data[1] = (bitstream1 >> 8) & 0x0f;
if (data[1] != 0x0a) return false;
data[2] = (bitstream1 >> 4) & 0x0f;
data[3] = (bitstream1 >> 0) & 0x0f;
data[4] = (bitstream2 >> 24) & 0x0f;
data[5] = (bitstream2 >> 20) & 0x0f;
data[6] = (bitstream2 >> 16) & 0x0f;
data[7] = (bitstream2 >> 12) & 0x0f;
data[8] = (bitstream2 >> 8) & 0x0f;
data[9] = (bitstream2 >> 4) & 0x0f;
//==================================================================================
// first perform a checksum check to make sure the packet is a valid LaCrosse packet
for (byte i=0;i<10;i++){
checksum=checksum + data[i];
}
checksum=checksum & 0x0f;
if (checksum != (bitstream2 &0x0f )) return false;
//==================================================================================
// Prevent repeating signals from showing up, skips every second packet!
//==================================================================================
unsigned long tempval=(data[4])>>1;
tempval=((tempval)<<16)+((data[3])<<8)+data[2];
if( (SignalHash!=SignalHashPrevious) || (RepeatingTimer<millis()) || (SignalCRC != tempval) ){
// not seen this RF packet recently
SignalCRC=tempval;
} else {
return true; // already seen the RF packet recently, but still want the humidity
}
//==================================================================================
// now process the various sensor types
//==================================================================================
// Output
// ----------------------------------
if (data[2] == 0x00) {
temperature = data[5]*100;
temperature = temperature + data[6]*10;
temperature = temperature + data[7];
temperature = temperature-500;
data[4]=(data[4])>>1;
sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number
Serial.print( pbuffer );
Serial.print(F("LaCrosse;ID=")); // Label
PrintHex8( data+3,2);
sprintf(pbuffer, ";TEMP=%04x;", temperature);
Serial.println( pbuffer );
RawSignal.Repeats=false;
RawSignal.Number=0;
success=true;
} else
if (data[2] == 0x0e) {
humidity=(data[5]*16)+data[6];
if (humidity==0) return false; // humidity should not be 0
data[4]=(data[4])>>1;
sprintf(pbuffer, "20;%02X;", PKSequenceNumber++); // Node and packet number
Serial.print( pbuffer );
Serial.print(F("LaCrosse;ID=")); // Label
PrintHex8( data+3,2);
sprintf(pbuffer, ";HUM=%02x;", (humidity)&0xff);
Serial.println( pbuffer );
RawSignal.Repeats=true;
RawSignal.Number=0;
success=true;
}
//==================================================================================
return success;
}
#endif // PLUGIN_043