]> mj.ucw.cz Git - misc.git/blob - sphinx/sphinx.c
Merge branch 'master' of git+ssh://git.ucw.cz/home/mj/GIT/misc
[misc.git] / sphinx / sphinx.c
1 /*
2  *      The Sphinx -- A Device for Asking Riddles at the Entrance to the Garage
3  *
4  *      (c) 2017--2019 Martin Mares <mj@ucw.cz>
5  */
6
7 /*
8  *      Pin assignment (component side view; "*" marks inverted signals)
9  *
10  *                      +-------------------+
11  *                      | RESET*        VCC |
12  *      button*         | PB3       PB2=SCK |   signal "closed" from drive*
13  *      output to drive | PB4      PB1=MISO |   corridor LED
14  *                      | GND      PB0=MOSI |   diagnostic LED*
15  *                      +-------------------+
16  *
17  *      Note that MISO, MOSI, and SCK are used for programming, so our signals
18  *      are connected via jumpers, which must be kept open during programming.
19  *
20  *      Connections to door control unit:
21  *
22  *              3 = +24V
23  *              1 = GND
24  *              2 = output
25  *
26  *      Connections to EP 161 module:
27  *
28  *              H1 = signal +
29  *              92 = signal -
30  */
31
32 #define F_CPU 1200000UL
33
34 #include <avr/interrupt.h>
35 #include <avr/io.h>
36 #include <avr/sleep.h>
37 #include <util/delay.h>
38
39 typedef uint8_t byte;
40 typedef uint16_t u16;
41
42 #define B(x) (1U<<(x))
43
44 static void sleep(uint16_t millisec)
45 {
46         while (millisec) {
47                 _delay_ms(1);
48                 millisec--;
49         }
50 }
51
52 static byte out_pulse;
53 #define OUT_PULSE_WIDTH 100
54 static byte debounce;
55 #define DEBOUNCE_THRESHOLD 3
56 static byte pressed;
57 static u16 press_timer;
58 #define LONG_PRESS 150
59 static u16 unlock_timer;
60 #define UNLOCK_SECONDS 30
61 static byte blink_timer;
62
63 ISR(TIM0_COMPA_vect)
64 {
65         // Generate output pulse
66         if (out_pulse) {
67                 if (!--out_pulse)
68                         PORTB &= ~B(PB4);
69         }
70
71         // Debounce the button
72         if (PINB & B(PB3)) {
73                 // Switch open
74                 if (debounce)
75                         debounce--;
76                 else
77                         pressed = 0;
78         } else {
79                 // Switch closed
80                 if (debounce < DEBOUNCE_THRESHOLD)
81                         debounce++;
82                 else
83                         pressed = 1;
84         }
85
86         // PB2 is 0 if the door is fully closed,
87         // we need to set door_open if it is at least partially open.
88         byte door_open;
89         if (PINB & B(PB2)) {
90                 door_open = 1;
91                 PORTB |= B(PB1);
92                 unlock_timer = UNLOCK_SECONDS * 60;
93         } else {
94                 door_open = 0;
95                 PORTB &= ~B(PB1);
96         }
97
98         // Locking
99         if (unlock_timer) {
100                 if (pressed) {
101                         PORTB &= ~B(PB0);
102                         if (out_pulse < 2)
103                                 out_pulse = 2;
104                         PORTB |= B(PB4);
105                         unlock_timer = UNLOCK_SECONDS * 100;
106                 } else if (door_open) {
107                         if (!(blink_timer & 63))
108                                 PORTB ^= B(PB0);
109                 } else {
110                         if (!(blink_timer & 15))
111                                 PORTB ^= B(PB0);
112                         unlock_timer--;
113                 }
114         } else {
115                 if (!pressed) {
116                         PORTB |= B(PB0);
117                         press_timer = 0;
118                 } else {
119                         PORTB &= ~B(PB0);
120                         if (press_timer < LONG_PRESS) {
121                                 press_timer++;
122                                 if (press_timer == LONG_PRESS)
123                                         unlock_timer = UNLOCK_SECONDS * 100;
124                         }
125                 }
126         }
127
128         blink_timer++;
129 }
130
131 int main(void)
132 {
133         // PB0: output for diagnostic LED
134         DDRB |= B(PB0);
135
136         // PB1: output for corridor LED
137         PORTB &= ~B(PB1);
138         DDRB |= B(PB1);
139
140         // PB2: signal "door closed" from the drive, needs pull up
141         PORTB |= B(PB2);
142
143         // PB3: button input, needs pull up
144         PORTB |= B(PB3);
145
146         // PB4: output to control the gate
147         PORTB &= ~B(PB4);
148         DDRB |= B(PB4);
149
150         for (byte i=0; i<5; i++) {
151                 PORTB &= ~B(PB0);
152                 sleep(50);
153                 PORTB |= B(PB0);
154                 sleep(50);
155         }
156
157 #if 0
158         // Copy mode
159         for (;;) {
160                 sleep(10);
161                 if (PINB & B(PB2)) {
162                         PORTB |= B(PB0);
163                         PORTB &= ~B(PB1);
164                         PORTB &= ~B(PB4);
165                 } else {
166                         PORTB &= ~B(PB0);
167                         PORTB |= B(PB1);
168                         PORTB |= B(PB4);
169                 }
170         }
171 #endif
172
173         // Set up timer interrupt
174         TCCR0A = 0x02;                  // No OC pins, clear on compare
175         TCCR0B = 0x05;                  // Run at CLK_IO / 1024
176         OCR0A = 11;                     // Approximately once in 10ms
177         TIMSK0 = B(OCIE0A);             // Interrupt on compare
178         sei();
179
180         // Sleep well, my little sphinx
181         for (;;) {
182                 set_sleep_mode(SLEEP_MODE_IDLE);
183                 sleep_mode();
184         }
185 }