FAQ ESP8266

Подключение исполнительных устройств, датчиков, контроллеров.

Модератор: immortal

Аватара пользователя
nick7zmail
Сообщения: 7573
Зарегистрирован: Пн окт 28, 2013 8:14 am
Откуда: Екатеринбург
Благодарил (а): 121 раз
Поблагодарили: 2010 раз

Re: FAQ ESP8266

Сообщение nick7zmail » Пт май 29, 2020 11:02 am

Ну для начала я бы избавился от delay...иначе получать сигнал с датчика по прошествии 3 сек с его сработки (а тем более, к примеру, включать свет по таким показаниям) - как минимум не комфортно...во вторых увеличил бы интервал отправки температуры/влажности....зачем так часто спамить (для отладки хорошо, но на постоянку я бы засылал раз в 5-10 минут, а движение, естесственно, при сработке по факту).
Вот тут очитайте про базовые таймеры https://arduinomaster.ru/program/arduino-delay-millis/
За это сообщение автора nick7zmail поблагодарил:
ZyaK (Пт май 29, 2020 12:52 pm)
Рейтинг: 1.16%
Raspberry Pi3+Broadlink+esp8266 (blynk)+AMS
Если вам помогло какое-либо сообщение - не забывайте пользоваться кнопкой "СПАСИБО".
:arrow: Услуги в профиле коннект
>>>>>Мой новый канал на ютутбе, подписывайтесь!<<<<<
ZyaK
Сообщения: 407
Зарегистрирован: Вт окт 24, 2017 2:01 pm
Откуда: Ижевск
Благодарил (а): 45 раз
Поблагодарили: 69 раз

Re: FAQ ESP8266

Сообщение ZyaK » Пт май 29, 2020 12:51 pm

Semendey писал(а):
Пт май 29, 2020 9:15 am
Может что то можно улучшить ?
улучшать необходимо.
в лупе у вас постоянный спам от датчиков температуры да еще и задержки по 1 секунде...
задержки убирать однозначно, спам в сериал порт и преобразования датчиком температуры убирать в отдельную функцию.
в лупе завести таймер и при определенном значении вызывать функцию опроса датчиков и спама в порт.
датчик движения я так понимаю работает по прерыванию? обработчика прерывания не увидел.
Как то так примерно должно выглядеть
Вложения
IMG_20200529_135953.jpg
IMG_20200529_135953.jpg (1.41 МБ) 3307 просмотров
За это сообщение автора ZyaK поблагодарил:
Semendey (Пт май 29, 2020 1:25 pm)
Рейтинг: 1.16%
PI2 + MQTT + 5 ESP8266 + Atmega16
Аватара пользователя
nick7zmail
Сообщения: 7573
Зарегистрирован: Пн окт 28, 2013 8:14 am
Откуда: Екатеринбург
Благодарил (а): 121 раз
Поблагодарили: 2010 раз

Re: FAQ ESP8266

Сообщение nick7zmail » Пт май 29, 2020 1:46 pm

А ещё советую глянуть код AMS (Arduino Mega Server), тут на форуме есть проект...там очень много мусора в коде, но конкретно с таймерами там максимально понятно сделано. Я даже конкретно вырезку с таймерами использовал в отдельных проектах.
Raspberry Pi3+Broadlink+esp8266 (blynk)+AMS
Если вам помогло какое-либо сообщение - не забывайте пользоваться кнопкой "СПАСИБО".
:arrow: Услуги в профиле коннект
>>>>>Мой новый канал на ютутбе, подписывайтесь!<<<<<
Semendey
Сообщения: 68
Зарегистрирован: Чт сен 26, 2019 4:14 pm
Откуда: Ярославль
Благодарил (а): 19 раз
Поблагодарили: 6 раз

Re: FAQ ESP8266

Сообщение Semendey » Пт май 29, 2020 2:34 pm

