watchdog_3-leonardo/watchdog_3-leonardo.ino
2020-01-23 23:58:57 +05:00

878 lines
19 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ----------------------------------------------------------------------------------------------------
// Watchdog v3 - Leonardo
// EoF 2017
// ----------------------------------------------------------------------------------------------------
#include "watchdog_3.h"
GPRS modem(Serial1, MOD_PWR);
bool modemOK = false;
byte modemErrCount = 0;
DHT dht(T_PIN, DHT11);
char command[CMD_SIZE];
char message[SMS_SIZE];
char phone[PHONE_SIZE];
char datetime[DATE_SIZE];
char s_buffer[BUFFER_SIZE];
char l_buffer[LONG_BUFFER_SIZE];
bool cmdFromModem = false;
unsigned long t = 0;
int addr = 0;
byte temp = 0, hum = 0, loud = 0, power = 0, battery = 0;
byte btnCount = 0;
bool btnTrigger = false;
byte event;
const float r12_1 = 20000; // 20K
const float r12_2 = 10000; // 10K
const float r5_1 = 10000; // 20K
const float r5_2 = 10000; // 10K
const float rbat_1 = 10000; // 10K
const float rbat_2 = 10000; // 10K
const float rout_1 = 10000; // 10K
const float rout_2 = 10000; // 10K
float vcc = 0.0;
float k12, k5, kbat, kout;
float v12, v5, vbat, vout;
// эту константу (typVbg) необходимо откалибровать индивидуально
const float typVbg = 1.08; // 1.0 -- 1.2
void setup() {
Serial.begin(COM_BR);
Serial1.begin(MOD_BR);
modem.powerOn();
modemOK = modem.init();
// Конфигурируем пины
pinMode(A_PIN, INPUT);
pinMode(V5_PIN, INPUT);
pinMode(V12_PIN, INPUT);
pinMode(VBAT_PIN, INPUT);
pinMode(VOUT_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(BTN_PIN, INPUT);
pinMode(FAN_PIN, OUTPUT);
// Инициализируем климатический сенсор
dht.begin();
// Коэффициенты делителей напряжений
k12 = r12_2 / (r12_1 + r12_2);
k5 = r5_2 / (r5_1 + r5_2);
kbat = rbat_2 / (rbat_1 + rbat_2);
kout = rout_2 / (rout_1 + rout_2);
modem.controlGPIO(1, 0);
modem.controlGPIO(2, 0);
modem.controlGPIO(3, 0);
modem.controlGPIO(4, 0);
sprint("Init");
printParams();
sprint_P(str_help);
initEEPROM();
}
void loop() {
if (millis() - t > PERIOD) {
getStatus();
t = millis();
//sprint("Status!");
}
checkModem();
if (!cmdFromModem) {
readSerial(command, CMD_SIZE);
}
if (command[0] != '\0') {
sprint(">", false);
sprint(command);
if (!strcmp_P(command, cmd_status)) {
getStatusMessage(message);
returnAnswer(message);
} else if (!strcmp_P(command, cmd_help)) {
sprint_P(str_help);
sendHelp();
} else if (!strcmp_P(command, cmd_sms_on)) {
smsOn(true);
returnAnswer();
} else if (!strcmp_P(command, cmd_sms_off)) {
smsOn(false);
returnAnswer();
} else if (!strcmp_P(command, cmd_auth_on)) {
authOn(true);
returnAnswer();
} else if (!strcmp_P(command, cmd_auth_off)) {
authOn(false);
returnAnswer();
} else if (!strcmp_P(command, cmd_fwd_on)) {
fwdOn(true);
returnAnswer();
} else if (!strcmp_P(command, cmd_fwd_off)) {
fwdOn(false);
returnAnswer();
} else if (!strcmp_P(command, cmd_on)) {
mainPowerOn();
returnAnswer();
} else if (!strcmp_P(command, cmd_off)) {
mainPowerOff();
returnAnswer();
} else if (!strcmp_P(command, cmd_reset)) {
mainReset();
returnAnswer();
} else if (!strcmp_P(command, cmd_a)) {
a_PowerBreak();
returnAnswer();
} else if (!strcmp_P(command, cmd_b)) {
b_PowerBreak();
returnAnswer();
} else if (!strcmp_P(command, cmd_forward)) {
forwardLastSMS();
} else if (!strcmp_P(command, cmd_modem_reset) && !cmdFromModem) {
modem.reset();
modem.init();
returnAnswer();
} else if (!strcmp_P(command, cmd_modem_console) && !cmdFromModem) {
modemConsole();
} else if (!strcmp_P(command, cmd_edit_ab) && !cmdFromModem) {
editAddressBook();
} else if (!strcmp_P(command, cmd_show_ab) && !cmdFromModem) {
printAddressBook();
} else {
sprint_P(str_help);
sendHelp();
}
}
ledOn();
delay(5);
ledOff();
if (buttonPressed()) {
ledOn();
if (btnCount < BTN_COUNT) btnCount++;
} else {
ledOff();
btnCount = 0;
btnTrigger = false;
}
if (btnCount >= BTN_COUNT && !btnTrigger) {
btnTrigger = true;
if (entryNotEmpty(0)) {
loadPhoneNumber(0, phone);
modem.callUp(phone);
sprint("Out call: ", phone);
while (true) {
ledOn();
event = modem.getEvent(message, phone, datetime, l_buffer, LONG_BUFFER_SIZE, NO_CHECK);
ledOff();
delay(150);
if (event == BUSY || event == NO_CARRIER || event == NO_ANSWER) break;
if (event == ANSWER) t = millis();
if (!buttonPressed()) btnTrigger = false;
if (millis() - t > CALL_TIMEOUT || (buttonPressed() && !btnTrigger)) {
btnTrigger = true;
break;
}
}
modem.hangup();
sprint("Call over");
} else {
flash(5);
}
}
}
// Запрос статуса
word getStatus() {
unsigned int stat = 0;
// Get sensors
getTH();
if (temp > 28) stat |= TEMP_WARN;
if (hum > 85) stat |= HUM_WARN;
loud = analogRead(A_PIN);
if (loud > 250) stat |= LOUD_WARN;
getVs();
if (v12 < 10) stat |= POWER_WARN;
if (vbat < 6.4) stat |= BAT_WARN;
return stat;
}
//
void printAll() {
sprint("phone: ", phone);
sprint("date: ", datetime);
sprint("msg: ", message);
sprint("msg len: ", strlen(message));
sprint("cmd: ", command);
sprint("t: ", temp);
sprint("h: ", hum);
sprint("l: ", loud);
sprint("mdm: ", modemOK, true);
sprint("sms: ", smsOn(), true);
sprint("auth: ", authOn(), true);
sprint("fwd: ", fwdOn(), true);
sprint("Vcc: ", vcc);
sprint("V12: ", v12);
sprint("V5: ", v5);
sprint("Vbat: ", vbat);
sprint("Vout: ", vout);
}
// Формируем читабельную строку со статусом
void getStatusMessage(char *message) {
word stat = getStatus();
char s[10];
message[0] = '\0';
if (stat != 0) {
strcat(message, "WARNING");
} else {
strcat(message, "OK");
}
strcat(message, "\nT: ");
itoa(temp, s, 10);
strcat(message, s);
strcat(message, "C");
if (stat & TEMP_WARN) strcat(message, " !");
strcat(message, " H: ");
itoa(hum, s, 10);
strcat(message, s);
strcat(message, "%");
if (stat & HUM_WARN) strcat(message, " !");
strcat(message, " L: ");
itoa(loud, s, 10);
strcat(message, s);
strcat(message, "UE");
if (stat & LOUD_WARN) strcat(message, " !");
strcat(message, "\nVcc: ");
ftoa(vcc, s);
strcat(message, s);
strcat(message, "V");
strcat(message, "\nV12: ");
ftoa(v12, s);
strcat(message, s);
strcat(message, "V");
strcat(message, " V5: ");
ftoa(v5, s);
strcat(message, s);
strcat(message, "V");
strcat(message, "\nVbat: ");
ftoa(vbat, s);
strcat(message, s);
strcat(message, "V");
strcat(message, " Vout: ");
ftoa(vout, s);
strcat(message, s);
strcat(message, "V");
strcat(message, "\n\nPower: ");
if (stat & POWER_WARN) strcat(message, "Off");
else strcat(message, "On");
strcat(message, " Battery: ");
if (stat & BAT_WARN) strcat(message, "Low");
else strcat(message, "Ok");
strcat(message, "\nSMS: ");
if (smsOn()) strcat(message, "On");
else strcat(message, "Off");
strcat(message, " Auth: ");
if (authOn()) strcat(message, "On");
else strcat(message, "Off");
strcat(message, " Fwd: ");
if (fwdOn()) strcat(message, "On");
else strcat(message, "Off");
}
// Обработка событий модема
void checkModem() {
cmdFromModem = false;
command[0] = '\0';
switch (modem.getEvent(message, phone, datetime, l_buffer, LONG_BUFFER_SIZE)) {
case ERR:
sprint("Mdm err!");
modemOK = false;
if (modemErrCount < MAX_ERROR) modemErrCount++;
if (modemErrCount >= MAX_ERROR) {
sprint("Rst mdm...");
modem.reset();
if (modem.init()) {
sprint("Mdm OK");
modemOK = true;
}
}
return;
case SMS:
sprint("SMS: ", phone);
flash(3);
if (authOn() && !authOK(phone)) {
strcpy_P(command, cmd_forward);
} else {
strncpy(command, message, CMD_SIZE);
}
cmdFromModem = true;
break;
case CALL:
sprint("Call: ", phone);
if (authOn() && !authOK(phone)) {
modem.hangup();
sprint("Unauth call");
} else {
modem.answer();
t = millis();
while (!modem.ifcallEnd()) {
ledOn();
delay(1000);
ledOff();
if (millis() - t > CALL_TIMEOUT || buttonPressed()) {
modem.hangup();
break;
}
}
sprint("Call over");
}
cmdFromModem = true;
break;
case NONE:
cmdFromModem = false;
break;
}
modemErrCount = 0;
modemOK = true;
}
// Консоль модема
void modemConsole() {
sprint_P(str_modem_cons_on);
sprint("Timeout is ", TIMEOUT, false);
sprint(" ms after last command");
t = millis();
byte i = 0;
while (millis() - t < TIMEOUT) {
if (Serial1.available()) {
while(Serial1.available()) {
s_buffer[i++] = Serial1.read();
if(i == BUFFER_SIZE) break;
}
Serial.write(s_buffer, i);
i = 0;
}
if (Serial.available()) {
Serial1.write(Serial.read());
t = millis();
}
}
sprint_P(str_modem_cons_off);
}
// Читаем команды с COM-порта
void readSerial(char *com, byte com_size) {
com[0] = '\0';
com[com_size - 1] = '\0';
byte i = 0;
while (Serial.available() > 0 && i < com_size - 1) {
com[i] = Serial.read();
if (com[i] == '\r') {
com[i] = '\0';
} else if (com[i] == '\n') {
com[i] = '\0';
break;
}
i++;
}
}
// Отправляем ответ
void returnAnswer(const char * answer) {
sprint(answer);
if (modemOK && smsOn() && cmdFromModem) {
modem.sendSMS(phone, answer);
}
}
void sendHelp() {
if (modemOK && smsOn() && cmdFromModem) {
if (authOn() && !authOK(phone)) return;
modem.sendSMS(phone, str_help);
}
}
void forwardLastSMS() {
char main_phone[PHONE_SIZE];
sprint("Get SMS from: ", false);
sprint(phone);
sprint("at: ", false);
sprint(datetime);
sprint(message);
if (fwdOn() && entryNotEmpty(0)) {
loadPhoneNumber(0, main_phone);
s_buffer[0] = '\0';
strcat(s_buffer, "SMS from ");
strcat(s_buffer, phone);
strcat(s_buffer, " at ");
strcat(s_buffer, datetime);
modem.sendSMS(main_phone, s_buffer);
modem.sendSMS(main_phone, message);
}
}
// Управление подключенным оборудованием
void mainPowerOn() {
if (!isPowerOn()) {
modem.controlGPIO(1, 1);
delay(500);
modem.controlGPIO(1, 0);
}
}
void mainPowerOff() {
modem.controlGPIO(1, 1);
delay(6000);
modem.controlGPIO(1, 0);
}
void mainReset() {
modem.controlGPIO(2, 1);
delay(500);
modem.controlGPIO(2, 0);
}
void a_PowerBreak() {
modem.controlGPIO(3, 1);
delay(5000);
modem.controlGPIO(3, 0);
}
void b_PowerBreak() {
modem.controlGPIO(4, 1);
delay(5000);
modem.controlGPIO(4, 0);
}
bool isPowerOn() {
getVs();
if (v12 > 10) return true;
else return false;
}
// Адресная книга
bool savePhoneNumber(byte index, char * phone) {
if (index > AB_MAX_INDEX) return false;
addr = AB_ADDR + index * PHONE_SIZE;
for (byte i = 0; i < PHONE_SIZE; i++)
EEPROM.update(addr + i, phone[i]);
return true;
}
bool delPhoneNumber(byte index) {
if (index > AB_MAX_INDEX) return false;
clearBuffer(phone, PHONE_SIZE);
strcpy(phone, "<NONE>");
return savePhoneNumber(index, phone);
}
bool loadPhoneNumber(byte index, char * phone) {
if (index > AB_MAX_INDEX) return false;
addr = AB_ADDR + index * PHONE_SIZE;
for (byte i = 0; i < PHONE_SIZE - 1; i++)
phone[i] = EEPROM.read(addr + i);
phone[PHONE_SIZE - 1] = '\0';
return true;
}
bool entryNotEmpty(byte index) {
char phone[PHONE_SIZE];
loadPhoneNumber(index, phone);
return strcmp(phone, "<NONE>");
}
void printPhoneNumber(byte index) {
if (index > AB_MAX_INDEX) {
sprint("Max index is: ", AB_MAX_INDEX);
return;
}
loadPhoneNumber(index, phone);
sprint("", index, false);
sprint(": ", false);
sprint(phone);
}
void printAddressBook() {
sprint("i Phone");
for (byte i = 0; i <= AB_MAX_INDEX; i++)
printPhoneNumber(i);
}
void clearAddressBook() {
for (byte i = 0; i <= AB_MAX_INDEX; i++)
delPhoneNumber(i);
}
void editAddressBook() {
char *p;
char indexString[4];
byte index, i = 0;
sprint_P(str_eab);
sprint_P(str_help_ab);
sprint_P(str_format_ab);
t = millis();
while (millis() - t < TIMEOUT) {
readSerial(s_buffer, BUFFER_SIZE);
if (s_buffer[0] != '\0') {
t = millis();
if (!strcmp_P(s_buffer, cmd_exit)) {
break;
} else if (!strcmp_P(s_buffer, cmd_show)) {
printAddressBook();
} else if (!strcmp_P(s_buffer, cmd_clear)) {
clearAddressBook();
} else {
p = strstr(s_buffer, ":");
i = 0;
while(s_buffer[i] != ':' && i < BUFFER_SIZE) i++;
if (p == NULL || i == 0 || i > 1) {
sprint_P(str_format_ab);
} else {
// Get index
clearBuffer(indexString, 4);
strncpy(indexString, s_buffer, i);
index = atoi(indexString);
// Get phone
i = 0;
p = strstr(s_buffer, ":") + 1;
strcpy(phone, p);
if (strcmp(phone, "delete")) {
// Save
savePhoneNumber(index, phone);
sprint("OK");
} else {
// Delete
delPhoneNumber(index);
sprint("OK");
}
}
}
}
}
sprint_P(str_help);
}
// Утилиты
void clearBuffer(char *in_buffer, byte in_size) {
for(byte i = 0; i < in_size - 1; i++) in_buffer[i] = '\0';
}
void printParams() {
if(smsOn()) sprint("SMS on | ", false); else sprint("SMS off | ", false);
if(authOn()) sprint("Auth on | ", false); else sprint("Auth off | ", false);
if(smsOn()) sprint("Fwd on"); else sprint("Fwd off");
}
bool authOK(char *phone) {
char t_phone[PHONE_SIZE];
for(byte i = 0; i <= AB_MAX_INDEX; i++) {
loadPhoneNumber(i, t_phone);
if (!strcmp(phone, t_phone)) return true;
}
return false;
}
void sprint(const char *description, const byte data, const bool ln) {
Serial.print(description);
Serial.print(data);
if (ln) Serial.println();
}
void sprint(const char *description, const char *data, const bool ln) {
Serial.print(description);
Serial.print(data);
if (ln) Serial.println();
}
void sprint(const char *description, const int data, const bool ln) {
Serial.print(description);
Serial.print(data);
if (ln) Serial.println();
}
void sprint(const char *description, const unsigned int data, const bool ln) {
Serial.print(description);
Serial.print(data);
if (ln) Serial.println();
}
void sprint(const char *description, const long int data, const bool ln) {
Serial.print(description);
Serial.print(data);
if (ln) Serial.println();
}
void sprint(const char *description, const long unsigned int data, const bool ln) {
Serial.print(description);
Serial.print(data);
if (ln) Serial.println();
}
void sprint(const char *description, const float data, const bool ln) {
Serial.print(description);
Serial.print(data);
if (ln) Serial.println();
}
void sprint(const char *description, const bool ln) {
Serial.print(description);
if (ln) Serial.println();
}
void sprint_P(const char *description, const bool ln) {
int len = strlen_P(description);
char c;
for (byte i = 0; i < len; i++) {
c = pgm_read_byte_near(description + i);
Serial.print(c);
}
if (ln) Serial.println();
}
bool smsOn() {
addr = PARAM_ADDR + SMS_ON_INDEX;
return (bool)EEPROM.read(addr);
}
void smsOn(bool temp) {
addr = PARAM_ADDR + SMS_ON_INDEX;
EEPROM.update(addr, (byte)temp);
}
bool authOn() {
addr = PARAM_ADDR + AUTH_ON_INDEX;
return (bool)EEPROM.read(addr);
}
void authOn(bool temp) {
addr = PARAM_ADDR + AUTH_ON_INDEX;
EEPROM.update(addr, (byte)temp);
}
bool fwdOn() {
addr = PARAM_ADDR + FWD_ON_INDEX;
return (bool)EEPROM.read(addr);
}
void fwdOn(bool temp) {
addr = PARAM_ADDR + FWD_ON_INDEX;
EEPROM.update(addr, (byte)temp);
}
void fanPWM(byte power) {
analogWrite(FAN_PIN, power);
}
void fanOn() {
digitalWrite(FAN_PIN, HIGH);
}
void fanOff() {
digitalWrite(FAN_PIN, LOW);
}
void ledPWM(byte power) {
analogWrite(LED_PIN, power);
}
void ledOn() {
digitalWrite(LED_PIN, HIGH);
}
void ledOff() {
digitalWrite(LED_PIN, LOW);
}
void flash(byte count) {
for (byte i = 0; i < count; i++) {
ledOn();
delay(5);
ledOff();
delay(150);
}
}
bool buttonPressed() {
if (digitalRead(BTN_PIN) == LOW) return true;
return false;
}
float readvcc() {
byte i;
float tmp = 0.0;
// Read 1.1V reference against Avcc
// set the reference to vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
// works on an Arduino 168 or 328
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(3); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
tmp = (high<<8) | low;
tmp = (typVbg * 1023.0) / tmp;
return tmp;
}
void getVs() {
v12 = 0.0;
v5 = 0.0;
vbat = 0.0;
vout = 0.0;
v12 += readvcc() * analogRead(V12_PIN);
v5 += readvcc() * analogRead(V5_PIN);
vbat += readvcc() * analogRead(VBAT_PIN);
vout += readvcc() * analogRead(VOUT_PIN);
vcc = readvcc();
v12 = v12 / 1024.0 / k12;
v5 = v5 / 1024.0 / k5;
vbat = vbat / 1024.0 / kbat;
vout = vout / 1024.0 / kout;
}
void printVs() {
sprint("Vcc: ", vcc);
sprint("V12: ", v12);
sprint("V5: ", v5);
sprint("Vbat: ", vbat);
sprint("Vout: ", vout);
}
void getTH() {
int check;
check = dht.read();
switch (check) {
case DHT_OK:
temp = dht.getTemperatureC();
hum = dht.getHumidity();
break;
case DHT_ERROR_TIMEOUT:
temp = 253;
break;
case DHT_ERROR_CHECKSUM:
temp = 254;
break;
}
}
void ftoa(float f, char *s) {
int d1, d2;
char t[10];
s[0] = '\0';
d1 = (int)f;
f = f - d1;
f = f * 100;
d2 = (int)f;
itoa(d1, t, 10);
strcat(s, t);
strcat(s, ".");
itoa(d2, t, 10);
strcat(s, t);
}
void initEEPROM() {
int flag_addr = PARAM_ADDR + EEPROM_INIT_INDEX;
if (EEPROM.read(flag_addr) != INIT_FLAG) {
clearAddressBook();
smsOn(false);
authOn(true);
fwdOn(true);
EEPROM.write(flag_addr, INIT_FLAG);
}
}