Нужда родит идею, идея - действие!
Предисловие
История создания данного девайса следующая: есть очистная станция, в которой шкафы управления насосами и компрессорами на ПЛК Schneider Electric. При отключении городской электросети и переходе на дизель генератор и обратно оборудование уходит в ошибку, для квинтирования которой необходимо на месте нажать кнопки сброса аварии и запуска компрессоров. В случае не работы компрессоров бактерии в емкостях умирают. И если рабочий день окончен, некому произвести данную процедуру.
Готовые решения есть, но стоят они в десятки раз дороже данного решения.
Ни сказать чтобы компания не могла бы позволить себе купить готовые контроллеры с облачным управлением... Так как еще на своем опыте не почувствовал любовь инициативы к инициатору, решил сам сделать данный девайс.
Железо
В качестве основы был выбран ESP32 WROOM. К нему были взяты.
Для входных пинов - Модуль опторазвязки с индикаторными светодиодами 24в на 3,3в
Для выходных пинов - модуль с реле 8ми канальный (сами реле на 5В)
Для удобства плата ESP32 установлена на плате расширения
Также для мониторинга наличия городской сети задействован эл.магнитный контактор (обычный на дин рейку)
Схема подключения
8ми канальный модуль реле подключен параллельно к кнопкам управления на шкафах управления насосной (всего шкафов 3 штуки). Было задействовано 6 реле (2е штуки остались в резерве).
Модуль оптопар был подключен параллельно к индикаторным лампам на шкафу управления компрессорами, для мониторинга работы компрессоров.
Катушка реле на дин рейке (220В) подключен к шинам питающим АВР, на нем будет 220в только тогда когда есть питание от ТП городской сети.
Создание бота в Телеграме
В сети полно более подробных инструкций, опишу все вкратце. Качаем и устанавливаем себе Телеграм на телефон или компьютер. Создаем аккаунт если его у вас еще нет. В поиске находим БОТА под ником BotFather.
Начинаем с ним диалог сообщением: /start
Он ответит какие каманды может выполнять, нас интересует: /newbot вводим и следуем инструкциям по созданию бота.
Если ваш бот успешно создан, вы получите сообщение со ссылкой для доступа к нему и токеном бота. Сохраните токен бота, он вам понадобится, чтобы обеспечить взаимодействие ESP32 с ботом.
Далее я создал группу в телеграме т.к. ботом будут управлять помимо меня другие люди.
Создаем группу даем ей имя, добавляем туда созданного нами бота (в списке контактов у вас его не будет, нужно в графе поиска набрать его имя и добавить его) и других людей которые будут иметь доступ к нашему контроллеру.
Также нужно временно добавить бота который укажет нам id нашего чата, в случае группы id будет отрицательным числом.
Программирование ESP32:
Для написания скетча была использована среда Arduino IDE 2.0
В качестве библиотеки для телеграм бота была выбрана библиотека fastbot
Сам код, с поясняющими комментариями:
#define WIFI_SSID "ImyaTochkiDostupa" //Имя сети WIFI
#define WIFI_PASS "Password" //Пароль сети WIFI
#define BOT_TOKEN "1234567890:ABCdEfghiJKlMnopQr1STUvWx23Y-4ZaBCD" //Токен бота ТЕЛЕГРАМ
#define CHAT_ID "-123456789" //ID чата в который будет писать БОТ и откуда будет принимать сообщения
#include <FastBot.h> //Подключаем библиотеку телеграм бота AlexGyver
#define gorodset 12 //Городская сеть
#define BTN_14_1_start 32 //14.1 ПУСК
#define BTN_14_2_start 33 //14.2 ПУСК
#define BTN_GrO_reset 25 //Грубая очистка СБРОС
#define BTN_MlO_reset 26 //Мелкая очистка СБРОС
#define BTN_PrE_reset 27 //Приемная емкость СБРОС
#define BTN_LOS_reset 14 //ЛОС СБРОС
#define ON_14_1 34 //14.1 Включен
#define ON_14_2 35 //14.2 Включен
unsigned short workled = 0; //Переменная для таймера для моргания светодиода индикатора работы (быстро моргает попытка к подключения к wifi, раз в секунду проход через цикл LOOP)
uint32_t startUnix; //Переменная в которую записываем время включения ESP32 для игнорирования сообщений которые приходили пока ESP32 была не в сети
bool status_14_1 = false; //Переменная для Статуса 14.1
bool prev_status_14_1 = false; //Переменная для хранения предыдущего статуса компрессора 14.1
bool status_14_2 = false; //Переменная для Статуса 14.2
bool prev_status_14_2 = false; //Переменная для хранения предыдущего статуса компрессора 14.2
bool status_gorodset = true; //Переменная для статуса городская сеть
bool prev_status_gorodset = true; //Переменная для хранения предыдущего статуса городской сети
bool auto_sbros = true; //Переменная (авто/вручную) сброс ошибок и запуск компрессоров при переходе на дизель и обратно. Если true то переключения будут автоматические
unsigned long prev_connect = 0; //Время последней попытки подключения
unsigned long time_kompr = 0; //Таймер для сообщений о том что компрессоры не запускались более 3600000 миллисекунд (ЧАС)
unsigned long prev_time_kompr = 0; //Таймер для сообщений о том что компрессоры не запускались более 3600000 миллисекунд (ЧАС)
unsigned int time_kompr_min = 0; //Таймер не работающих компрессоров в минутах
unsigned int prev_a_sbros = 0; //Таймер время с момента предыдущего авто сброса в минутах
unsigned long prev_time_kompr2 = 0;
unsigned long time_notconnected = 0; //Таймер запоминает время в котором нет подключения
unsigned long time_connected = 0; //Таймер запоминает время в которое еще есть подключение к wifi
unsigned long time_gorset = 0; //Таймер проверки Городской сети
unsigned long time_status_14_1 = 0; //Таймер проверки состояния компрессора 14.1
unsigned long time_status_14_2 = 0; //Таймер проверки состояния компрессора 14.2
unsigned long time_utro = 0; //Таймер отправки сообщения утром(для того чтобы не отправлял сообщение многократно)
String time_izm_14_1 = "hh:mm:ss"; //Время изменения состояния компрессора 14.1
String time_izm_14_2 = "hh:mm:ss"; //Время изменения состояния компрессора 14.2
String date_izm_14_1 = "dd:mm:yy"; //Дата изменения состояния компрессора 14.1
String date_izm_14_2 = "dd:mm:yy"; //Дата изменения состояния компрессора 14.2
String time_izm_gorset = "hh:mm:ss"; //Время изменения состояния городской сети
String date_izm_gorset = "dd:mm:yy"; //Дата изменения состояния городской сети
FastBot bot(BOT_TOKEN); //Инициализируем бота
void setup() {
//Настраиваем ПИНЫ
pinMode(2, OUTPUT); //2й пин, на нем сидит встроенный светодиод
digitalWrite(2, LOW); //изначально встроенный светодиод в выключееном состоянии
pinMode(BTN_14_1_start, OUTPUT);
digitalWrite(BTN_14_1_start, LOW);
pinMode(BTN_14_2_start, OUTPUT);
digitalWrite(BTN_14_2_start, LOW);
pinMode(BTN_GrO_reset, OUTPUT);
digitalWrite(BTN_GrO_reset, LOW);
pinMode(BTN_MlO_reset, OUTPUT);
digitalWrite(BTN_MlO_reset, LOW);
pinMode(BTN_PrE_reset, OUTPUT);
digitalWrite(BTN_PrE_reset, LOW);
pinMode(BTN_LOS_reset, OUTPUT);
digitalWrite(BTN_LOS_reset, LOW);
pinMode(ON_14_1, INPUT); //Нужно подтянуть к высокому уровню 3.3В через резистор, на плате DST-1R4p-N уже сделано
pinMode(ON_14_2, INPUT); //Нужно подтянуть к высокому уровню 3.3В через резистор, на плате DST-1R4p-N уже сделано
pinMode(gorodset, INPUT_PULLUP); //Нужно подтянуть к высокому уровню 3.3В через резистор, на плате DST-1R4p-N уже сделано
connectWiFi(); //Подключаемся к ВайФай
delay(1000); //ждем 1сек
bot.setChatID(CHAT_ID); // установить ID чата, чтобы принимать сообщения только из него, передай "" (пустую строку) чтобы отключить проверку
// можно указать несколько ID через запятую
//bot.setChatID("123456,7891011,12131415");
bot.skipUpdates(); //пропустить сообщения которые приходили в момент когда плата была выключена
bot.sendMessage("Я в сети!"); // отправить сообщение в чат указанный в setChatID
startUnix = bot.getUnix(); //Запоминаем время включения ESP32 для игнорирования сообщений которые приходили пока ESP32 была не в сети
bot.attach(newMsg); //подключаем функцию-обработчик
}
void newMsg(FB_msg& msg) { // обработчик сообщений
if (msg.unix < startUnix) return;// игнорировать сообщения которые приходили пока ESP32 была не в сети
//+++++++ОБНОВЛЕНИЕ ПРОШИВКИ ПО ВОЗДУХУ+++++++
if (msg.OTA && msg.fileName == "update.bin" && msg.chatID == "-123456789") bot.update(); //Прошиваем если имя файла update.bin и сообщение пришло от чата с id -123456789
//+++++++ОБНОВЛЕНИЕ ПРОШИВКИ ПО ВОЗДУХУ+++++++
if (msg.text == "/help"){ //если пришло сообщение /help...
bot.sendMessage("Я умею:\n/clock - Синхронизировать время\n/hardreset - Перезагрузка\n/auto_sbros_on - Включить автосброс\n/auto_sbros_off - выключить автосброс\n/on_14_1 - Пуск 14.1\n/on_14_2 - Пуск 14.2\n/gruboch_reset\n/melkoch_reset\n/priememk_reset\n/los_reset", msg.chatID);
}
if (msg.text == "/clock"){ //если пришло сообщение /clock...
bot.sendMessage("Синхронизирую время с серверами ТЕЛЕГРАМ", msg.chatID); //отправляем сообщение в чат (необходимо для получения времени в UNIX)
bot.getUnix(); //синхронизируем время с серверами ТЕЛЕГРАМ
FB_Time t = bot.getTime(3); //Записываем время в UNIX в локальную переменную t
time_izm_gorset = t.timeString(); //записываем время изменения состояния городской сети в формате чч:мм:сс в переменную time_izm_gorset
date_izm_gorset = t.dateString(); //записываем дату изменения состояния городской сети в формате дд:мм:гг в переменную date_izm_gorset
time_izm_14_1 = t.timeString(); //записываем время изменения состояния компрессора 14.1 в формате чч:мм:сс в переменную time_izm_14_1
date_izm_14_1 = t.dateString(); //записываем дату изменения состояния компрессора 14.1 в формате дд:мм:гг в переменную date_izm_14_1
time_izm_14_2 = t.timeString(); //записываем время изменения состояния компрессора 14.2 в формате чч:мм:сс в переменную time_izm_14_2
date_izm_14_2 = t.dateString(); //записываем дату изменения состояния компрессора 14.2 в формате дд:мм:гг в переменную date_izm_14_2
delay(1000); //ждем 1сек
proverka(); //Запускаем функцию проверки состояния компрессоров и городской сети
// отправляем состояние компрессоров и городской сети в чат
if (!status_14_1 && status_14_2) bot.sendMessage("14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
if (status_14_1 && !status_14_2) bot.sendMessage("14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
if (status_14_1 && status_14_2) bot.sendMessage("ВНИМАНИЕ! \n14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2 + "\nНеработают " + time_kompr_min + " минут(ы),\nАВТО сброс=" + String(auto_sbros), msg.chatID);
if (!status_14_1 && !status_14_2) bot.sendMessage("СТРАННО! \n14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2 + "\nАВТО сброс=" + String(auto_sbros), msg.chatID);
if (!status_gorodset) bot.sendMessage("\U00002705 ЕСТЬ городская сеть c " + time_izm_gorset + " " + date_izm_gorset, CHAT_ID);
if (status_gorodset) bot.sendMessage("\U0000274c НЕТ городской сети c " + time_izm_gorset + " " + date_izm_gorset, CHAT_ID);
}
if (msg.text == "/hardreset"){ //если пришло сообщение /hardreset...
bot.sendMessage("ПЕРЕЗАГРУЖАЮСЬ...", msg.chatID); //отправляем сообщение в чат
delay(1000); //ждем 1сек
bot.tickManual(); //пропускаем один тик для того чтобы сообщение указалось прочитанным и плата не ушла в бесконечную перезагрузку
ESP.restart(); //перезагружаем плату
}
if (msg.text == "/auto_sbros_on"){ //если пришло сообщение /auto_sbros_on...
auto_sbros = true; //записываем в переменную, автосброс включен
bot.sendMessage("Автоматический сброс аварий и запуск компрессоров \U00002705 ВКЛЮЧЕН \nАВТО сброс=" + String(auto_sbros), msg.chatID); //отправляем сообщение о новом состоянии функции автосброса
}
if (msg.text == "/auto_sbros_off"){ //если пришло сообщение /auto_sbros_off...
auto_sbros = false; //записываем в переменную, автосброс выключен
bot.sendMessage("Автоматический сброс аварий и запуск компрессоров \U0000274c ОТКЛЮЧЕН \nАВТО сброс=" + String(auto_sbros), msg.chatID); //отправляем сообщение о новом состоянии функции автосброса
}
if (msg.text == "/on_14_1"){ //если пришло сообщение /on_14_1...
digitalWrite(BTN_14_1_start, HIGH); //включаем реле ПУСК 14.1
delay(1000); //ждем 1сек
digitalWrite(BTN_14_1_start, LOW); //выключаем реле ПУСК 14.1
bot.sendMessage("СТАРТ 14.1", msg.chatID); //отправляем сообщение о пуске компрессора 14.1 в чат
}
else if (msg.text == "/on_14_2"){ //если пришло сообщение /on_14_2...
digitalWrite(BTN_14_2_start, HIGH); //включаем реле ПУСК 14.2
delay(1000); //ждем 1сек
digitalWrite(BTN_14_2_start, LOW); //выключаем реле ПУСК 14.2
bot.sendMessage("СТАРТ 14.2", msg.chatID); //отправляем сообщение о пуске компрессора 14.2 в чат
}
else if (msg.text == "/gruboch_reset"){ //если пришло сообщение /gruboch_reset...
digitalWrite(BTN_GrO_reset, HIGH); //включаем реле СБРОС ошибки Грубой очистки
delay(1000); //ждем 1сек
digitalWrite(BTN_GrO_reset, LOW); //выключаем реле СБРОС ошибки Грубой очистки
bot.sendMessage("СБРОС Аварии Грубой очистки", msg.chatID); //отправляем сообщение о сбросе ошибки грубой очистки в чат
}
else if (msg.text == "/melkoch_reset"){ //если пришло сообщение /melkoch_reset...
digitalWrite(BTN_MlO_reset, HIGH); //включаем реле СБРОС ошибки Мелкой очистки
delay(1000); //ждем 1сек
digitalWrite(BTN_MlO_reset, LOW); //выключаем реле СБРОС ошибки Мелкой очистки
bot.sendMessage("СБРОС Аварии Мелкой очистки", msg.chatID); //отправляем сообщение о сбросе ошибки мелкой очистки в чат
}
else if (msg.text == "/priememk_reset"){ //если пришло сообщение /priememk_reset...
digitalWrite(BTN_PrE_reset, HIGH); //включаем реле СБРОС ошибки приемной емкости
delay(1000); //ждем 1сек
digitalWrite(BTN_PrE_reset, LOW); //выключаем реле СБРОС ошибки приемной емкости
bot.sendMessage("СБРОС Аварии Приемной емкости", msg.chatID); //отправляем сообщение о сбросе ошибки приемной емкости в чат
}
else if (msg.text == "/los_reset"){ //если пришло сообщение /los_reset...
digitalWrite(BTN_LOS_reset, HIGH); //включаем реле СБРОС ошибки ЛОС
delay(1000); //ждем 1сек
digitalWrite(BTN_LOS_reset, LOW); //выключаем реле СБРОС ошибки ЛОС
bot.sendMessage("СБРОС Аварии ЛОС", msg.chatID); //отправляем сообщение о сбросе ошибки ЛОС в чат
}
// сверяем состояния компрессоров и состояние городской сети, исходя из этого отправляем нужное сообщение в чат
else if (msg.text == "/status"){ //если пришло сообщение /status...
if (!status_14_1 && status_14_2) bot.sendMessage("14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
if (status_14_1 && !status_14_2) bot.sendMessage("14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
if (status_14_1 && status_14_2) bot.sendMessage("ВНИМАНИЕ! \n14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2 + "\nНеработают " + time_kompr_min + " минут(ы),\nАВТО сброс=" + String(auto_sbros), msg.chatID);
if (!status_14_1 && !status_14_2) bot.sendMessage("СТРАННО! \n14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
if (!status_gorodset) bot.sendMessage("\U00002705 ЕСТЬ городская сеть c " + time_izm_gorset + " " + date_izm_gorset + "\nАВТО сброс=" + String(auto_sbros), CHAT_ID);
if (status_gorodset) bot.sendMessage("\U0000274c НЕТ городской сети c " + time_izm_gorset + " " + date_izm_gorset + "\nАВТО сброс=" + String(auto_sbros), CHAT_ID);
}
else if (msg.text == "/lllsbroslll"){ //если пришло сообщение /lllsbroslll...
bot.sendMessage("Начинаю СБРОС после переключения электричества...", msg.chatID); //отправляем сообщение о начале сброса ошибок и запуска компрессоров в чат
int32_t lmsgidd = bot.lastBotMsg(); //переменная куда запоминаем id сообщения отправленного ботом выше для его последующего редактирования
digitalWrite(BTN_LOS_reset, HIGH); //включаем реле СБРОС ошибки ЛОС
delay(1000); //ждем 1сек
digitalWrite(BTN_LOS_reset, LOW); //выключаем реле СБРОС ошибки ЛОС
bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...", msg.chatID); //редактируем предыдущее сообщение с добавлением новой информации(аналогично сообщения ниже)
delay(1000); //ждем 1сек
digitalWrite(BTN_GrO_reset, HIGH); //включаем реле СБРОС ошибки Грубой очистки
delay(1000); //ждем 1сек
digitalWrite(BTN_GrO_reset, LOW); //выключаем реле СБРОС ошибки Грубой очистки
bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...", msg.chatID);
delay(1000); //ждем 1сек
digitalWrite(BTN_MlO_reset, HIGH); //включаем реле СБРОС ошибки Мелкой очистки
delay(1000); //ждем 1сек
digitalWrite(BTN_MlO_reset, LOW); //выключаем реле СБРОС ошибки Мелкой очистки
bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...", msg.chatID);
delay(1000); //ждем 1сек
digitalWrite(BTN_PrE_reset, HIGH); //включаем реле СБРОС ошибки Приемной емкости
delay(1000); //ждем 1сек
digitalWrite(BTN_PrE_reset, LOW); //выключаем реле СБРОС ошибки Приемной емкости
bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...", msg.chatID);
delay(1000); //ждем 1сек
digitalWrite(BTN_14_1_start, HIGH); //включаем реле Пуск 14.1
delay(1000); //ждем 1сек
digitalWrite(BTN_14_1_start, LOW); //выключаем реле Пуск 14.1
bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...", msg.chatID);
delay(1000); //ждем 1сек
digitalWrite(BTN_14_2_start, HIGH); //включаем реле Пуск 14.2
delay(1000); //ждем 1сек
digitalWrite(BTN_14_2_start, LOW); //выключаем реле Пуск 14.2
bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...", msg.chatID);
delay(3000); //ждем 3сек
proverka(); //Запускаем функцию проверки состояния компрессоров и городской сети
delay(500); //ждем 0,5сек
//исходя из состояния компрессоров и городской сети, редактируем последнее сообщение с добавлением статуса компрессоров и городской сети
if (!status_14_1 && status_14_2) bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...\n\n14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
if (status_14_1 && !status_14_2) bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...\n\n14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
if (status_14_1 && status_14_2) bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...\n\n!!! ВНИМАНИЕ !!!\n14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
if (!status_14_1 && !status_14_2) bot.editMessage(lmsgidd, "Начинаю СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...\n\nСТРАННО!\n14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, msg.chatID);
delay(1000); //ждем 1сек
bot.sendMessage("ГОТОВО", msg.chatID); //отправляем сообщение ГОТОВО в чат
}
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void loop() {
margun(); //вызов функции мигания встроенного светодиода
bot.tick(); //тикаем в луп (проверка входящих и отметка о прочтении предыдущих)
FB_Time t = bot.getTime(3); //Записываем время в UNIX в локальную переменную t
String vremya = t.timeString(); //записываем текущее время в формате чч:мм:сс в переменную vremya
if (millis() - time_utro >= 600000){ //делаем фильтр для того чтобы контроллер не начал спамить в 10:00:00
if (vremya == "10:00:00" || vremya == "10:00:01"){ //условие если каким то образом пропустили 10:00:00
if (!status_14_1 && status_14_2) bot.sendMessage("14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID);
if (status_14_1 && !status_14_2) bot.sendMessage("14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID);
if (status_14_1 && status_14_2) bot.sendMessage("ВНИМАНИЕ! \n14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2 + "\nНеработают " + time_kompr_min + " минут(ы),\nАВТО сброс=" + String(auto_sbros), CHAT_ID);
if (!status_14_1 && !status_14_2) bot.sendMessage("СТРАННО! \n14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID);
if (!status_gorodset) bot.sendMessage("\U00002705 ЕСТЬ городская сеть c " + time_izm_gorset + " " + date_izm_gorset + "\nАВТО сброс=" + String(auto_sbros), CHAT_ID);
if (status_gorodset) bot.sendMessage("\U0000274c НЕТ городской сети c " + time_izm_gorset + " " + date_izm_gorset + "\nАВТО сброс=" + String(auto_sbros), CHAT_ID);
delay(2000); //задержка, можно убрать т.к. выше установлен таймер на 10ть минут
time_utro = millis(); //сброс таймера после отправки сообщения в 10:00:00
}
}
proverka(); //проверяем состояние компрессоров 14.1 и 14.2, если они отключены более 3600000 мсек(ЧАС) - отправляем сообщение в чат
if (auto_sbros == true){ //если автосброс включен... (включен по умолчанию (прсле перезагрузки платы), можно отключить отправив сообщение боту /auto_sbros_off)
if (millis() - prev_a_sbros >= 1800000) a_sbros(); //если включен авто сброс и время с момента предыдущего а.сброса прошло больше 30ти минут, совершить авто сброс
}
stat_wifi(); //Проверка состояния подключения к сети wifi
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void connectWiFi() { //функция подключения к сети wifi
delay(2000); //ждем 2сек
WiFi.begin(WIFI_SSID, WIFI_PASS); //подключаемся используя указанные выше SSID(имя сети) и PASS(пароль от данной сети)
prev_connect = millis(); //записываем время когда была попытка подключиться
while (WiFi.status() != WL_CONNECTED){ //пока нет водключения к сети wifi...
for (unsigned short x=0;x<=2;x++){ //цикл для обозначения встроенным светодиодом о том, что нет соединения с wifi сетью (быстрое мерцание) unsigned short в arduino ide 0...255
digitalWrite(2, HIGH); //включаем встроенный светодиод
delay(250); //ждем 0,25сек
digitalWrite(2, LOW); //выключаем встроенный светодиод
delay(250); //ждем 0,25сек
}
if ((millis() - prev_connect) > 5000) return; //ждем 5 сек
}
}
void stat_wifi() { //Проверка состояния подключения к сети wifi
if (WiFi.status() != WL_CONNECTED) { //если нет водключения к сети wifi...
digitalWrite(2, HIGH); //включаем встроенный светодиод
delay(50); //ждем 0,05сек
digitalWrite(2, LOW); //выключаем встроенный светодиод
delay(50); //ждем 0,05сек
time_notconnected = millis(); //записываем последнее время при котором не было подключения к сети wifi для таймера переподключения
}
if (WiFi.status() == WL_CONNECTED) time_connected = millis(); //если плата подключена к сети wifi, записываем текущее время в переменную time_connected
if (WiFi.status() != WL_CONNECTED && ((time_notconnected - time_connected) >= 60000) && (millis() - prev_connect) > 15000) connectWiFi(); //если нет подключения более 1 мин пытаемся переподключиться
}
void proverka(){ //функция проверки (проверяем состояние компрессоров 14.1 и 14.2, если они отключены более 3600000 мсек(ЧАС) - отправляем сообщение в чат)
if (millis() - time_gorset >= 1500){ //Проверяем состояние городской сети раз в 1,5сек. для избегания дребезга контактов
status_gorodset = digitalRead(gorodset); //записываем состояние городской сети в переменную status_gorodset
time_gorset = millis(); //записываем время для таймера переодичности опроса состояния городской сети
}
if (status_gorodset != prev_status_gorodset){ //Если состояние городской сети изменилось (пропала гор сеть или появилась)
prev_status_gorodset = status_gorodset; //записываем новое состояние гор.сети в переменную предыдущего состояния гор.сети
FB_Time t = bot.getTime(3); //Записываем время в UNIX в локальную переменную t
time_izm_gorset = t.timeString(); //записываем время изменения состояния городской сети в формате чч:мм:сс в переменную time_izm_gorset
date_izm_gorset = t.dateString(); //записываем дату изменения состояния городской сети в формате дд:мм:гг в переменную date_izm_gorset
if (!status_gorodset) bot.sendMessage("\U00002705 ЕСТЬ городская сеть с " + time_izm_gorset + " " + date_izm_gorset, CHAT_ID);
if (status_gorodset) bot.sendMessage("\U0000274c НЕТ городской сети с " + time_izm_gorset + " " + date_izm_gorset, CHAT_ID);
if (auto_sbros == true) bot.sendMessage("Автоматический сброс аварий и запуск компрессоров \U00002705 ВКЛЮЧЕН", CHAT_ID);
if (auto_sbros == false) bot.sendMessage("Автоматический сброс аварий и запуск компрессоров \U0000274c ВЫКЛЮЧЕН", CHAT_ID);
}
if (millis() - time_status_14_1 >=1500){ //Проверяем состояние 14.1 раз в 1,5сек. для избегания дребезга контактов
status_14_1 = digitalRead(ON_14_1); //Записываем состояние компрессора 14.1 в переменную status_14_1
if (status_14_1 != prev_status_14_1){ //Если состояние коипрессора 14.1 изменилось...
prev_status_14_1 = status_14_1; //Записываем новое состояние компрессора 14.1 в переменную последнего состояния prev_status_14_1
FB_Time t = bot.getTime(3); //Записываем время в UNIX в локальную переменную t
time_izm_14_1 = t.timeString(); //записываем время изменения состояния компрессора 14.1 в формате чч:мм:сс в переменную time_izm_14_1
date_izm_14_1 = t.dateString(); //записываем дату изменения состояния компрессора 14.1 в формате дд:мм:гг в переменную date_izm_14_1
}
time_status_14_1 = millis(); //записываем время для таймера переодичности опроса состояния компрессора 14.1
}
if (millis() - time_status_14_2 >=1500){ //Проверяем состояние 14.2 раз в 1,5сек. для избегания дребезга контактов
status_14_2 = digitalRead(ON_14_2); //Записываем состояние компрессора 14.2 в переменную status_14_2
if (status_14_2 != prev_status_14_2){ //Если состояние коипрессора 14.2 изменилось...
prev_status_14_2 = status_14_2; //Записываем новое состояние компрессора 14.2 в переменную последнего состояния prev_status_14_2
FB_Time t = bot.getTime(3); //Записываем время в UNIX в локальную переменную t
time_izm_14_2 = t.timeString(); //записываем время изменения состояния компрессора 14.2 в формате чч:мм:сс в переменную time_izm_14_2
date_izm_14_2 = t.dateString(); //записываем дату изменения состояния компрессора 14.2 в формате дд:мм:гг в переменную date_izm_14_2
}
time_status_14_2 = millis(); //записываем время для таймера переодичности опроса состояния компрессора 14.2
}
if (!status_14_1 || !status_14_2) { //Если один из компрессоров включен...
time_kompr = millis(); //записываем время для таймера состояния компрессоров time_kompr
prev_time_kompr = millis(); //записываем время для таймера предыдущего состояния компрессоров prev_time_kompr
prev_time_kompr2 = millis(); //записываем время для таймера предыдущего состояния компрессоров prev_time_kompr2
prev_a_sbros = millis(); //записываем время для таймера запоминания предыдущего времени автоматического сброса
}
if (status_14_1 == true && status_14_2 == true){ //если оба компрессора выключены...
time_kompr = millis(); //записываем время отключенного состояния обоих компрессоров в переменную time_kompr
time_kompr_min = ((time_kompr - prev_time_kompr2)/60000); //считаем и записываем время которое не работают оба компрессора в минутах, в переменную time_kompr_min
}
if (status_14_1 == true && status_14_2 == true && ((time_kompr - prev_time_kompr) >= 3600000)){ //если оба компрессора отключены и находятся в таком состоянии болше 3600000мс-часа...
prev_time_kompr = millis(); //записываем время для таймера переодичности оповещения о том, что таймеры не работают в чат
bot.sendMessage("Компрессоры не работают уже " + String(time_kompr_min) + " минут\nАВТО сброс=" + String(auto_sbros), CHAT_ID); //отправляем оповещение в чат
}
}
void a_sbros(){ //Функция автоматического сброса аварий и включения компрессоров
prev_a_sbros = millis(); //Запоминаем время автосброса в минутах
bot.sendMessage("Начинаю Автоматический СБРОС после переключения электричества...", CHAT_ID); //отправляем сообщение в чат
int32_t lmsgidd = bot.lastBotMsg(); //переменная куда запоминаем id сообщения отправленного ботом выше для его последующего редактирования
digitalWrite(BTN_LOS_reset, HIGH); //включаем реле "Сброс аварии ЛОС"
delay(1500); //ждем 1,5сек
digitalWrite(BTN_LOS_reset, LOW); //выключаем реле "Сброс аварии ЛОС"
bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...", CHAT_ID); //редактируем предыдущее сообщение с дополнением
delay(1500); //ждем 1,5сек
digitalWrite(BTN_GrO_reset, HIGH); //включаем реле "Сброс аварии Грубой очистки"
delay(1500); //ждем 1,5сек
digitalWrite(BTN_GrO_reset, LOW); //выключаем реле "Сброс аварии Грубой очистки"
bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...", CHAT_ID); //редактируем пред.сообщение
delay(1500); //ждем 1,5сек
digitalWrite(BTN_MlO_reset, HIGH); //включаем реле "Сброс аварии Мелкой очистки(14.х)"
delay(1500); //ждем 1,5сек
digitalWrite(BTN_MlO_reset, LOW); //выключаем реле "Сброс аварии Мелкой очистки(14.х)"
bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...", CHAT_ID);
delay(1500); //ждем 1,5сек
digitalWrite(BTN_PrE_reset, HIGH); //включаем реле "Сброс аварии Приемной емкости"
delay(1500); //ждем 1,5сек
digitalWrite(BTN_PrE_reset, LOW); //выключаем реле "Сброс аварии Приемной емкости"
bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...", CHAT_ID);
delay(1500); //ждем 1,5сек
digitalWrite(BTN_14_1_start, HIGH); //включаем реле "Пуск компрессора 14.1"
delay(1500); //ждем 1,5сек
digitalWrite(BTN_14_1_start, LOW); //выключаем реле "Пуск компрессора 14.1"
bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...", CHAT_ID);
delay(1500); //ждем 1,5сек
digitalWrite(BTN_14_2_start, HIGH); //включаем реле "Пуск компрессора 14.2"
delay(1500); //ждем 1,5сек
digitalWrite(BTN_14_2_start, LOW); //выключаем реле "Пуск компрессора 14.2"
bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...", CHAT_ID);
delay(3000); //ждем 3сек
proverka(); //выполняем функцию проверки (опрос состояния компрессором и городской сети)
if (!status_14_1 && status_14_2) bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...\n\n14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID);
if (status_14_1 && !status_14_2) bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...\n\n14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID);
if (status_14_1 && status_14_2) bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...\n\n!!! ВНИМАНИЕ !!!\n14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID);
if (!status_14_1 && !status_14_2) bot.editMessage(lmsgidd, "Начинаю Автоматический СБРОС после переключения электричества...\nСБРОС Аварии ЛОС...\nСБРОС Аварии Грубой очистки...\nСБРОС Аварии Мелкой очистки...\nСБРОС Аварии Приемной емкости...\nСТАРТ 14.1...\nСТАРТ 14.2...\n\nСТРАННО!\n14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID);
delay(1000); //ждем 1сек
bot.sendMessage("ГОТОВО", CHAT_ID); //отправляем сообщение в чат
}
void margun (){ //Функция мигания встроенным светодиодом для визуального определения работы программы(если светодиод не мигает программа гдето зависла)
if (workled <= 2767) { //если значение переменной workled менее или равно 2767
digitalWrite(2, HIGH); //включаем встроенный светодиод
workled++; //увеличиваем значение в переменной workled на единицу
}
if (workled > 2767) { //если значение переменной workled более 2767
digitalWrite(2, LOW); //выключаем встроенный светодиод
workled++; //увеличиваем значение в переменной workled на единицу
}
}
Комментарии (16)
GennPen
14.04.2023 19:05А если после пропадания электричества действительно какая нть аварийная ситуация случится, а вы ее сбросите автоматикой?
marsel790 Автор
14.04.2023 19:05Ну от всего не перестраховаться, и от этого не зависит ничья жизнь и здоровье, в случае аварии промышленный контроллер просто снова свалиться в ошибку.
romanetz_omsk
14.04.2023 19:05А в чëм сакральный смысл отваливаться в ошибку при пропадании питания?
marsel790 Автор
14.04.2023 19:05Этого незнаю, прошивки нету и логики выпадения в ошибку сказать не могу.
dcs_pls
14.04.2023 19:05Неожиданно. Для частного дома прикольно, но для промышленного оборудования есть огромное количество готовых решений, а еще проще перепрограммировать ПЛК. Но плюс однозначно.
marsel790 Автор
14.04.2023 19:05Это была частная инициатива, а так конечно есть плк с облачными сервисами, но они стоят тысяч 40, а эта штука тысячи 2
romanetz_omsk
14.04.2023 19:05"Облачные сервисы" к ПЛК обычный роутер на openwrt перешитый добавляет. Вай-фай на объекте у вас уже есть, судя по всему.
dcs_pls
14.04.2023 19:05В районе четырех тысяч полно готовых решений для умных домов например GSM реле SimPal-D210. Но я так понял что оборудование дорогое и надо подходить системно, как уже тут писали защитить ПЛК и другое микропроцессорное оборудование УПС-ом, перепрограммировать алгоритмы ПЛК для нормального запуска при переходе на дизель. Если нужен удаленный мониторинг и управление установить устройство телемеханики. Но как частная инициатива, я бы дал премию.
Dr_Faksov
14.04.2023 19:05+1Странное впечатление от статьи. Всё вроде по делу, но не покидает ощущение перебора.
pistoletov
14.04.2023 19:05Если надежность высокая не нужна то хороший вариант. От себя могу порекомендовать что бы есп периодически что-то скидывала в бота. Типа статусы какие-то что бы понимать что сама есп работает исправно. Сваливание в ошибку после пропадания питания наверное и сделано что бы человек проконтролировал нормальное восстановление. Бактерии то могут погибнуть-)
marsel790 Автор
14.04.2023 19:05Часть кода
FB_Time t = bot.getTime(3); //Записываем время в UNIX в локальную переменную t String vremya = t.timeString(); //записываем текущее время в формате чч:мм:сс в переменную vremya if (millis() - time_utro >= 600000){ //делаем фильтр для того чтобы контроллер не начал спамить в 10:00:00 if (vremya == "10:00:00" || vremya == "10:00:01"){ //условие если каким то образом пропустили 10:00:00 if (!status_14_1 && status_14_2) bot.sendMessage("14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID); if (status_14_1 && !status_14_2) bot.sendMessage("14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID); if (status_14_1 && status_14_2) bot.sendMessage("ВНИМАНИЕ! \n14.1 Выключен \U0000274c в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Выключен \U0000274c в " + time_izm_14_2 + " " + date_izm_14_2 + "\nНеработают " + time_kompr_min + " минут(ы),\nАВТО сброс=" + String(auto_sbros), CHAT_ID); if (!status_14_1 && !status_14_2) bot.sendMessage("СТРАННО! \n14.1 Включен \U00002705 в " + time_izm_14_1 + " " + date_izm_14_1 + "\n14.2 Включен \U00002705 в " + time_izm_14_2 + " " + date_izm_14_2, CHAT_ID); if (!status_gorodset) bot.sendMessage("\U00002705 ЕСТЬ городская сеть c " + time_izm_gorset + " " + date_izm_gorset + "\nАВТО сброс=" + String(auto_sbros), CHAT_ID); if (status_gorodset) bot.sendMessage("\U0000274c НЕТ городской сети c " + time_izm_gorset + " " + date_izm_gorset + "\nАВТО сброс=" + String(auto_sbros), CHAT_ID); delay(2000); //задержка, можно убрать т.к. выше установлен таймер на 10ть минут time_utro = millis(); //сброс таймера после отправки сообщения в 10:00:00 } }
В 10 утра бот шлет сообщения о статусе гор.сети и компрессоров
DieSlogan
14.04.2023 19:05+1Friendly advice:
Использование вместо unsigned short -> uint16_t, unsigned long -> uint64_t, unsigned int -> uint32_t
Даёт гарантию того, что тип будет именно столько бит вне зависимости от платформы.
aborouhin
Всё-таки кажется, что бороться надо было с первопричиной. А почему это происходит? Переключение не моментальное? - так ставим какой-нибудь самый дохлый UPS или вообще конденсатор. Генератор вместо синусоиды выдаёт непойми что? - так ставим стабилизатор.
Ivanii
Только не с этой и не так, если нельзя организовать непрерывное питание, а при мощностях в сотни киловатт это не легко то для механики и двигателей нужно сделать паузу в электропитании для остановки и штатного запуска, а плк запрограммировать на нормальный старт.
marsel790 Автор
Основная задача была мониторить, а остальное уже было сделано поскольку были подведены провода параллелящие кнопки.
marsel790 Автор
Причину пропадания эл.энергии устранить невозможно т.к. мы потребитель 3й категории. Дизель генератор заводится и это занимает секунд 30 т.к. он выдает по 500А на фазу. А эта штука esp32 управляет 3мя разными шкафами, если ставить ups то нужно чтобы он питал только цепи управления. Поэтому был выбран самый легкий с моей точки зрения вариант и если он нам не подойдет его демонтаж займет от силы пол часа.