• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Нужна помощь помогите подружить функции

sega6549

New member
всем привет, есть задача, по датчику HC-SR04 включать и выключать свет в туалете, и хочется оставить функцию кнопки при этом, иресть если кто то сидит в туалете то свет горит, и если нажать на кнопку то свет выключится, если еще нажать то включится, в общем обе функции чтоб инвертировали состояние света и при этом не мешали друг другу, я застрял на том что когда я использую датчик HC-SR04 то он если кто то есть на его пути свет держит включенным а если нет то выключеным, он постоянно выполняет условие и делает посстоянно так как прописано при соблюдении или не соблюдении условия, а при нажатии на кнопку при этом отрабатывается код кнопки свет моргает на долю секунды и возвращается в состоянии которое диктует датчик HC-SR04
в общем вот скетч, функция датчика это void ultrasonic1() а кнопки void button() помогите подружить их или объединить, заранее спасибо
Код:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ultrasonic.h>

Ultrasonic ultrasonic(13,12);

const char* ssid = "Rostelecom_Inactivated"; //Название сети
const char* password = "*********";           //пароль
const char* mqtt_server = "192.168.1.10";    //ip брокера mqtt


#define RELAY_1 2                            //Выход на реле или светодиод
#define BUTTON_1 4                           //Кнопка
#define relays_topic "relay/relay_1"         //публикуемый топик

WiFiClient espClient;                        //инициализация WiFi клиента
PubSubClient client(espClient);              //инициализация MQTT клиента
long last_mls = millis();                    //функция времени в милисекундах
long last_mls2 = millis();
boolean Status = false;                      //объявляем статус реле в 0
boolean btnPress = false;                    //объявляем что кнопка не нажата 0
boolean lastbtnStat = false;                 //объявляем что статус кнопки 0
char msg[50];

void setup()                                 //Выполняется при запуске 1 раз
{
  pinMode(RELAY_1, OUTPUT);                  //Выход на реле или светодиод
  pinMode(BUTTON_1, INPUT);                  //Кнопка
  digitalWrite(RELAY_1, Status);             //устанавливаем на выходе статус реле
  Serial.begin(115200);                      //инициализация монитора порта
  client.setServer(mqtt_server, 1883);       //подключаемся к MQTT
  client.setCallback(callback);              //нужно для вывода в монитор порта что произожло переключение
  delay(100);                                //ждем 100 милисекунд
  WiFi.begin(ssid, password);                //подключаемся к WiFi
  delay(6000);                               //ждем 6 секунд
  client.connect("ESP8266Client");                    //конектимся с брокером
  client.subscribe(relays_topic);            //подписываемся на топик
}