ZyaK писал(а):
Пт май 29, 2020 12:51 pm
Semendey писал(а):
Пт май 29, 2020 9:15 am
Может что то можно улучшить ?
улучшать необходимо.
в лупе у вас постоянный спам от датчиков температуры да еще и задержки по 1 секунде...
задержки убирать однозначно, спам в сериал порт и преобразования датчиком температуры убирать в отдельную функцию.
в лупе завести таймер и при определенном значении вызывать функцию опроса датчиков и спама в порт.
датчик движения я так понимаю работает по прерыванию? обработчика прерывания не увидел.
Как то так примерно должно выглядеть
Прошу прощения, вот это не функция обработки прерывания

void IRAM_ATTR detectsMovementD6() { // Функция при обнаружении движения на D6
Serial.println("Движение на D6!!!");
}
ZyaK
Сообщения: 407
Зарегистрирован: Вт окт 24, 2017 2:01 pm
Откуда: Ижевск
Благодарил (а): 45 раз
Поблагодарили: 69 раз

Re: FAQ ESP8266

Сообщение ZyaK » Пт май 29, 2020 3:01 pm

А где вы её вызываете то? она у вас обозначена в начале программы и всё, где она вызывается? в лупе о ней нет ни строчки, что происходит внутри функции тоже нигде не написано.
это должно быть организовано на подобии кнопки, в лупе вызываем таймер, если таймер наступил - делаем действия по таймеру, после таймера опрос функций обработчиков входных пинов, в функции опрашивается состояние пина если высокий - ничего не делаем, выходим из функции, если упал на 0, проверяем еще раз через 10 миллисекунд (защита от дребезга) если высокий - ничего не делаем и выходим из функции, если 0 -> обрабатываем срабатывание датчика движения кричим, орем, мигаем светодиодом или там данные на сервер передаём - выходим из обработчика функции -> дальше крутимся по бесконечному циклу, проверяем таймер, опять опрашиваем состояния входных пинов
так же можно добавить таймер и еще одну переменную что бы не опрашивать несколько сотен раз в секунду уже включенный датчик движения, а то при каждом заходе в функцию пока датчик включен будут на сервер лететь пакеты которые и так уже нафиг не нужны. называется "Флаг срабатывания" когда датчик отключился, соответственно пин стал опять в высокий уровень, флаг сбрасывается в 0. при следующем срабатывании датчика флаг выставляется в 1 после отправки данных и больше в функцию отправки программа попадать не должна.
PI2 + MQTT + 5 ESP8266 + Atmega16
Semendey
Сообщения: 68
Зарегистрирован: Чт сен 26, 2019 4:14 pm
Откуда: Ярославль
Благодарил (а): 19 раз
Поблагодарили: 6 раз

Re: FAQ ESP8266

Сообщение Semendey » Пт май 29, 2020 3:31 pm

ZyaK писал(а):
Пт май 29, 2020 3:01 pm
А где вы её вызываете то? она у вас обозначена в начале программы и всё, где она вызывается? в лупе о ней нет ни строчки, что происходит внутри функции тоже нигде не написано.
это должно быть организовано на подобии кнопки, в лупе вызываем таймер, если таймер наступил - делаем действия по таймеру, после таймера опрос функций обработчиков входных пинов, в функции опрашивается состояние пина если высокий - ничего не делаем, выходим из функции, если упал на 0, проверяем еще раз через 10 миллисекунд (защита от дребезга) если высокий - ничего не делаем и выходим из функции, если 0 -> обрабатываем срабатывание датчика движения кричим, орем, мигаем светодиодом или там данные на сервер передаём - выходим из обработчика функции -> дальше крутимся по бесконечному циклу, проверяем таймер, опять опрашиваем состояния входных пинов
так же можно добавить таймер и еще одну переменную что бы не опрашивать несколько сотен раз в секунду уже включенный датчик движения, а то при каждом заходе в функцию пока датчик включен будут на сервер лететь пакеты которые и так уже нафиг не нужны. называется "Флаг срабатывания" когда датчик отключился, соответственно пин стал опять в высокий уровень, флаг сбрасывается в 0. при следующем срабатывании датчика флаг выставляется в 1 после отправки данных и больше в функцию отправки программа попадать не должна.
Спасибо Вам за ликбез, очень поучительно.
Я далеко не специалист конечно, но подскажите пожалуйста, да действительно данная функция пока ничего не делает кроме как пишет в монитор сообщение, но если я правильно разобрался то она вызывается сам когда происходит прерывание на контакте, так как флаг стоит RISING то прерывание вызывается (а значит и функция) только когда на пине происходит смена с LOW на HIGH, а значит есть сработка датчика.

