-
Notifications
You must be signed in to change notification settings - Fork 0
/
dashboard-motion-detect.c
213 lines (176 loc) · 5.15 KB
/
dashboard-motion-detect.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
/*
* dashboard-motion-detect.c:
* Motion Detector for Office Dashboard
*
* Uses ISR (interrupt) method to run interrupt handling code
* when GPIO pin 4 (wiring Pi 7) of the PIR goes HIGH
*
* PIR = Pyroelectric Infrared Sensor aka passive motion sensor
*
* How to use:
* Connect a standard Adafruit-like PIR to the Raspberry. Use
* GPIO pin 4 on the board, along with 5V and GND.
*
* July 2016, Torbjørn Kristoffersen <torbjoern@gmail.com>
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sqlite3.h>
#include <wiringPi.h>
#define WPI_PIN 7
#define IDLE_THRESHOLD 900
#define SCRIPT_PATH "dpms-scripts"
#define SCREEN_ON "screen-on.sh"
#define SCREEN_OFF "screen-off.sh"
#define DB_FILE "motion.sqlite3"
#define DB_MOTION_TABLE "create table motion (motion_timestamp datetime not null, screen_on int not null)"
#define DB_INSERT_START "insert into motion (motion_timestamp, screen_on) values ('"
bool create_motion_table() {
sqlite3 *conn;
int ret = sqlite3_open(DB_FILE, &conn);
if(ret == SQLITE_ERROR) {
fprintf(stderr, "Could not open database file: " DB_FILE "\n");
sqlite3_close(conn);
return false;
}
// Check if table already exists
sqlite3_stmt *stmt;
ret = sqlite3_prepare_v2(conn, "pragma table_info(motion)", -1, &stmt, NULL);
if(ret != SQLITE_OK) {
fprintf(stderr, "Could not check for motion table: %s\n", sqlite3_errmsg(conn));
sqlite3_close(conn);
return false;
}
ret = sqlite3_step(stmt);
sqlite3_finalize(stmt);
if (ret != SQLITE_ROW) {
ret = sqlite3_exec(conn, DB_MOTION_TABLE, NULL, 0, 0);
if(ret != SQLITE_OK) {
fprintf(stderr, "Could not create motion table: %s\n", sqlite3_errmsg(conn));
sqlite3_close(conn);
return false;
}
fprintf(stderr, "Created motion table in SQLite3 database: " DB_FILE "\n");
} else {
fprintf(stderr, "Found motion table in SQLite3 database: " DB_FILE "\n");
}
// Always close
sqlite3_close(conn);
return true;
}
void insert_motion_table(bool is_screen_on) {
sqlite3 *conn;
int ret = sqlite3_open(DB_FILE, &conn);
if(ret == SQLITE_ERROR) {
fprintf(stderr, "Could not open database file: " DB_FILE "\n");
sqlite3_close(conn);
return;
}
time_t cur_time = time(NULL);
if(cur_time == ((time_t)-1)) {
perror("time");
return;
}
struct tm *tmp = localtime(&cur_time);
if(tmp == NULL) {
perror("localtime");
return;
}
char buf[64];
if(strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tmp) == 0) {
perror("strftime");
return;
}
int len = strlen(DB_INSERT_START) + strlen(buf) + 5; // quote + comma + number + ) + NULL
char *final = malloc(len);
if(final == NULL) {
perror("malloc");
return;
}
// Build query
snprintf(final, len, "%s%s',%d)", DB_INSERT_START, buf, (is_screen_on ? 1 : 0));
// Use string
ret = sqlite3_exec(conn, (char*)final, NULL, 0, 0);
if (ret != SQLITE_OK) {
fprintf(stderr, "Could not execute query '%s' due to: %s\n", final, sqlite3_errmsg(conn));
} else {
fprintf(stderr, "Executed query '%s'\n", final);
}
// Free memory and close DB
free(final);
sqlite3_close(conn);
}
static volatile int screen_on = 1;
static volatile int idle_counter = 0;
void sighandler(int signo) {
if (signo == SIGCHLD) {
int status;
wait(&status);
}
}
void turn_screen_on() {
fprintf(stderr, "Turning screen ON\n");
fflush(stderr);
int pid;
if((pid = fork()) == 0) {
execl(SCRIPT_PATH "/" SCREEN_ON, SCREEN_ON, NULL);
}
}
void turn_screen_off() {
fprintf(stderr, "Turning screen OFF\n");
fflush(stderr);
int pid;
if((pid = fork()) == 0) {
execl(SCRIPT_PATH "/" SCREEN_OFF, SCREEN_OFF, NULL);
}
}
void log_motion() {
if(screen_on == 1) {
insert_motion_table(true);
} else {
insert_motion_table(false);
}
}
void motion_detected (void) {
// Reset idle count
idle_counter = 0;
// Log motion in SQLite3 database
log_motion();
if(screen_on == 1) {
return;
}
turn_screen_on();
screen_on = 1;
}
int main (void) {
wiringPiSetup();
wiringPiISR(WPI_PIN, INT_EDGE_RISING, &motion_detected);
// Catch the SIGCHLD signal so we can reap the screen zombie process
signal(SIGCHLD, sighandler);
// Turn screen on initially
turn_screen_on();
// Create motion table in SQLite3 database
if(!create_motion_table()) {
return 1;
}
// CPU will spend most of its time in delay, so we don't waste CPU cycles
for (;;) {
delay(1000);
if(idle_counter < IDLE_THRESHOLD) {
idle_counter++;
} else if(idle_counter == IDLE_THRESHOLD && screen_on == 1) {
turn_screen_off();
screen_on = 0;
}
}
return 0;
}