void reconnect_server()                      //Выполняется каждые 5 секунд
{

  if (WiFi.status() != WL_CONNECTED)         //если нет подключения с сети
  {
    WiFi.begin(ssid, password);
    Serial.println("");
    Serial.println("WiFi connect...");       //выводим в монитор порта что пытаемся подключиться
  } else                                     //если есть подключение
  {
    Serial.println("");
    Serial.println("WiFi connected");        //выводим в монитор порта что подключились
    Serial.println("IP address: ");          //выводим в монитор порта наш IP
    Serial.println(WiFi.localIP());          //выводим в монитор порта наш IP
    Serial.println(WiFi.RSSI());             //выводим в монитор порта уровень сети
 
  }

  if (!client.connected() && WiFi.status() == WL_CONNECTED) //если к сети подключились но к MQTT нет
  {
    if (client.connect("ESP8266Client"))              //если с брокером уже конектились
  {
      Serial.println("Mosquitto connect...");//выводим в монитор порта что пытаемся подключиться
      client.subscribe(relays_topic);        //подписываемся на топик
    } else                                   //если не получилось
    {
      Serial.print("failed connect Mosquitto, rc="); //пишем ошибку
      Serial.print(client.state());
      Serial.println("");
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) //функция вывода в монитор порта что произощло переключение
{
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++)
  {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  if ((char)payload[0] == '1')
  {
    Status = true;
    digitalWrite(RELAY_1, Status);
  } else {
    Status = false;
    digitalWrite(RELAY_1, Status);
  }
}



void button()       //функция кнопки
{
  btnPress = digitalRead(BUTTON_1);
  if (btnPress && !lastbtnStat)
  {
    delay(30);                           
    btnPress = digitalRead(BUTTON_1);

    if (btnPress)
    {
      Status = !Status;
      digitalWrite(RELAY_1, Status);
      client.publish(relays_topic, String(Status).c_str()); // публикуем изменение
    }
  }
  lastbtnStat = btnPress;
}

void ultrasonic1() //функция датчика
{
  float dist_cm = ultrasonic.Ranging(CM);
  Serial.print(ultrasonic.Ranging(CM));
  Serial.println("cm");
  if (dist_cm < 110)
  {
    Status = true;
    digitalWrite(RELAY_1, Status);
    client.publish(relays_topic, String(Status).c_str());
  } else
  {
    Status = false;
    digitalWrite(RELAY_1, Status);
    client.publish(relays_topic, String(Status).c_str());
  }
}

void loop()                                     //основной цикл, выполняется посстоянно
{

  client.loop();

  if (millis() - last_mls > 5000)               //периодичность проверки переподключения
  {
    last_mls = millis();
    reconnect_server();
  }

if (millis() - last_mls2 > 500)                  //расстояние
  {
    last_mls2 = millis();
    ultrasonic1();
  }

  button();                                     //функция кнопки и публикации на брокер состояния
}

PS Скетч комментировал для себя чтоб не забыть что для чего, это вообще мой первый опять со скетчами)))
 

guinpin

New member
[HASHTAG]#define[/HASHTAG] MODE_ON 0 //принудительно включить
[HASHTAG]#define[/HASHTAG] MODE_OFF 1 //принудительно выключить
[HASHTAG]#define[/HASHTAG] MODE_AUTO 2//автоматически по датчику
int mode = MODE_AUTO

Если MODE_AUTO - рулит сенсор, в противном случае его показания не учитываются

А дальше можете реализовать более удобную для вас логику. Например:
- однократным нажатием на кнопку циклически перебираете режимы
- однократным нажатием на кнопку включаете/выключаете свет, длительным нажатием переводите в автоматический режим
- включаете фантазию и придумываете свой вариант.

По своему опыту могу порекомендовать следующее:
- использовать датчик типа HC-SR501. Он обеспечивает большие углы и хорошую чуствительность. Идеально монтируется в накладную телефонную розетку, выглядит аккуратно, удобно подключается к контроллеру обжатым телефонным кабелем.
- в мастерской у меня вообще нет выключателя. Если регистрируется движение, свет включается на 5 минут. Если за эти 5 минут регистрируется повторное движение - время продляется еще на 5 минут.
- в ванной установлен обычный выключатель, который подключен к контроллеру. Переключает АВТО и ВКЛЮЧЕНО. Если залезть в душ - датчик не срабатывает. Если из ВКЛЮЧЕНО переключить в АВТО, свет выключается и опрос датчика начинается через 15 секунд. Минус в том, что если вышел и надо сразу вернуться - свет надо включить выключателем.
- на выключателе установлен светодиод, который горит, если работаем по датчику и гаснет, если свет включен принудительно.
 

sega6549

New member
[HASHTAG]#define[/HASHTAG] MODE_ON 0 //принудительно включить
[HASHTAG]#define[/HASHTAG] MODE_OFF 1 //принудительно выключить
[HASHTAG]#define[/HASHTAG] MODE_AUTO 2//автоматически по датчику
int mode = MODE_AUTO

Если MODE_AUTO - рулит сенсор, в противном случае его показания не учитываются

А дальше можете реализовать более удобную для вас логику. Например:
- однократным нажатием на кнопку циклически перебираете режимы
- однократным нажатием на кнопку включаете/выключаете свет, длительным нажатием переводите в автоматический режим
- включаете фантазию и придумываете свой вариант.