Или я не прав в своих рассуждениях и не верно понял смысл прерывания?
ZyaK
Сообщения: 407
Зарегистрирован: Вт окт 24, 2017 2:01 pm
Откуда: Ижевск
Благодарил (а): 45 раз
Поблагодарили: 69 раз

Re: FAQ ESP8266

Сообщение ZyaK » Пт май 29, 2020 3:45 pm

прерывания это очень хорошо, но в данном случае его использовать нет смысла, лучше завести функцию опроса. Если по каким то причинам будет на пине дребезг или помехи, встанет вся программа и заспамит сообщениями.
PI2 + MQTT + 5 ESP8266 + Atmega16
Semendey
Сообщения: 68
Зарегистрирован: Чт сен 26, 2019 4:14 pm
Откуда: Ярославль
Благодарил (а): 19 раз
Поблагодарили: 6 раз

Re: FAQ ESP8266

Сообщение Semendey » Пт май 29, 2020 4:26 pm

ZyaK писал(а):
Пт май 29, 2020 3:45 pm
прерывания это очень хорошо, но в данном случае его использовать нет смысла, лучше завести функцию опроса. Если по каким то причинам будет на пине дребезг или помехи, встанет вся программа и заспамит сообщениями.

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

Например для датчиков окна - простых герконов, я хочу применить именно прерывание, а дребезг подавить RC - фильтрами на уровне железа.
ZyaK
Сообщения: 407
Зарегистрирован: Вт окт 24, 2017 2:01 pm
Откуда: Ижевск
Благодарил (а): 45 раз
Поблагодарили: 69 раз

Re: FAQ ESP8266

Сообщение ZyaK » Пт май 29, 2020 4:55 pm

Сам смысл прерывания это экстренно при возникновении события забить хрен на всю программу в плоть до прекращения принятий сообщений по интерфейсам (uart, i2c, spi) с целью обработки прерывания, именно по этому код в прерывании должен быть очень коротким(ставим флаг и быстро сваливаем, что бы не мешать основной программе, а потом уже где то в программе проверяется флаг и выполняются какие то действия) естественно никакие делеи там не допустимы (да и в целом по программе делеи не желательно ставить, т.к. Делей это та же функция с бесконечным циклом внутри и пока он идёт - программа стоит на месте и ждет пока кончится делей) то есть это необходимо для моментального реагирования, в случае с датчиками движения или герконами такой необходимости нет. Это как использовать мощный микропроцессор АРМовский с целью моргать светодиодом.
Но смысл поняли правильно.
Дальше двигаться необходимо в сторону создания функций обработки датчиков и обработки входов и вызывать эти функции в лупе.
так же необходимо глянуть в даташит, возможность вызова прерываний есть далеко не у каждой ноги контроллера, например у Атмеги 8 если память не изменяет этих ног всего 3
PI2 + MQTT + 5 ESP8266 + Atmega16
Semendey
Сообщения: 68
Зарегистрирован: Чт сен 26, 2019 4:14 pm
Откуда: Ярославль
Благодарил (а): 19 раз
Поблагодарили: 6 раз

Re: FAQ ESP8266

Сообщение Semendey » Пт май 29, 2020 5:30 pm

