DecoderPFR-130.ino


1
/*
2
 * Decoder for Pollin PFR-130 Temerature/Rain Gauge 
3
 * (c)27.05.2016 W.Kracht
4
 * 
5
 * The external unit is sending at 433,92 MHz with ASK modulation 
6
 * recommended receiver: Geeetech 433Mhz Superheterodyne 3400RF @5V (data at pin 8 of Arduino Uno)
7
 * 
8
 * 36 bit protocol send every 45 seconds with 7 repeats
9
 * 
10
 * Sync:  435us high 8100us low
11
 * 0:     435us high 2130us low
12
 * 1:     435us high 3840us low
13
 * 
14
 * S0000 0000 0011 1111 1111 2222 2222 2233 3333
15
 * S0123 4567 8901 2345 6789 0123 4567 8901 2345
16
 * |----|----|----|----|----|----|----|----|----
17
 * S0000 hhhh bsRR tttt tttt tttt rrrr rrrr xxxx
18
 * 
19
 * S:  Sync
20
 * 0:  4 bit 0
21
 * h:  4 bit house code changes on 
22
 * b:  1 bit battery state 0 == OK
23
 * t:  1 bit force TX switch 1 == switch pressed
24
 * R:  2 bit MSB of rain value
25
 * t: 12 bit temperature *10 (eg: 23,1 send as 231)
26
 * r:  8 bit LSB of rain value
27
 * x:  4 bit checksum XOR of 8 nibbles send
28
 * 
29
 * The rain value is a 10 bit and is the accumulated count of seesaw pulses since last transmission multiplied by 25 
30
 * one pulse is 25 and 40 pulses are 1000
31
 */
32
33
// data pin of receiver has to be connected to pin d8
34
35
#include <Arduino.h>
36
37
#define PULSE_MAX 500
38
static char width_icp[PULSE_MAX];
39
static int  pulse_cnt = 0;
40
static int  ovfl_cnt = 0;
41
42
static unsigned long last_msg = 0;
43
44
// input capture interrupt vector
45
ISR(TIMER1_CAPT_vect)
46
{
47
  static unsigned int last_icr = 0;
48
  static unsigned int low_us = 0;
49
  static int          cnt = 0;
50
  
51
  if (0 < pulse_cnt) return;
52
53
  // Capture is an external asynchronous event. Overflow and Capture may occur at the
54
  // same time, but we are already entering this ISR. Check for missed overflow first.
55
  if (ICR1H < 16 && (TIFR1 & _BV(TOV1)))
56
  {
57
    ovfl_cnt++;               // do what ISR(TIMER1_OVF_vect) should have done ;)
58
    TIFR1 = _BV(TOV1);        // clear overflow flag, because already handled here
59
  }
60
61
  // calculate total time in us using overflows and time difference
62
  unsigned long t = (((unsigned long) ovfl_cnt << 16) + ICR1 - last_icr + 4) >> 3;
63
  last_icr = ICR1;
64
  ovfl_cnt = 0;
65
66
  if (TCCR1B & _BV(ICES1))  // detection set to rising edge
67
  {
68
    PORTB |= _BV(PB5);      // LED d13 on
69
    TCCR1B &= ~_BV(ICES1);  // set detection to falling edge
70
    low_us += t;
71
  }
72
  else                      // detection of falling edge
73
  {
74
    PORTB &= ~_BV(PB5);     // LED d13 off
75
    TCCR1B |= _BV(ICES1);   // set detection to rising edge
76
77
    if (t > 860L)           // detected valid high pulse
78
    {
79
      if (low_us > 16000L)
80
      {
81
        // sync detected
82
        width_icp[cnt] = 0;
83
        if (cnt >= 36 && cnt <= 37)
84
        {
85
          pulse_cnt = cnt;
86
        }
87
        cnt = 0;
88
        if (pulse_cnt <= 0)
89
        {
90
          width_icp[cnt] = 0;
91
        }
92
      }
93
      else if (cnt < PULSE_MAX - 1)
94
      {
95
        width_icp[cnt++] = low_us > 6000L ? '1' : '0';
96
      }
97
      low_us = 0;
98
    }
99
    else
100
    {
101
      low_us += t;
102
    }
103
  }
104
}
105
106
// timer overflow interrupt vector
107
ISR(TIMER1_OVF_vect)
108
{
109
  ovfl_cnt++;
110
}
111
112
bool decoder()
113
{
114
  int i;
115
  unsigned long code = 0;
116
  unsigned long cod1 = 0;
117
  int chks = 0;
118
  int chk1 = 0;
119
  
120
  for (i = 0; i < 32; i++)
121
  {
122
    code <<= 1;
123
    code |=  (width_icp[i] == '1');
124
  }
125
126
  for (; i < 36; i++)
127
  {
128
    chks <<= 1;
129
    chks |=  (width_icp[i] == '1');
130
  }
131
  
132
  cod1 = code;
133
  for (i = 7; i >= 0; i--)
134
  {
135
    chk1 ^= (cod1 & 0xf);
136
    cod1 >>= 4;
137
  }
138
139
  if (chks == chk1)
140
  {
141
    int hcod = (code >> 24) & 0xff;
142
    int batt = (code >> 23) & 0x1;
143
    int txsw = (code >> 22) & 0x1;
144
    int rain = (code >> 12) & 0x300;
145
    int temp = ((code >> 4) & 0xfff0) / 16;  // take care of sign
146
    rain    |= (code >> 0) & 0xff;
147
148
    //if (hc == 6)  // fiter if there is more than one device detected
149
    {
150
      unsigned long duration = millis() - last_msg;
151
      if (last_msg == 0)
152
      {
153
        last_msg = duration;
154
      }
155
      else if (duration > 30000)
156
      {
157
        last_msg = millis();
158
        Serial.print("last message: ");
159
        Serial.println(duration);
160
      }
161
    
162
      width_icp[36] = 0;
163
      Serial.print(&width_icp[0]);
164
      Serial.print(": h="); 
165
      Serial.print(hcod, HEX); 
166
      Serial.print(", b="); 
167
      Serial.print(batt); 
168
      Serial.print(", s="); 
169
      Serial.print(txsw); 
170
      Serial.print(", t="); 
171
      Serial.print(temp); 
172
      Serial.print(", r="); 
173
      Serial.println(rain);
174
    }
175
  }
176
  
177
  return (chks == chk1);
178
}
179
180
// initialize timer1 for PWM
181
void timer_init()
182
{
183
  // initialize timer1 for input capture with noise canceler
184
  TCCR1A = 0;                       // timer1 running free
185
  TCCR1B = _BV(ICNC1);              // input capture on falling edge with noise canceler
186
  TIMSK1 = _BV(TOIE1) | _BV(ICIE1); // enable ICP and OVF interrupts
187
  TCCR1B |= _BV(CS10);              // start timer1 with 8 MHz
188
}
189
190
void setup() {
191
  digitalWrite(8, HIGH);  // Pull Up for D8
192
  pinMode(13, OUTPUT);    // LED pin D13 shows interupt activity 
193
194
  timer_init();           // setup timer 1 for free running pulse witdth measurement
195
  Serial.begin(115200);   // decoded values arde dumped to Serial
196
  sei();                  // enable interrupts
197
  
198
  Serial.println("scanning");
199
}
200
201
void loop() {
202
  if (pulse_cnt > 0)
203
  {
204
    decoder();
205
    pulse_cnt = 0;
206
  }
207
}