По своему опыту могу порекомендовать следующее:
- использовать датчик типа HC-SR501. Он обеспечивает большие углы и хорошую чуствительность. Идеально монтируется в накладную телефонную розетку, выглядит аккуратно, удобно подключается к контроллеру обжатым телефонным кабелем.
- в мастерской у меня вообще нет выключателя. Если регистрируется движение, свет включается на 5 минут. Если за эти 5 минут регистрируется повторное движение - время продляется еще на 5 минут.
- в ванной установлен обычный выключатель, который подключен к контроллеру. Переключает АВТО и ВКЛЮЧЕНО. Если залезть в душ - датчик не срабатывает. Если из ВКЛЮЧЕНО переключить в АВТО, свет выключается и опрос датчика начинается через 15 секунд. Минус в том, что если вышел и надо сразу вернуться - свет надо включить выключателем.
- на выключателе установлен светодиод, который горит, если работаем по датчику и гаснет, если свет включен принудительно.
А можешь полностью скетч кинуть сюда, а то не очень понятно как использовать и куда прикручивать, ну или просто любой пример похожий, спасибо за советы)
 

guinpin

New member
А можешь полностью скетч кинуть сюда, а то не очень понятно как использовать и куда прикручивать, ну или просто любой пример похожий, спасибо за советы)
Это был не скетч, это был пример. Из головы.
Мои проекты были даже не ардуиновские - чистый сишник под AVR.
Вопрос не в том. Это же не работа с железом, а логика программы, которую можно сделать как больше нравится. Просто надо отделить мух от котлет. Либо режим ручной, либо автоматический. Тогда сенсор не будет вмешиваться в ручную логику.
Например, у вас в void button() вы напрямую рулите релюхой, а вы рулите режимом (mode), а в основном цикле уже смотрите в каком режиме находитесь:
MODE_ON - включили релюху
MODE_OFF - отключили
MODE_AUTO - вызвали void ultrasonic1()
 

sega6549

New member
Все же хотелось бы более конкретные примеры, но все же спасибо за наводки, буду курить))) идея различных режимов мне понрачилась, теперь осталось реализовать)))
 

sega6549

New member
Всем привет еще раз, спасибо guinpin за помочь и подсказку, собрал кое как такой вот скетч
Код:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ultrasonic.h>


const char* ssid = "Rostelecom_Inactivated"; //Название сети
const char* password = "*******";           //пароль
const char* mqtt_server = "192.168.1.2";    //ip брокера mqtt

#define RELAY_1 5                            //Выход на реле или светодиод
#define BUTTON_1 4                           //Кнопка
#define relays_topic "ESP04/relay_1"         //публикуемый топик
Ultrasonic ultrasonic(13,12);                //пины дальнометра 12,echo 13,trig

WiFiClient espClient;                        //инициализация WiFi клиента
PubSubClient client(espClient);              //инициализация MQTT клиента
long last_mls = millis();                    //функция времени в милисекундах
long last_mls2 = millis();
long last_mls3 = millis();
long last_mls4 = millis();
boolean Status = true;                      //объявляем статус реле в 0
boolean btnPress = false;                    //объявляем что кнопка не нажата 0
boolean lastbtnStat = false;                 //временная переменная для хранения статуса
int regim=1;                                 //режим по умолчанию при включении
int lastdist_cm = 40;                        //расстояние до объекта

void setup()                                 //Выполняется при запуске 1 раз
{
  pinMode(RELAY_1, OUTPUT);                  //Выход на реле или светодиод
  pinMode(BUTTON_1, INPUT);                  //Кнопка
  digitalWrite(RELAY_1, Status);             //устанавливаем на выходе статус реле
  Serial.begin(115200);                      //инициализация монитора порта
  client.setServer(mqtt_server, 1883);       //подключаемся к MQTT
  client.setCallback(callback);              //функция получения топиков с брокера
  delay(100);                                //ждем 100 милисекунд
  WiFi.begin(ssid, password);                //подключаемся к WiFi
  delay(2000);                               //ждем 2 секунд
  client.connect("ESP8266Client");           //конектимся с брокером как клиент
  client.subscribe(relays_topic);            //подписываемся на топик
}


