-
Notifications
You must be signed in to change notification settings - Fork 0
/
smart_house.ino
470 lines (431 loc) · 21.5 KB
/
smart_house.ino
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
// ---
// smart house v3 Serial1
// ---
#define numSensors sizeof(mySensors)/sizeof(tSensor) // подсчет количества элементов в массиве numSensors
#define numDevices sizeof(myDevices)/sizeof(tDevice) // подсчет количества элементов в массиве numDevices
#define numPhones sizeof(smsNumbers)/sizeof(char*) // подсчет количества элементов в массиве numPhones
// постоянные
const int buttonPin = 12; // пин для кнопки
const int perimeterPin = 8; // пин для периметра
const int ledPin = 10 ; // пин индикатора
const int sysledPin = 13; // пин системного индикатора
const int tonePin = 5; // пин для динамика
const int gsmPowerPin = 4; // пин включения GSM модема
const long restTime = 15; // время в сек. в котором система будет в состоянии покоя после снятия с охраны
const long restSiren = 300; // время в сек. в котором система будет не будет повторно подавать аварийный сигнал на динамик, в случае аварии по сенсорам
// типы
typedef struct
{
int pin; // пин на котором расположен сенсор
boolean state; // состояние сенсора
boolean alarm; // состояние аварии
char* name; // название сенсора
char* errorMsg; // сообщение в случае включения сенсора
char* errorOk; // сообщение в случае отключения сенсора
}
tSensor;
// типы
typedef struct
{
int pin; // пин отвечающий за устройство
boolean enabled; // состояние устройства (true - вкл., false - выкл.)
boolean touchButton; // тип кнопки (true - сенсорная, false - фиксированя)
char* name; // название сенсора
}
tDevice;
// объявление сенсоров и их кодов ошибок
tSensor mySensors[] = {
{ 6, false, false, "Water", "WARNING! Found water leakage.", "Water leakage disappeared." },
{ 3, false, false, "Gas", "WARNING! Found gas leakage.", "Gas leakage disappeared." }
};
// объявление устройств и их состояний
tDevice myDevices[] = {
{ 7, false, true, "Unit 1"},
{ 2, false, false, "Unit 2"}
};
// переменные
boolean buttonState = false; // состояние кнопки
boolean perimeterState = false; // состояние периметра
boolean sensorState = false; // состояние всех сенсоров
int alarmState = 0; // состояние сигнализации ( 0 - выкл, 1 - нажата секретка, 2 - нажата секретка и открыта дверь, 3 - на охране, 4 - несанкционированное проникновение)
unsigned long currentTime;
unsigned long loopTime;
//char* smsNumbers[] = {}; // запуск без телефонов, смс не будут отсылаться
char* smsNumbers[] = {"+79110000000", "+79630000000"}; // номера на которые необходимо отсылать аварийные сообщения
String secretStr = "7770:"; // кодовое слово для подачи команд по смс
String currentStr = "";
/*
* функция программного перезапуска Arduino
*/
void(* resetFunc) (void) = 0;
/*
* Функция отправки SMS-сообщения
*/
void sendTextMessage(String text) {
for (int i = 0; i < numPhones; i++){
// Устанавливает текстовый режим для SMS-сообщений
Serial1.println("AT+CMGF=1");
delay(100); // даём время на усваивание команды
// Устанавливаем адресата: телефонный номер в международном формате
Serial1.println("AT+CMGS=\""+String(smsNumbers[i])+"\"");
delay(300);
// Пишем текст сообщения
Serial1.print(text);
delay(300);
// Отправляем Ctrl+Z, обозначая, что сообщение готово
Serial1.print((char)26);
delay(5000);
}
}
/*
* Функция для проигрывания аварийной мелодии thisNote раз
*/
void runSiren(int count) {
for (int thisNote = 0; thisNote < count; thisNote++) {
digitalWrite(ledPin, HIGH);
// отключить проигрывать функцию проигрывания ноты на пине tonePin
noTone(tonePin);
// проиграть ноту на tonePin длительностью 200 ms
tone(tonePin, 440, 300);
delay(300);
digitalWrite(ledPin, LOW);
noTone(tonePin);
tone(tonePin, 494, 600);
delay(600);
//noTone(tonePin);
//tone(tonePin, 523, 200);
//delay(200);
}
}
/*
* Функция для проигрывания звуков на постановку и снятие сигнализации
*/
void setAlarm(int state) {
switch (state) {
case 0:
// проиграть звук снятия с сигнализации
noTone(tonePin);
tone(tonePin, 1000, 100);
delay(200);
noTone(tonePin);
tone(tonePin, 1000, 100);
delay(200);
// визуальная индикация cнятия с охраны
digitalWrite(ledPin, HIGH);
delay (100);
digitalWrite(ledPin, LOW);
delay (70);
digitalWrite(ledPin, HIGH);
delay (100);
digitalWrite(ledPin, LOW);
break;
case 1:
// проиграть звук постановки на кнопку
noTone(tonePin);
tone(tonePin, 1000, 200);
delay(300);
// визуальная индикация готовности постановки на охрану
digitalWrite(ledPin, HIGH);
delay (70);
digitalWrite(ledPin, LOW);
break;
case 2:
// проиграть звук постановки на сигнализацию
noTone(tonePin);
tone(tonePin, 1500, 400);
delay(300);
// визуальная индикация постановки на охрану
digitalWrite(ledPin, HIGH);
delay (100);
digitalWrite(ledPin, LOW);
delay (70);
digitalWrite(ledPin, HIGH);
delay (100);
digitalWrite(ledPin, LOW);
break;
}
}
/*
* Функция проверки свой-чужой
*/
boolean checkIFF(int second) {
int i=0;
buttonState = false; // на всякий случай обнуляем состояние секретной кнопки
while(i <= second*10 && buttonState == false){ // цикл ожидающий нажатия i секунд на нажатие секретной кнопки
buttonState = digitalRead(buttonPin); // читаем текущее состояние секретной кнопки
delay(1000/10); // задержка выполнения цикла на 1/10 секунду
i++;
}
return buttonState;
}
/*
* Функция чтения всего буфера с серийного порта gsm модема
*/
String readSerial() {
String inData = "";
if (Serial1.available() > 0) {
int h = Serial1.available();
for (int i = 0; i < h; i++) {
inData += (char)Serial1.read();
}
return inData;
}
else {
return "No connection";
}
}
/*
* Функция чтения из буфера серийного порта gsm модема одной строки
*/
String readSerialStr() {
String content = "";
boolean retStr=false;
char currentChar;
delay(100);
while(Serial1.available() && retStr==false) {
currentChar = Serial1.read();
//if (currentChar=='\n' || currentChar=='\r') {
if (currentChar=='\n') {
retStr=true;
}
else {
content.concat(currentChar);
}
}
return content;
}
/*
* Функция опрос состояния системы и отправить отчет по sms
*/
void getStatus() {
String msgReport="";
if(alarmState==4){ // если периметр разомкнут
msgReport += "Perimeter=Fail; ";
}
else { // иначе
msgReport += "Perimeter=Ok; ";
}
msgReport += "\n";
for (int i = 0; i < numSensors; i++){ // в цикле опрашиваем все сенсоры
msgReport += mySensors[i].name;
if (mySensors[i].alarm==false){ // если состояние сенсора "покой", то
msgReport += "=Ok; ";
}
else { // иначе
msgReport += "=Fail; ";
}
}
msgReport += "\n";
for (int i = 0; i < numDevices; i++){ // в цикле опрашиваем все устройства
msgReport += myDevices[i].name;
if (myDevices[i].enabled==true){ // если состояние устройства вкл.
msgReport += "=On; ";
}
else { // иначе
msgReport += "=Off; ";
}
}
sendTextMessage(msgReport); // отправляем смс отчет о статусе системы
Serial.println(msgReport);
}
/*
* Функция парсинга sms сообщение и выполнение заданных команд
*/
void parseSMS(String msg) {
String tmp = "";
int startPos = secretStr.length();
int msgLength = msg.length();
if ((msgLength-startPos)==numDevices) { // если количество устройств в смс соответствует количеству подключенных устройств
for (int i = 0; i < msgLength-startPos; i++){ // парсим каждый символ смс начиная с позиции где завершается секретное слово и до конца
tmp = msg.substring(i+startPos, i+startPos+1); // берем один символ
Serial.println("Device #" + String(i+1) + " = " + tmp);
if (tmp=="1") { // если 1, то включаем устройство
myDevices[i].enabled = true;
digitalWrite(myDevices[i].pin, HIGH);
delay(1000);
if (myDevices[i].touchButton==true) { // если кнопка сенсорная, то после паузы ставим логический 0
digitalWrite(myDevices[i].pin, LOW);
delay(1000);
}
}
else if (tmp=="0") { // иначе если 0, то выключаем устройство
myDevices[i].enabled = false;
if (myDevices[i].touchButton==true) { // если кнопка сенсорная, то сначала ставим логическую 1
digitalWrite(myDevices[i].pin, HIGH);
delay(1000);
}
digitalWrite(myDevices[i].pin, LOW);
delay(1000);
}
}
}
}
/*
* Функция выполняющаяся при первичной инициализации Arduino
*/
void setup(){
// Начинаем последовательный обмен данными и ждем открытие порта
Serial.begin(9600);
//while (!Serial) {
// ; // ждем когда последовательный порт подлючится. Необходимо только для Leonardo
//}
// инициализация пинов как входых или выходных
pinMode(ledPin, OUTPUT); // выход для пина светодиода
pinMode(sysledPin, OUTPUT); // выход для пина системного светодиода
pinMode(gsmPowerPin, OUTPUT); // выход для пина GSM модема
pinMode(perimeterPin, INPUT); //вход на пин периметра
pinMode(buttonPin, INPUT); // вход на пин кнопки
//Включаю GSM Модуль
digitalWrite(gsmPowerPin, HIGH);
delay(1000);
digitalWrite(gsmPowerPin, LOW);
delay(5000);
Serial1.begin(9600);
Serial1.flush();
// опрашиваем gsm-модем
Serial1.println("AT");
delay(100);
Serial1.println("AT+CSCA?"); // запрашиваем номер смс-центра
delay(300);
Serial1.println("AT+CMGF=1"); // выставляем текстовым режим сообщений
delay(300);
Serial1.println("AT+IFC=1,1"); // Set Local Data Flow Control
delay(300);
Serial1.println("AT+CPBS=\"SM\""); // SIM Phonebook Memory Storage
delay(300);
Serial1.println("AT+CNMI=1,2,2,1,1"); // Включаю перехват SMS
delay(500);
currentTime = millis(); // считать текущие значение секунд с момента запуска Arduino
loopTime = currentTime;
alarmState = 0;
// отключение аварий на всех сенсорах и их инициализация
for (int i = 0; i < numSensors; i++){
mySensors[i].alarm==false;
pinMode(mySensors[i].pin, INPUT);
}
// инициализация устройств
for (int i = 0; i < numDevices; i++){
pinMode(myDevices[i].pin, OUTPUT);
if (myDevices[i].enabled==true) {
digitalWrite(myDevices[i].pin, HIGH);
delay(1000);
if (myDevices[i].touchButton==true) {
digitalWrite(myDevices[i].pin, LOW);
delay(1000);
}
}
}
Serial.println("System Ready ..."); // Инициализация после Сброса
Serial.println(String(numSensors) + " sensor(s) detected."); // Подсчет и отображение числа сенсоров
Serial.println(String(numDevices) + " device(s) detected."); // Подсчет и отображение числа устройств
Serial.println(String(numPhones) + " phone number(s) detected."); // Подсчет и отображение числа телефонных номеров для рассылки аварийных смс
digitalWrite(sysledPin, LOW);
delay(1000);
//parseSMS("5550:01");
//getStatus();
}
/*
* Основная функция запускающая главный цикл Arduino
*/
void loop(){
digitalWrite(sysledPin, HIGH);
buttonState = digitalRead(buttonPin); // считываем состояние секретной кнопки
if(buttonState==true && alarmState==0){ // если нажата кнопка и система не на охране, то
//if(buttonState==true && perimeterState==true && alarmState==0){ // если нажата кнопка, закрыта дверь, и система не на охране, то
alarmState=1; // перевести систему в состояние готовности постановки на охрану
setAlarm(1); // сообщить гудком, что нажата кнопка
delay(1000);
Serial.println("The system is ready for arming.");
}
perimeterState = digitalRead(perimeterPin); // считываем состояние периметра
if(alarmState==1 || alarmState==2){ // если система в готовности постановки на охрану, тогда
setAlarm(1); // сообщить гудком, что нажата кнопка
delay(1000);
if(perimeterState==false && alarmState==1) { // если дверь открыли после нажатия на секретную кнопку, то
alarmState=2; // переход в крайнее положение постановки на охрану, ждем закрытия двери
//while(perimeterState==false){ // цикл, пока не закроем дверь
// perimeterState = digitalRead(perimeterPin);
}
if(perimeterState==true && alarmState==2) { // если дверь закрыли после нажатия на секретную кнопку, то
alarmState=3; // переход в крайнее положение постановки на охрану, ждем закрытия двери
setAlarm(2); // сообщить гудком, что система поставлена на охрану
Serial.println("The system is armed.");
}
}
if(alarmState==3){ // если система находится на охране
if(perimeterState==false) { // и произошло размыкание контура или открытие двери
// выполняем проверку свой-чужой через функцию checkIFF(second), где second количество секунд для нажатия секретной кнопки
if(checkIFF(10)==false) { // если в течении 10 секунд секретная кнопка не нажата, то
alarmState = 4; // перевод сигнализации в состояние - несанкционированное проникновение
sendTextMessage("WARNING! The perimeter broken, unauthorized access."); // немедленно отправляем смс сообщение о несанкционированном проникновении
Serial.println("WARNING! The perimeter broken, unauthorized access.");
}
else {
alarmState = 0; // перевод сигнализации в состояние - снята с охраны
setAlarm(0); // сообщить гудком, что система снята с охраны
Serial.println("The system is disarmed.");
delay(restTime*1000); // Система переходит в состояние покоя на restTime сек.
}
}
}
if(alarmState==4){ // если произошло размыкание периметра и система находится в режиме тревоги
runSiren(1); // Включаем серену 1 раз
buttonState = digitalRead(buttonPin); // считываем еще раз состояние секретной кнопки
currentTime = millis(); // считать текущие значение секунд с момента запуска Arduino
if(buttonState==true){ // если секретную кнопку все же нажали после сирены, то
if(currentTime >= (loopTime + 4000)){ //если кнопка нажата уже 4 секунды, то
alarmState=0; // снимаем систему с охраны
setAlarm(0); // сообщить гудком, что система снята с охраны
Serial.println("The system is disarmed after unauthorized access.");
delay(restTime*1000); // Система переходит в состояние покоя на restTime сек.
}
}
else {
loopTime = currentTime; // есди кнопка не нажата, фиксируем текущее время
}
}
//Опрос всех сенсоров
for (int i = 0; i < numSensors; i++){
mySensors[i].state = digitalRead(mySensors[i].pin);
//Serial.println(mySensors[i].name + String(" = ") + mySensors[i].state);
if (mySensors[i].state==true && mySensors[i].alarm==false){ // если сработал сенсор и состояние сенсоров было "покой", то
mySensors[i].alarm=true; // перевести систему в состояние аварии по сенсору
sendTextMessage(mySensors[i].errorMsg); // немедленно отправляем смс сообщение о сработке сенсора
Serial.println(mySensors[i].errorMsg);
runSiren(5); // Включить сирену 5 раза
}
if(mySensors[i].state==false && mySensors[i].alarm==true){ // если авария пропала и состояние сенсора было "авария", то
mySensors[i].alarm=false; // перевести систему в состояние покоя по сенсору
Serial.println(mySensors[i].errorOk);
}
if (mySensors[i].alarm==true) {
// Подавать аварийный сигнал с сенсоров можно не чаще чем раз в restSiren секунд
currentTime = millis(); // считать текущие значение секунд с момента запуска Arduino
if(currentTime >= (loopTime + (restSiren*1000))){
runSiren(5); // Включить сирену 5 раза
loopTime = currentTime;
}
}
//if (Serial.available()){ // если в буфере серийного порта есть данные (для теста)
// Serial1.print((char)Serial.read()); // отправить на модем (для теста)
//}
if (Serial1.available()){ // если в буфере серийного порта есть данные
currentStr=readSerialStr(); // считываем одну строку из буфера серийного порта посимвольно
currentStr.trim();
//Serial.println(currentStr);
if (currentStr.startsWith("+CMT")) { // и если текущая строка начинается с "+CMT", то следующая строка является сообщением
currentStr=readSerialStr(); // считываем еще одну строку из буфера серийного порта посимвольно
currentStr.trim();
//Serial.println(currentStr);
if (currentStr.compareTo("STATUS")==0) { // если sms сообщене это "STATUS"
Serial.println("Command received. Execute 'STATUS'");
getStatus(); // выполнить опрос системы и отправить отчет
}
else if (currentStr.startsWith(secretStr)) {
Serial.println("Command received. Execute parseSMS(\""+currentStr+"\");");
parseSMS(currentStr);
}
}
}
}
}