ZyaK писал(а):
Пт май 29, 2020 4:55 pm
Сам смысл прерывания это экстренно при возникновении забить хрен на всю программу в плоть до прекращения принятий сообщений по интерфейсам с целью обработки прерывания именно по этому код в прерывании должен быть очень коротким(ставим флаг и быстро сваливаем, что бы не мешать основной программе, а потом уже где то в программе проверяется флаг и выполняются какие то действия), то есть это необходимо для моментального реагирования, в случае с датчиками движения или герконами такой необходимости нет. Это как использовать мощный микропроцессор АРМовский с целью моргать светодиодом.
Но смысл поняли правильно.
Дальше двигаться необходимо в сторону создания функций обработки датчиков и обработки входов и вызывать эти функции в лупе.
так же необходимо глянуть в даташит, возможность вызова прерываний есть далеко не у каждой ноги контроллера, например у Атмеги 8 если память не изменяет этих ног всего 3

Спасибо.
Я буду использовать nodemcu V3, как понимаю у нее все GRIO, кроме 16-го поддерживают прерывание.
Конечно железка для таких целей как у меня наверное мощновата, но да ладно.
Цель у меня простая, получить с датчиков данные и отправить их в сервер.
Ниже в коде я постарался (на сколько хватает понимания) учесть ваши замечания.
Не могли бы взглянуть и высказать новые замечания

Код: Выделить всё

// Блок [Подключение библиотек] ************************************************
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <BME280I2C.h>
#include <Wire.h>
// END Блок [Подключение библиотек] ********************************************
 
// Блок [Подключение к сети WI-FI] *********************************************
const char* ssid = "ХХХХХХХХ";                // Имя WI-FI сети
const char* password = "ХХХХХХХХХХХХ";        // Пароль WI-FI сети
const char* mqtt_server = "192.168.2.3";      // IP адрес MQTT сервер
// END Блок [Подключение к сети WI-FI] *****************************************
 

#define motionSensorD6   D6 // пин, к которому подключен датчик Движения 
#define openSensorD3   D3 // пин, к которому подключен датчик Окна 1 
#define openSensorD4   D4 // пин, к которому подключен датчик Окна 2 
#define smokeSensorD5   D5 // пин, к которому подключен датчик Дыма  


#define Temp_topic "ESP/Temp"      // Топик Температуры 
#define Humidity_topic "ESP/Humidity"      // Топик Влажности
#define Pressure_topic "ESP/Pressure"      // Топик Давления
#define motionSensor_topic "ESP/Motion"      // Топик датчика движения
#define openSensor_topic1 "ESP/WINDOWS_1"     // Топик датчика окна 1
#define openSensor_topic2 "ESP/WINDOWS_2"     // Топик датчика окна 2
#define smokeSensor_topic "ESP/SMOKE"     // Топик датчика Дыма 
 
 
WiFiClient espClient;
PubSubClient client(espClient);
BME280I2C bme280;
 
long last_mls = millis();
long last_mls_temp = millis();
long last_mls_move = millis();
long last_mls_moveOff = millis();

boolean moveOn = false;
boolean Win1On = false;
boolean Win2On = false;
boolean SmokeOn = false;

int Win1Old;
int Win2Old;
int SmokeOld;


char msg[50];


void IRAM_ATTR detectsMovementD6() { // Функция при обнаружении движения на D6
    if(int val = digitalRead(motionSensorD6)){
    delay(10);
        if(int val = digitalRead(motionSensorD6)){
          client.publish(motionSensor_topic, String(val).c_str(), false); // Публикуем состояние датчика движения  
          Serial.println("Движение D6!!!");
          moveOn = true;
        }
    }
}


void IRAM_ATTR detectsOpenD3() { // Функция при обнаружении Открытия окна D3
    Win1On = true;
    Win1Old = digitalRead(openSensorD3);
 }

void IRAM_ATTR detectsOpenD4() { // Функция при обнаружении Открытия окна D4
    Win2On = true;
    Win2Old = digitalRead(openSensorD4);
 }