void ultra_son()                             //функция чтения с датчика расстояния
{
  float dist_cm = ultrasonic.Ranging(CM);
  Serial.print(ultrasonic.Ranging(CM));
  Serial.println("cm");
  if (dist_cm < lastdist_cm)                 //если растояние меньше указанного
  {                                          //то включем свет
    Status = false;
    digitalWrite(RELAY_1, Status);
  } else                                     //если нет то выключаем
  {
    Status = true;
    digitalWrite(RELAY_1, Status);
  }
}

void callback(char* topic, byte* payload, unsigned int length) //читаем топики
{                                          
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++)
  {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  if ((char)payload[0] == '1')
  {
    Status = true;
    regim = 1;
  }
  else if ((char)payload[0] == '2')
  {
    Status = false;
    regim = 2;
  }else {
    regim = 3;
  }
}

void button()                                //функция нажатия кнопки и переключение режимов
{
  Status = digitalRead(RELAY_1);
  btnPress = digitalRead(BUTTON_1);
  if (btnPress==HIGH&&lastbtnStat==false)
  {
   delay(30);
   regim++;
   lastbtnStat=true;
   if(regim>3)
    {
     regim=1;
    }    
  }
  if (btnPress==LOW&&lastbtnStat==true)
  {
    lastbtnStat=false;
  
  }
  if(regim==1)                               //первый режим
  {
    Status = true;
    digitalWrite(RELAY_1, Status);
  }
  if(regim==2)                               //второй режим
  {
    Status = false;
    digitalWrite(RELAY_1, Status);
  }
  if(regim==3)                               //третий режим (авторежим)
  {
    if (millis() - last_mls2 > 500)          //функция ultra_son раз 500 милисекунд
   {
    last_mls2 = millis();
    ultra_son();
   }
  }
}

void reconnect_server()                      //функция проверки подключения
{
  if (last_mls > millis()) last_mls = 0;
  if (millis() > last_mls + 5000)            
{
if (WiFi.status() != WL_CONNECTED)         //если нет подключения с сети
  {
    WiFi.begin(ssid, password); 
    Serial.println("");
    Serial.println("WiFi connect...");       //выводим в монитор порта что пытаемся подключиться
  }
  if (!client.connected() && WiFi.status() == WL_CONNECTED) //если к сети подключились но к MQTT нет

  {
    client.connect("ESP8266Client");    //из за нее не работает кнопка
    client.subscribe(relays_topic);         //подписываемся на топик
    Serial.println("Mosquitto connect..."); //выводим в монитор порта что пытаемся подключиться
   // Serial.println(millis();
  }
  last_mls = millis();
}
}

void MQTT()                                   //публикуем топик с состоянием режима
{
  if (millis() - last_mls3 > 2000)            //периодичность публикации топика
  {
    last_mls3 = millis();
    client.publish(relays_topic, String(regim).c_str());
  }
}

void loop()                                
{
  client.loop();
  MQTT();
  button();
  reconnect_server();
}
но появилась новая проблема и уже который день мучаюсь с ней, в общем в функции void reconnect_server() проверяется подключение к WiFi и к MQTT и если к MQTT не подключилось или пропало подключение то он начинает переподключаться, думаю это понятно из кода, и вот пока он переподключается у меня не работает кнопка переключения режимов работы, оытным путем выяснил что дело в строчке client.connect("ESP8266Client"); в функции void reconnect_server() получается что если нет подключения к брокеру то эта строчка пытается подключиться каждые 5 секунд, и пока она это делает ничего не работает, все висит, потом 5 секунд работает и снова по кругу, пробовал всевозможные способы вызова этой функции раз в 5 секунд и этот еще самый щадящий, остальные вообще не дают ничему работать пока не подключится, самое интересное что если убрать эту строчку и допустим вырубить WiFi то все работает, функция реконекта раз в 5 секунд пытается подключиться но другим не мешает, что не так с данной строчкой и есть ли способ что то с ней сделать
P.S. убирать совсем нельзя, так как без этой строчки если заного включить брокер то модуль не подключится к нему и тут только релог ESP-хи поможет...
 
Последнее редактирование:
Сверху Снизу