-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmain.c
750 lines (610 loc) · 24.6 KB
/
main.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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
/*
Alternative firmware for STC15W101/4 processor + SYN115 radio transmitter door/window reed sensor(s)
(see README)
*/
#include "project-defs.h"
#include <delay.h>
// just do not have much more space for HAL with 1KB flash code size
//#include <eeprom-hal.h>
//#include <gpio-hal.h>
//#include <power-hal.h>
//#include <timer-hal.h>
#ifndef MCU_HAS_WAKE_UP_TIMER
// Shouldn't happen, unless using an STC12.
#error "The selected MCU doesn't have a power-down wake-up timer."
#endif // MCU_HAS_WAKE_UP_TIMER
// uncomment only one protocol timing
//#define PROTOCOL_STOCK
#define PROTOCOL_ONE
//#define PROTOCOL_TWO
//---------------------------------------------------------------------------------------------
// we need to obey rules for alarm transmission: https://www.law.cornell.edu/cfr/text/47/15.231
// human actuated or alarm conditions are allowed to transmit (emergency if door opened)
// battery condition would not be an emergency and so we piggy when sending human actuated codes
// very little time is allowed for any further periodic transmission, as an exercise given our timings
// for 2 secs / hour allowed periodic transmission
// number of events is about 2000000/(20*(470+14060+24*(470+1360))) ~ 2 events
// (3600 seconds / 2 events) / (16 sec wakeup timer) ~ 113 wake ups
//---------------------------------------------------------------------------------------------
// check for tamper switch pressed at startup, if so enter bootloader mode
// this is helpful during firmware development or reflashing
// however, if inserting the battery, make sure not to hold down the tamper switch
// FIXME: concerned that brownout reset could falsely trigger this while in housing
#if 1
#define CHECK_FOR_TAMPER_AT_STARTUP true
#else
#define CHECK_FOR_TAMPER_AT_STARTUP false
#endif
// keep sending switch state until alarm condition clears
//
// otherwise some users may only want to know about switch changes
// for example using the circuit board as on/off switches for lighting
// disabling tamper also saves on battery power
#if 1
#define TAMPERS_ENABLED true
#else
#define TAMPERS_ENABLED false
#endif
// use upper bits of code as packet count to help in detecting missing packets
#if 0
#define PACKET_COUNT_WITH_CODE true
#else
#define PACKET_COUNT_WITH_CODE false
#endif
// this involves waking up from sleep regularly to track time
// so checking battery contributes to draining battery
#if 0
#define BATTERY_TRIP_ENABLED true
#else
#define BATTERY_TRIP_ENABLED false
#endif
// hardware pin definitions
// circuit is voltage divider and high side transistor with processor controlling divider
// pin 3.0 is also connected with receive pin on header
// LED is attached to P3.1 for door sensor RF 433 MHz (STC15W101 and STC15W104 processor, SYN115 RF transmitter)
// pin 3.1 is also connected with transmit pin on header
// ASK modulation to RF chip is pin 3.4
// battery sensed on my board when variable power supply (stand in for battery)
// reaches just above one volt from starting point of 1.5 volts
// pin 3.5 signal line goes from high (3.3V) to low (0V)
#define RADIO_VDD P3_0
#define LED_PIN P3_1
#define REED_SWITCH P3_2
#define TAMPER_SWITCH P3_3
#define RADIO_ASK P3_4
#define BATTERY_DETECT P3_5
// radio protocol apparently requires repeated transmissions so it is accepted at receiver
// RF-Bridge-EFM8BB1 alternative firmware for receiver chip on sonoff bridge seems to require only repeating twice
// stock EFM8BB1 receiver on sonoff bridge r1 seems to require four retransmissions
// @mmotley999 reported up to eight retransmissions are required
// @bismosa reports stock firmware repeats transmission twenty times!
// sonoff bridge r2 seems to to similarly require many repeats
//#define REPEAT_TRANSMISSIONS 1
//#define REPEAT_TRANSMISSIONS 4
//#define REPEAT_TRANSMISSIONS 8
#define REPEAT_TRANSMISSIONS 20
// sec. 9, table datasheet output blanking VDD transition from low to high (500 microseconds)
// oscillator startup time crystal HC49S (300 microseconds)
// standby ask delay time (min, 30), typical(75), max(120) milliseconds
// however fig. 6 shows only about a two millisecond ramp up time for VDD
// sec. 14.2 of SYN115-ASK-Transmitter-Datasheet.pdf describes two more modes for on-off control that save battery power
// may want to look into that more in the future
// milliseconds
#define RADIO_STARTUP_TIME 10
// some receivers require a delay so that multiple packets are received (e.g., sonoff bridge r2v2.2)
// milliseconds
#define RADIO_GUARD_TIME 30
// tens of microseconds
#define DEBOUNCE_TIME_10US 20
// maximum is 32768 as per sec. 7.8 power down wake-up special timer
// highest bit of register will be set to enable wake up timer
// so maximum count is 0x7FFF and then highest bit set
// pg. 551, sec. 7.8 power down wake up special timer
// this equates to about 16 seconds according to examples
// note: weird, why does setting to 0xFFFF wake up too quickly, about every two seconds?
#define SLEEP_TIME_0 0xfffe
// milliseconds
#define CONTROLLER_STARTUP_TIME 200
// array size
#define EVENT_HISTORY_SIZE 16
// about 16 secs per count (assumes wktc set to full value near 0x7fff)
// we use bitwise-and in check logic instead of modulo divide to save code space
// about one minute
//#define TAMPER_SLOT 4
//#define REED_SLOT 4
// about ten minutes
#define TAMPER_SLOT 38
#define REED_SLOT 38
// about thirty minutes
//#define TAMPER_SLOT 113
//#define REED_SLOT 113
#define BATTERY_SLOT 113
// about one hour
//#define BATTERY_SLOT 225
// unique ID is stored in ram in the same locations on mcu 101/104 parts
// see makefile - we are limiting ram size so that it is not initialized/written at these addresses
// it is possible to pull id from flash space, but it is at different locations on different sized parts
// note: the values stored in ram at these locations should match the last four characters shown by Target UID when flashing
#define ID0_ADDR_RAM 0x76
#define ID1_ADDR_RAM 0x77
// 1K MCU (e.g. STC15W101)
// #define ID_ADDR_ROM 0x03f9
// 4K MCU (eg. STC15W104)
//#define ID_ADDR_ROM 0x0ff9
// point at ram locations to derive uniqe id for inclusion in radio messages as original firmware did
// see sec. 1.12 global unique identification number STC15-English.pdf
static const __idata unsigned char *guid0 = (__idata unsigned char*) ID0_ADDR_RAM;
static const __idata unsigned char *guid1 = (__idata unsigned char*) ID1_ADDR_RAM;
// codes used in original firmware are (0x06, 0x07, 0x0a, 0x0e), others were added
// original codes reported by: https://community.home-assistant.io/t/budget-priced-wife-friendly-wireless-wall-switch-using-433mhz-and-mqtt/88281
// if sensor is being powered by 3.3V on header pins, this will also show battery low apparently
static const unsigned char gBatteryLow = 0x06;
static const unsigned char gTamperOpen = 0x07;
static const unsigned char gTamperClose = 0x08;
static const unsigned char gBatteryOk = 0x09;
static const unsigned char gReedOpen = 0x0a;
static const unsigned char gReedClose = 0x0e;
// stock protocol was read from unaltered sensor by user @bismosa using logic analyzer
// "protocol 1" is the first rc-switch protocol which is also supported by RF-Bridge-EFM8BB1
// (https://github.com/Portisch/RF-Bridge-EFM8BB1/blob/master/inc/RF_Protocols.h)
// rc-switch project timings (https://github.com/sui77/rc-switch)
// changed pulse lengths from microseconds in rc-switch library to 10 microseconds units
// because hardware abstraction layer provides delay10us() for STC15 MCU
// avoid performing mathematical multiplies on processor by computing these as constants
#if defined(PROTOCOL_STOCK)
// stock sensor timings (see door_sensor_reverse_notes_fv1.txt)
// (tested working on Sonoff Bridge R2 V1.0)
// (does not work on stock Sonoff Bridge R2 V2.2)
const uint16_t gPulseHigh = 47;
const uint16_t gPulseLow = 1406;
const uint16_t gZeroHigh = 47;
const uint16_t gZeroLow = 136;
const uint16_t gOneHigh = 136;
const uint16_t gOneLow = 47;
// it saves code space to just specify polarity for conditional compilation
#define PROTOCOL_INVERTED false
#elif defined(PROTOCOL_ONE)
const uint16_t gPulseHigh = 35;
const uint16_t gPulseLow = 1085;
const uint16_t gZeroHigh = 35;
const uint16_t gZeroLow = 105;
const uint16_t gOneHigh = 105;
const uint16_t gOneLow = 35;
#define PROTOCOL_INVERTED false
#elif defined(PROTOCOL_TWO)
const uint16_t gPulseHigh = 65;
const uint16_t gPulseLow = 350;
const uint16_t gZeroHigh = 65;
const uint16_t gZeroLow = 130;
const uint16_t gOneHigh = 130;
const uint16_t gOneLow = 65;
#define PROTOCOL_INVERTED false
#endif // specify protocol
// set flags in interrupts, then save and send event history in main loop
struct Flags {
volatile bool reedInterrupted;
volatile bool tamperInterrupted;
volatile bool batteryLowInterrupted;
volatile bool tamperTripped;
volatile bool reedTripped;
volatile bool batteryLowTripped;
// save switches or battery states for sending
volatile unsigned char eventHistory[EVENT_HISTORY_SIZE];
volatile unsigned char eventCount;
// track packets sent since startup
volatile unsigned char packetCount;
// track times woken up so we can track longer time periods
unsigned char tamperWakeupCount;
unsigned char reedWakeupCount;
unsigned char batteryWakeupCount;
};
// arrays are not initialized, but okay because we only read when count is incremented
struct Flags flag = {
.reedInterrupted = false,
.tamperInterrupted = false,
.tamperTripped = false,
.reedTripped = false,
.batteryLowTripped = false,
.eventCount = 0,
.packetCount = 0,
.tamperWakeupCount = 0,
.reedWakeupCount = 0,
.batteryWakeupCount = 0
};
// only enable power to radio when we are going to modulate ASK pin (i.e., send data)
// low pin setting enables transistor which supplies power to radio chip
inline void enable_radio_vdd(void)
{
RADIO_VDD = 0;
}
// pin setting functions are more readable than direct pin setting
// and avoid making errors (e.g., "enabling" something is actually setting pin logic level to zero)
inline void disable_radio_vdd(void)
{
RADIO_VDD = 1;
}
// specify inline to save some flash code space
inline void radio_ask_first_logic_level(void)
{
#if PROTOCOL_INVERTED
RADIO_ASK = 1;
#else
RADIO_ASK = 0;
#endif
}
inline void radio_ask_second_logic_level(void)
{
#if PROTOCOL_INVERTED
RADIO_ASK = 0;
#else
RADIO_ASK = 1;
#endif
}
// led is controlled by transistor which essentially inverts pin output
// (so low level enables transistor and then LED is on)
inline void led_on(void)
{
LED_PIN = 0;
}
inline void led_off(void)
{
LED_PIN = 1;
}
// enable external interrupt 0 (reed switch connected)
// enable external interrupt 1 (tamper switch connected)
// also of course need to enable global interrupts (EA = 1)
inline void enable_interrupts(void)
{
// called the interrupt enable register in datasheet (IE)
// note: default for ext. interrupts 0 and 1 is to interrupt on both falling and rising edges
// (IT0 = 0, IT1 = 0)
// EA = 1, EX1 = 1, EX0 = 1
IE1 = 0x85;
// EX3 = 1 for battery status
INT_CLKO = 0x20;
}
// allows integer delays with 10 microsecond function at the expense of accuracy
void delay10us_wrapper(unsigned int microseconds)
{
const unsigned char step = 0xff;
while (microseconds > step)
{
delay10us(step);
microseconds -= step;
}
delay10us(microseconds);
}
/*! \brief
*
*/
inline bool isTamperOpen(void)
{
return TAMPER_SWITCH;
}
inline bool isReedOpen(void)
{
return REED_SWITCH;
}
// read resistive divider state
inline bool isBatteryLow(void)
{
return !BATTERY_DETECT;
}
/*! \brief Description
* Tips [http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf]
*
*/
void send(const unsigned char byte)
{
const unsigned char numBits = 8;
// mask out highest bit
const unsigned char mask = 1 << (numBits - 1);
unsigned char i = 0;
// byte for shifting
unsigned char toSend = byte;
// Repeat until all bits sent
for(i = 0; i < numBits; i++)
{
// mask out all but left most bit value, and if byte is not equal to zero (i.e. left most bit must be one) then send one level
if((toSend & mask) > 0)
{
radio_ask_second_logic_level();
delay10us_wrapper(gOneHigh);
radio_ask_first_logic_level();
delay10us_wrapper(gOneLow);
}
else
{
radio_ask_second_logic_level();
delay10us_wrapper(gZeroHigh);
radio_ask_first_logic_level();
delay10us_wrapper(gZeroLow);
}
toSend = toSend << 1;
}
}
void send_radio_packet(const unsigned char rfcode)
{
unsigned char index;
unsigned char byteToSend;
enable_radio_vdd();
delay1ms(RADIO_STARTUP_TIME);
// many receivers require repeatedly sending identical transmissions to accept data
for (index = 0; index < REPEAT_TRANSMISSIONS; index++)
{
// rf sync pulse
radio_ask_second_logic_level();
delay10us(gPulseHigh);
// this should be the only really long delay required
radio_ask_first_logic_level();
delay10us_wrapper(gPulseLow);
// send rf key with unique id and code
send(*guid0);
send(*guid1);
#if (PACKET_COUNT_WITH_CODE)
// effectively wraps around every sixteen counts because we only have upper four bits in radio packet available
byteToSend = (flag.packetCount << 4) | rfcode;
send(byteToSend);
#else
send(rfcode);
#endif
}
disable_radio_vdd();
// FIXME: we need to force ask low/high just in case correct?
}
//-----------------------------------------
//FIXME: handle reentrancy?
// interrupt and wake up on reed pin change (default is rising and falling edge)
void external_isr0(void) __interrupt (0)
{
flag.reedInterrupted = true;
}
//-----------------------------------------
// interrupt and wake up on tamper switch pin change
void external_isr1(void) __interrupt (2)
{
flag.tamperInterrupted = true;
}
//-----------------------------------------
// interrupt and wake up on battery state change
// because isr3 is so far down on the vector table (entry 11) intermediate bytes are wasted (uses 76 bytes for almost nothing!)
void external_isr3(void) __interrupt (11)
{
flag.batteryLowInterrupted = true;
}
// sec. 4.1 All port pins default to quasi-bidirectional after reset. Each one has a Schmitt-triggered input for improved input noise rejection.
//batteryMonitor GPIO_PIN_CONFIG(GPIO_PORT3, GPIO_PIN5, GPIO_HIGH_IMPEDANCE_MODE)
//radioASK GPIO_PIN_CONFIG(GPIO_PORT3, GPIO_PIN4, GPIO_BIDIRECTIONAL_MODE)
//tamperSwitch GPIO_PIN_CONFIG(GPIO_PORT3, GPIO_PIN3, GPIO_BIDIRECTIONAL_MODE)
//reedSwitch GPIO_PIN_CONFIG(GPIO_PORT3, GPIO_PIN2, GPIO_BIDIRECTIONAL_MODE)
//ledPin GPIO_PIN_CONFIG(GPIO_PORT3, GPIO_PIN1, GPIO_OPEN_DRAIN_MODE)
//radioVDD GPIO_PIN_CONFIG(GPIO_PORT3, GPIO_PIN0, GPIO_OPEN_DRAIN_MODE)
inline void configure_pin_modes(void)
{
// avoid bit operations to save on flash code space
// just explicitly set gpio mode
P3M1 = 0x23;
P3M0 = 0x03;
}
// open drain
// turn led off
// disable radio power
// sec. 4.9.1 quasi-bidirectional i/o
// enables weak pullups for reed and tamper switches assuming we are in bidirectional mode (pins high)
// note: do not use radio_ask_X() to disable level/power on ask pin, because it might be inverted, so would actually enable power!
inline void startup_pins_state(void)
{
P3 = 0x0f;
}
void main(void)
{
// as per HAL instructions
INIT_EXTENDED_SFR();
// makes the code easier to read below
bool state;
unsigned char index;
// setup gpio
configure_pin_modes();
startup_pins_state();
// pulse LED at startup because we delay next anyway
led_on();
// give the microcontroller time to stabilize
delay1ms(CONTROLLER_STARTUP_TIME);
led_off();
#if CHECK_FOR_TAMPER_AT_STARTUP
// if we are holding down tamper switch at power up, enter iap mode
// this is intended to make entering flash mode easier on subsequent flashes
// if the board has no tamper switch, then this will just never do anything
// if a regular user is inserting a battery, it is unlikely that they are also pressing tamper switch
// so this should just be skipped over in that case
if (!isTamperOpen())
{
// set ISP boot bit and reset processor
IAP_CONTR = 0x60;
}
#endif
// enable everything in one call to save bytes
enable_interrupts();
// catch if battery is already low at startup
if (isBatteryLow())
{
flag.batteryLowInterrupted = true;
}
// main loop
while (true)
{
#if TAMPERS_ENABLED
// only enable wake up timer if alarm wake up required, otherwise clear/disable
// note: sec. 3.3.1 special function registers address map shows that default state of WKTCx has default value in timer
if (flag.tamperTripped | flag.reedTripped | flag.batteryLowTripped)
{
// we do not use this currently
flag.tamperWakeupCount++;
flag.reedWakeupCount++;
flag.batteryWakeupCount++;
// set wake up count and enable wake up timer bit
WKTC = SLEEP_TIME_0;
} else {
// could just clear most significant enable bit, but guess no harm in clearing count also
WKTC = 0x0000;
}
#endif
// check if there are unserviced interrupt(s) prior to sleeping
if (!(flag.reedInterrupted | flag.tamperInterrupted | flag.batteryLowInterrupted))
{
{
// go to sleep
// program will next either resume due to wake up timer (if enabled) or due to interrupt(s)
PCON |= M_PD;
// sec. 2.3.3.1 demo program in datasheet and example HAL show providing nops() after power down
NOP();
NOP();
}
}
#if TAMPERS_ENABLED
// tamper trip sends messages while alarm is occuring (i.e., tamper opens once and remains open)
// sending stops once the tamper is again closed
if (flag.tamperTripped)
{
if (flag.tamperWakeupCount > TAMPER_SLOT)
{
flag.tamperWakeupCount = 0;
// only alarm for open condition
if (isTamperOpen())
{
flag.eventHistory[flag.eventCount] = gTamperOpen;
flag.eventCount++;
} else {
// otherwise alarm has been cleared, so disable trip
flag.tamperTripped = false;
}
}
}
// similar alarm message but for reed switch
if (flag.reedTripped)
{
if (flag.reedWakeupCount > REED_SLOT)
{
flag.reedWakeupCount = 0;
if(isReedOpen())
{
flag.eventHistory[flag.eventCount] = gReedOpen;
flag.eventCount++;
} else {
flag.reedTripped = false;
}
}
}
#endif
#if BATTERY_TRIP_ENABLED
if (flag.batteryLowTripped)
{
// only rarely send battery state
if (flag.batteryWakeupCount > BATTERY_SLOT)
{
flag.batteryWakeupCount = 0;
// go ahead and send battery state
if(isBatteryLow())
{
flag.eventHistory[flag.eventCount] = gBatteryLow;
} else {
// this logic will probably never happen
// and can assume battery is ok if no low code sent
// but go ahead and send it anyway
flag.eventHistory[flag.eventCount] = gBatteryOk;
flag.batteryLowTripped = false;
}
flag.eventCount++;
}
}
#endif
// this will only send once as battery discharges unless battery voltage bounces around
if (flag.batteryLowInterrupted)
{
// FIXME: debounce
delay10us(DEBOUNCE_TIME_10US);
flag.batteryLowInterrupted = false;
flag.batteryLowTripped = true;
// go ahead and send battery state, we know it is low due to interrupt
flag.eventHistory[flag.eventCount] = gBatteryLow;
flag.eventCount++;
// disable battery status interrupt so it cannot bounce around and send repeated packets
INT_CLKO &= ~0x20;
}
// if we do too much in the interrupts themselves it produces too many stack pushes/pops
// among other problems that are caused, so perform most logic here
if (flag.reedInterrupted)
{
// slope on reed switch from high to low is about 200 microseconds as measured on oscilloscope
// FIXME: this may be a terrible debounce
delay10us(DEBOUNCE_TIME_10US);
// clear flag after debounce so we do not pick up any bounces
flag.reedInterrupted = false;
// more readable for array indexing
index = flag.eventCount;
// just read switch once here, but use in two logic locations below
state = isReedOpen();
// save any switch (or later battery) changes in order of occurence
// note: we really do not have code space for checking array overflows
if (state)
{
flag.eventHistory[index] = gReedOpen;
} else {
flag.eventHistory[index] = gReedClose;
}
// track count because we might have multiple events in quick succession
flag.eventCount++;
// if reed is open, consider it tripped until closed
// note: if trip is not enabled, this flag should not do anything
if (state)
{
// flag as tripped for alarm sending
flag.reedTripped = true;
flag.reedWakeupCount = 0;
}
}
// we do not check if reed occurred prior to tamper or vice versa
// so if both codes are sent reed code will always arrive first at receiver
if (flag.tamperInterrupted)
{
// FIXME: another terrible debounce
delay10us(DEBOUNCE_TIME_10US);
flag.tamperInterrupted = false;
index = flag.eventCount;
state = isTamperOpen();
if (state)
{
flag.eventHistory[index] = gTamperOpen;
} else {
flag.eventHistory[index] = gTamperClose;
}
flag.eventCount++;
// same trip behavior
if (state)
{
// flag as tripped for sending elsewhere
flag.tamperTripped = true;
flag.tamperWakeupCount = 0;
}
}
// send any stored event(s) over radio if count is incremented
// we need to start at index zero every time (the oldest event)
index = 0;
while (flag.eventCount > 0)
{
// this is smarter than using using blocking delays to pulse led
led_on();
// send radio packet consisting of start pulse and 24-bits of data
send_radio_packet(flag.eventHistory[index]);
// delay before sending next packet so as not to overwhelm receiver
delay1ms(RADIO_GUARD_TIME);
// effectively just pulsed led due to inherent delays of radio sending
led_off();
// count up from zero (i.e., send oldest to newest event in that order)
index++;
flag.eventCount--;
// track this for debugging purposes
flag.packetCount++;
}
} // main while loop
}