void IRAM_ATTR detectsSmokeD5() { // Функция при обнаружении движения на D6
  SmokeOn = true;
  SmokeOld = digitalRead(smokeSensorD5);
}


void setup() {

  Serial.begin(115200);
  client.setServer(mqtt_server, 1883);
 
  delay(100);
  WiFi.begin(ssid, password);
  delay(6000);
  client.connect("ESP8266Client");
  
  
  pinMode(motionSensorD6, INPUT_PULLUP); // уставливаем пин D6 на прием и включаем внутренний подтягивающий резистор
    // Первоначальные данные по датчику движения
    if(int val = digitalRead(motionSensorD6)){
    client.publish(motionSensor_topic, String(val).c_str(), true); // Публикуем состояние датчика движения  
    Serial.println("Движение D6!!!");
    }else{
    client.publish(motionSensor_topic, String(val).c_str(), true); // Публикуем состояние датчика движения   
    Serial.println("Все тихо D6!!!");
    }

    
  pinMode(openSensorD3, INPUT_PULLUP); // уставливаем пин D3 на прием и включаем внутренний подтягивающий резистор
  attachInterrupt(openSensorD3, detectsOpenD3, CHANGE); // Прерывание для датчика окна запуск функции  detectsOpenD3  
    // Первоначальные данные по датчику окна 1
    if(int val = digitalRead(openSensorD3)){
    client.publish(openSensor_topic1, String(val).c_str(), false); // Публикуем состояние окна 1   
    Serial.println("Открыто окно D3!!!");
    }else{
    client.publish(openSensor_topic1, String(val).c_str(), false); // Публикуем состояние окна 1  
    Serial.println("Закрыто окно D3!!!");
    } 
  pinMode(openSensorD4, INPUT_PULLUP); // уставливаем пин D4 на прием и включаем внутренний подтягивающий резистор
  attachInterrupt(openSensorD4, detectsOpenD4, CHANGE); // Прерывание для датчика окна запуск функции  detectsOpenD4    
    // Первоначальные данные по датчику окна 2 
    if(int val = digitalRead(openSensorD4)){
     client.publish(openSensor_topic2, String(val).c_str(), false); // Публикуем состояние окна 2  
    Serial.println("Открыто окно D4!!!");
    }else{
     client.publish(openSensor_topic2, String(val).c_str(), false); // Публикуем состояние окна 2  
    Serial.println("Закрыто окно D4!!!");
    }

  pinMode(smokeSensorD5, INPUT_PULLUP); // уставливаем пин D6 на прием и включаем внутренний подтягивающий резистор
  attachInterrupt(smokeSensorD5, detectsSmokeD5, CHANGE); // Прерывание для датчика движения запуск функции  detectsMovementD6
    // Первоначальные данные по датчика дыма
    if(int val = digitalRead(smokeSensorD5)){
    client.publish(smokeSensor_topic, String(val).c_str(), false); // Публикуем состояние датчика дыма  
    Serial.println("Пожар D5!!!");
    }else{
     client.publish(smokeSensor_topic, String(val).c_str(), false); // Публикуем состояние датчика дыма  
    Serial.println("Дыма нет D5!!!");
    }
    
Wire.begin();
  while(!bme280.begin())
  {
    Serial.println("Could not find BME280 sensor 76!");
    delay(1000);
  }

    
}
 
void reconnect_server() {
 
  if (WiFi.status() != WL_CONNECTED){
    WiFi.begin(ssid, password);
    Serial.println("");
    Serial.println("WiFi connect...");
  } else {
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("");
  }
 
  if(!client.connected() && WiFi.status() == WL_CONNECTED){
    if (client.connect("ESP8266Client")) {
      Serial.println("Mosquitto connect...");
  client.subscribe(motionSensor_topic);
  client.subscribe(openSensor_topic1);
  client.subscribe(openSensor_topic2);
    } else {
      Serial.print("failed connect Mosquitto, rc=");
      Serial.print(client.state());
      Serial.println("");
    }
  }
}



void IRAM_ATTR ChangeTemp() { // Функция считывания и публикации данных о температуре
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
BME280::PresUnit presUnit(BME280::PresUnit_Pa);

// Датчик температуры на адресе 0х76
float temp76(NAN), hum76(NAN), pres76(NAN);
bme280.read(pres76,temp76,hum76,tempUnit,presUnit);
Serial.print("Temp76: ");
Serial.print(temp76);
Serial.println("°C");
Serial.print("Humidity76: ");
Serial.print(hum76);
Serial.println("% RH");
Serial.print("Pressure76: ");
Serial.print(pres76*0.00750062,2);
Serial.println(" mmHg");
client.publish(Temp_topic, String(temp76).c_str(), false); // Публикуем температуру
client.publish(Humidity_topic, String(hum76).c_str(), false); // Публикуем влажность 
client.publish(Pressure_topic, String(pres76*0.00750062,2).c_str(), false); // Публикуем давление 

}


void loop(){
 
client.loop();
 
// Блок [Проверка подключения] **********************************************
if (millis() - last_mls > 60000) { // Проверка подключения к сети (раз в 60 сек.)
    last_mls = millis();
    reconnect_server();
}
// END Блок [Проверка подключения] ******************************************


// Блок [Проверка температуры ] **********************************************
if (millis() - last_mls_temp > 60000) { // Проверка температуры  (раз в 60 сек.)
    last_mls_temp = millis();
    ChangeTemp();
}
// END Блок [Проверка температуры] ****************************************** 


// Блок [Проверка движения ] **********************************************
if (moveOn = false) { // будем проверять если не зафиксировано сработки датчика
  if (millis() - last_mls_move > 3000) { // Проверка датчика движения (раз в 3 сек.)
      last_mls_move = millis();
      detectsMovementD6();
  }
}
if (moveOn = true) { // будем проверять если Зафиксировано сработка датчика
  if (millis() - last_mls_moveOff > 15000) { // Проверка  (раз в 15 сек.)
      last_mls_moveOff = millis();
      moveOn=false; // Обнуляем сработку датчика 
  }
}
// END Блок [Проверка движения] ****************************************** 


if (Win1On = true) { 
    delay(10);
    if (Win1Old = digitalRead(openSensorD3)){
    
      if(int val = digitalRead(openSensorD3)){
        client.publish(openSensor_topic1, String(val).c_str(), false); // Публикуем состояние окна 1   
        Serial.println("Открыто окно D3!!!");
      }else{
        client.publish(openSensor_topic1, String(val).c_str(), false); // Публикуем состояние окна 1  
        Serial.println("Закрыто окно D3!!!");
      }
    Win1Old = digitalRead(openSensorD3);  
    }
  Win1On = false;
}

if (Win2On = true) { 
    delay(10);
    if (Win2Old = digitalRead(openSensorD4)){
      if(int val = digitalRead(openSensorD4)){
        client.publish(openSensor_topic2, String(val).c_str(), false); // Публикуем состояние окна 2  
        Serial.println("Открыто окно D4!!!");
      }else{
        client.publish(openSensor_topic2, String(val).c_str(), false); // Публикуем состояние окна 2  
        Serial.println("Закрыто окно D4!!!");
      }
    Win2Old = digitalRead(openSensorD4);
    }
  Win2On = false;
}
if (SmokeOn = true) { 
    delay(10);
    if (SmokeOld = digitalRead(smokeSensorD5)){  
      if(int val = digitalRead(smokeSensorD5)){
        client.publish(smokeSensor_topic, String(val).c_str(), false); // Публикуем состояние датчика дыма  
        Serial.println("Пожар D5!!!");
      }else{
        client.publish(smokeSensor_topic, String(val).c_str(), false); // Публикуем состояние датчика дыма  
        Serial.println("Дыма нет D5!!!");
      }
    SmokeOld = digitalRead(smokeSensorD5);
    }
  SmokeOn = false;
}

Ответить