Сборка мультисенсорного шилда для ESP8266

В этом проекте вы узнаете, как спроектировать и создать мультисенсорный шилд для платы ESP8266 Wemos D1 Mini. Шилд имеет датчик температуры (DS18B20), датчик движения PIR, LDR и разъем для подключения релейного модуля. Мы начнем с подготовки всего оборудования, а затем запрограммируем его.

Обзор проекта

Экран состоит из датчика температуры, датчика движения, LDR и 3-контактного разъема, к можно подключить любой выход, мы будем использовать релейный модуль.

Мы также напишем программу, которая запускает веб-сервер для управления платой, используя 4 различных режима с несколькими настройками.

К концу этого проекта у вас будет похожий гаджет.

Вы сможете управлять включением и выключением реле в ручном режиме.

Или вы можете выбрать автоматические режимы:

  • Автоматический режим распознавания движения - при обнаружении движения реле остается включенным в течение определенного количества секунд.
  • Режим яркости - реле включается, когда свет падает ниже определенного порогового значения.
  • комбинированный режим - управление реле на основе текущего значения яркости и обнаружения движения.

 

Особенности мультисенсорного шилда 

Multisensor Shield имеет несколько датчиков, которые могут быть полезны для наблюдения за вашим домом. Компоненты:

  • 1 светодиод SMD для индикации состояния
  • 1x светозависимый резистор (LDR)
  • 1x датчик температуры DS18B20
  • 1x ИК-датчик движения

Кроме того, он также имеет клеммный блок, который дает доступ к GND, 5 В и GPIO15. Этот терминал может использоваться для подключения реле или любого другого выхода, которым можно управлять.

Распределение пинов ESP8266 

Ниже описывается назначение контактов для каждого компонента мультисенсорного шилда :

ИК-датчик движения GPIO5 (D1)

Датчик температуры DS18B20 GPIO4 (D2)

Фоторезистор A0

СВЕТОДИОД GPIO12 (D6)

Дополнительный вывод GPIO15 (D8)

Тестирование схемы на макете

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

Необходимые компоненты

  • ESP8266 
  • 5 мм светодиод
  • 1х 330 Ом резистор
  • 1x датчик температуры DS18B20
  • 1x мини PIR датчик движения
  • 1x фоторезистор
  • 2x резистор 10 кОм
  • 1x релейный модуль
  • макетная плата
  • перемычки

Принципиальная схема:

Проектирование печатной платы

Убедившись в правильности работы схемы на макете, я разработал печатную плату на KiCad. KiCad - это программное обеспечение с открытым исходным кодом, используемое для проектирования печатных плат.

 

Пайка компонентов

Следующим шагом является пайка компонентов на печатной плате. Я использовал SMD LED и SMD резисторы. 

 

Вот как выглядит мультисенсорный шилд после сборки всех деталей. 

Программирование 

Код для этого проекта запускает веб-сервер, который позволяет контролировать и управлять мультисенсорным шилдом на основе нескольких настраиваемых параметров.

Обзор веб-сервера

Веб-сервер, который мы создадим, позволяет вам выбирать между 4 различными режимами управления реле:

  • Ручной (режим 0): в котором у вас есть кнопка для включения и выключения реле.
  • Auto PIR (режим 1): включает реле при обнаружении движения. В этом режиме есть поле, в котором вы можете указать количество секунд, в течение которых выход будет включен после обнаружения движения.
  • LDR (режим 2): реле включается, когда освещенность падает ниже определенного порога. Вы можете установить пороговое значение LDR в диапазоне от 0 до 100%.
  • Авто PIR и LDR (режим 3): этот режим объединяет датчик движения PIR и LDR. Когда выбран этот режим, реле включается, когда ИК-датчик обнаруживает движение, и если значение яркости ниже порогового значения. В этом режиме вы можете установить таймер и пороговое значение LDR.

На веб-сервере также есть кнопка, которую вы можете нажать, чтобы запросить показания температуры. После запроса показаний температуры вы можете нажать кнопку «Удалить показания датчика», чтобы скрыть показания для оптимизации производительности веб-сервера.

В каждом режиме есть метка, которая показывает выбранный режим, а также текущее состояние вывода.

Мы хотим, чтобы ESP8266 запомнил последнее состояние выхода и настройки, в случае, если он сбрасывается или внезапно теряет питание. Итак, нам нужно сохранить эти параметры в ESP8266 ESPROM.

Установка библиотек

Перед загрузкой кода вам необходимо установить две библиотеки в вашей среде IDE Arduino: библиотеку OneWire от Paul Stoffregen и библиотеку Dallas Temperature, чтобы можно было использовать датчик DS18B20. Выполните следующие шаги, чтобы установить эти библиотеки.

 

Код

Скопируйте следующий код в Arduino IDE.

/*********

*********/

// Load libraries
#include <ESP8266WiFi.h>
#include <EEPROM.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Auxiliary variables for temperature
static char celsiusTemp[7];
static char fahrenheitTemp[7];
String temperatureString = "";      // Variable to hold the temperature reading

// EEPROM size
// Address 0: Last output state (0 = off or 1 = on)
// Address 1: Selected mode (0 = Manual, 1 = Auto PIR,
// 2 = Auto LDR, or 3 = Auto PIR and LDR)
// Address 2: Timer (time 0 to 255 seconds)
// Address 3: LDR threshold value (luminosity in percentage 0 to 100%)
#define EEPROM_SIZE 4

// Set GPIOs for: output variable, status LED, PIR Motion Sensor, and LDR
const int output = 15;
const int statusLed = 12;
const int motionSensor = 5;
const int ldr = A0;
// Store the current output state
String outputState = "off";

// GPIO where the DS18B20 is connected to
const int oneWireBus = 4;          
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature sensors(&oneWire);

// Timers - Auxiliary variables
unsigned long now = millis();
unsigned long lastMeasure = 0;
boolean startTimer = false;
unsigned long currentTime = millis();
unsigned long previousTime = 0; 
const long timeoutTime = 2000;

// Auxiliary variables to store selected mode and settings 
int selectedMode = 0;
int timer = 0;
int ldrThreshold = 0;
int armMotion = 0;
int armLdr = 0;
String modes[4] = { "Manual", "Auto PIR", "Auto LDR", "Auto PIR and LDR" };

// Decode HTTP GET value
String valueString = "0";
int pos1 = 0;
int pos2 = 0;
// Variable to store the HTTP request
String header;
// Set web server port number to 80
WiFiServer server(80);

void setup() {
  // Start the DS18B20 sensor
  sensors.begin();

  // Serial port for debugging purposes
  Serial.begin(115200);

  // PIR Motion Sensor mode, then set interrupt function and RISING mode
  pinMode(motionSensor, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
  
  Serial.println("start...");
  EEPROM.begin(EEPROM_SIZE);
  
  // Uncomment the next lines to test the values stored in the flash memory
  /*Serial.println(" bytes read from Flash . Values are:");
  for(int i = 0; i < EEPROM_SIZE; i++) {
    Serial.print(byte(EEPROM.read(i))); 
    Serial.print(" ");
  }*/
  
  // Initialize the output variable and the LED as OUTPUTs
  pinMode(output, OUTPUT);
  pinMode(statusLed, OUTPUT);
  digitalWrite(output, HIGH);
  digitalWrite(statusLed, LOW);
  // Read from flash memory on start and store the values in auxiliary variables
  // Set output to last state (saved in the flash memory)
  if(!EEPROM.read(0)) {
    outputState = "off";
    digitalWrite(output, HIGH);
  }
  else {
    outputState = "on";
    digitalWrite(output, LOW);
  }
  selectedMode = EEPROM.read(1);
  timer = EEPROM.read(2);
  ldrThreshold = EEPROM.read(3);
  configureMode();
  
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop() {
  WiFiClient client = server.available();   // Listen for incoming clients
  if (client) {                             // If a new client connects,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected() && currentTime - previousTime <= timeoutTime) {            // loop while the client's connected
      currentTime = millis();
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();                     
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #555555;}</style></head>");
            
            // Request example: GET /?mode=0& HTTP/1.1 - sets mode to Manual (0)
            if(header.indexOf("GET /?mode=") >= 0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              selectedMode = valueString.toInt();
              EEPROM.write(1, selectedMode);
              EEPROM.commit();
              configureMode();
            }
            // Change the output state - turn GPIOs on and off
            else if(header.indexOf("GET /?state=on") >= 0) {
              outputOn();
            } 
            else if(header.indexOf("GET /?state=off") >= 0) {
              outputOff();
            }
            // Set timer value
            else if(header.indexOf("GET /?timer=") >= 0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              timer = valueString.toInt();
              EEPROM.write(2, timer);
              EEPROM.commit();
              Serial.println(valueString);
            }
            // Set LDR Threshold value
            else if(header.indexOf("GET /?ldrthreshold=") >= 0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              ldrThreshold = valueString.toInt();
              EEPROM.write(3, ldrThreshold);
              EEPROM.commit();
              Serial.println(valueString);
            }
            
            // Web Page Heading
            client.println("<body><h1>ESP8266 Web Server</h1>");
            // Drop down menu to select mode
            client.println("<p><strong>Mode selected:</strong> " + modes[selectedMode] + "</p>");
            client.println("<select id=\"mySelect\" onchange=\"setMode(this.value)\">");
            client.println("<option>Change mode");
            client.println("<option value=\"0\">Manual");
            client.println("<option value=\"1\">Auto PIR");
            client.println("<option value=\"2\">Auto LDR");
            client.println("<option value=\"3\">Auto PIR and LDR</select>");
          
            // Display current state, and ON/OFF buttons for output 
            client.println("<p>GPIO - State " + outputState + "</p>");
            // If the output is off, it displays the ON button       
            if(selectedMode == 0) {
              if(outputState == "off") {
                client.println("<p><button class=\"button\" onclick=\"outputOn()\">ON</button></p>");
              } 
              else {
                client.println("<p><button class=\"button button2\" onclick=\"outputOff()\">OFF</button></p>");
              }
            }
            else if(selectedMode == 1) {
              client.println("<p>Timer (0 and 255 in seconds): <input type=\"number\" name=\"txt\" value=\"" + 
                              String(EEPROM.read(2)) + "\" onchange=\"setTimer(this.value)\" min=\"0\" max=\"255\"></p>");
            }
            else if(selectedMode == 2) {
              client.println("<p>LDR Threshold (0 and 100%): <input type=\"number\" name=\"txt\" value=\"" + 
                              String(EEPROM.read(3)) + "\" onchange=\"setThreshold(this.value)\" min=\"0\" max=\"100\"></p>");
            }
            else if(selectedMode == 3) {
              client.println("<p>Timer (0 and 255 in seconds): <input type=\"number\" name=\"txt\" value=\"" + 
                               String(EEPROM.read(2)) + "\" onchange=\"setTimer(this.value)\" min=\"0\" max=\"255\"></p>");
              client.println("<p>LDR Threshold (0 and 100%): <input type=\"number\" name=\"txt\" value=\"" + 
                               String(EEPROM.read(3)) + "\" onchange=\"setThreshold(this.value)\" min=\"0\" max=\"100\"></p>");            
            }
            // Get and display DHT sensor readings
            if(header.indexOf("GET /?sensor") >= 0) {
              sensors.requestTemperatures(); 
              temperatureString = " " + String(sensors.getTempCByIndex(0)) + "C  " +  
                        String(sensors.getTempFByIndex(0)) + "F";   
             
              client.println("<p>");
              client.println(temperatureString);
              client.println("</p>");
              client.println("<p><a href=\"/\"><button>Remove Sensor Readings</button></a></p>");
            }
            else {
              client.println("<p><a href=\"?sensor\"><button>View Sensor Readings</button></a></p>");
            }
            client.println("<script> function setMode(value) { var xhr = new XMLHttpRequest();"); 
            client.println("xhr.open('GET', \"/?mode=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function setTimer(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?timer=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function setThreshold(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?ldrthreshold=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function outputOn() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?state=on\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function outputOff() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?state=off\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function updateSensorReadings() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?sensor\", true);"); 
            client.println("xhr.send(); location.reload(true); }</script></body></html>");
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
  }
  
  // Starts a timer to turn on/off the output according to the time value or LDR reading
  now = millis();
  
  // Mode selected (1): Auto PIR
  if(startTimer && armMotion && !armLdr) {
    if(outputState == "off") {
      outputOn();
    }
    else if((now - lastMeasure > (timer * 1000))) {
      outputOff();
      startTimer = false;     
    }
  }  
  
  // Mode selected (2): Auto LDR
  // Read current LDR value and turn the output accordingly
  if(armLdr && !armMotion) {
    int ldrValue = map(analogRead(ldr), 0, 1023, 0, 100); 
    Serial.println(ldrValue);
    if(ldrValue < ldrThreshold && outputState == "on") {
      outputOff();
    }
    else if(ldrValue > ldrThreshold && outputState == "off") {
      outputOn();
    }
    delay(100);
  }
  
  // Mode selected (3): Auto PIR and LDR
  if(startTimer && armMotion && armLdr) {
    int ldrValue = map(analogRead(ldr), 0, 1023, 0, 100);
    Serial.println(ldrValue);
    if(ldrValue < ldrThreshold) {
      outputOff();
      startTimer = false;
      Serial.println("a");
    }
    else if(ldrValue > ldrThreshold && outputState == "off") {
      outputOn();
      Serial.println("b");
    }
    else if(now - lastMeasure > (timer * 1000)) {
      outputOff();
      startTimer = false;
      Serial.println("c");
    }
  }
}

// Checks if motion was detected and the sensors are armed. Then, starts a timer.
void detectsMovement() {
  if(armMotion || (armMotion && armLdr)) {
    Serial.println("MOTION DETECTED!!!");
    startTimer = true;
    lastMeasure = millis();
  }  
}
void configureMode() {
  // Mode: Manual
  if(selectedMode == 0) {
    armMotion = 0;
    armLdr = 0;
    // Status LED: off
    digitalWrite(statusLed, LOW);
  }
  // Mode: Auto PIR
  else if(selectedMode == 1) {
    outputOff();
    armMotion = 1;
    armLdr = 0;
    // Status LED: on
    digitalWrite(statusLed, HIGH);
  }
  // Mode: Auto LDR
  else if(selectedMode == 2) {
    armMotion = 0;
    armLdr = 1;
    // Status LED: on    
    digitalWrite(statusLed, HIGH);
  }
  // Mode: Auto PIR and LDR
  else if(selectedMode == 3) {
    outputOff();
    armMotion = 1;
    armLdr = 1;
    // Status LED: on
    digitalWrite(statusLed, HIGH);
  }
}

// Change output pin to on or off
void outputOn() {
  Serial.println("GPIO on");
  outputState = "on";
  digitalWrite(output, LOW);
  EEPROM.write(0, 1);
  EEPROM.commit();
}
void outputOff() { 
  Serial.println("GPIO off");
  outputState = "off";
  digitalWrite(output, HIGH);
  EEPROM.write(0, 0);
  EEPROM.commit();
}
Для начала вы можете просто заменить следующие две переменные на свои сетевые учетные данные.
const char* ssid = "";
const char* password = "";

 

Как работает код

Подключение библиотек.

#include <ESP8266WiFi.h>
#include <EEPROM.h>
#include <OneWire.h>
#include <DallasTemperature.h>

Библиотека ESP8266WiFi необходима для использования возможностей ESP Wi-Fi. Библиотека EEPROM позволяет считывать и записывать постоянные данные в ESPROM ESP8266, а библиотеки OneWire и DallasTemperam позволяют считывать температуру с датчика температуры DS18B20.

Это вспомогательные переменные для хранения температуры в градусах Цельсия / Фаренгейта:

// Auxiliary variables for temperature and humidity
static char celsiusTemp[7];
static char fahrenheitTemp[7];
static char humidityTemp[7];
String temperatureString = ""; // Variable to hold the temperature reading

EEPROM

Затем мы определяем размер EEPROM, к которому мы хотим получить доступ.

#define EEPROM_SIZE 4

Итак, нам нужно 4 байта во флэш-памяти.

  • Адрес 0: последнее состояние выхода (0 = выключено или 1 = включено)
  • Адрес 1: выбранный режим (0 = ручной, 1 = автоматический PIR, 2 = автоматический LDR или 3 = автоматический PIR и LDR)
  • Адрес 2: Таймер (время от 0 до 255 секунд)
  • Адрес 3: пороговое значение LDR (яркость в процентах от 0 до 100%)

Определение GPIO

В этом разделе мы определяем GPIO для выхода, светодиодный индикатор состояния, PIR-датчик движения и LDR.

// Auxiliary variables for temperature and humidity
static char celsiusTemp[7];
static char fahrenheitTemp[7];
static char humidityTemp[7];

Мы также создаем переменную String для хранения outputState, который будет отображаться на веб-сервере.

String outputState = "off";

Датчик температуры

Затем создайте экземпляры, необходимые для датчика температуры. Датчик температуры подключен к GPIO 4.

// GPIO where the DS18B20 is connected to
const int oneWireBus = 4; 
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature sensors(&oneWire);

Таймеры

Далее мы создаем вспомогательные переменные для таймеров:

long now = millis();
long lastMeasure = 0;
boolean startTimer = false;

Выбранный режим и настройки

Здесь мы инициализируем переменные для сохранения выбранного режима и настроек:

int selectedMode = 0;
int timer = 0;
int ldrThreshold = 0;
int armMotion = 0;
int armLdr = 0;
String modes[4] = { "Manual", "Auto PIR", "Auto LDR", "Auto PIR and LDR" };

Установка переменных для веб-сервера

Следующий фрагмент кода связан с веб-сервером.

// Decode HTTP GET value
String valueString = "0";
int pos1 = 0;
int pos2 = 0;
// Variable to store the HTTP request
String header;
// Set web server port number to 80
WiFiServer server(80);

setup()

В setup() начните с инициализации датчика температуры DS18B20.

sensors.begin();

Инициализируйте последовательный порт со скоростью 115200 бод для целей отладки.

Serial.begin(115200);

Прерывание

Установите датчик движения PIR как INPUT_PULLUP и определите его как прерывание в режиме RISING.

pinMode(motionSensor, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

Флэш-память

Эта часть кода инициализирует флэш-память с размером EEPROM, определенным ранее.

EEPROM.begin(EEPROM_SIZE);

Установите светодиод состояния и выходной контакт в качестве выходов.

pinMode(output, OUTPUT);
pinMode(statusLed, OUTPUT);

Мы контролируем реле с инвертированной логикой, поэтому мы начнем с того, что установим его в HIGH состояние

digitalWrite(output, HIGH);
digitalWrite(statusLed, HIGH);

Затем установите выход в последнее сохраненное состояние. Выходное состояние сохраняется в позиции 0, поэтому используйте EEPROM.read (0).

Мы проверяем, включено или выключено сохраненное состояние, чтобы соответствующим образом обновить состояние вывода.

if(!EEPROM.read(0)) {
  outputState = "off";
  digitalWrite(output, HIGH);
}
else {
  outputState = "on";
  digitalWrite(output, LOW);
}

Мы также обновляем все переменные, в которых хранятся настройки, значениями, сохраненными в EEPROM, такими как выбранный режим, таймер и пороговое значение LDR.

selectedMode = EEPROM.read(1);
timer = EEPROM.read(2);
ldrThreshold = EEPROM.read(3);

Затем мы вызываем функцию configureMode (), чтобы назначить правильные значения каждому режиму.

configureMode();

функция configureMode ()

Давайте посмотрим, как работает эта функция. Если выбран режим «Вручную», движение не активируется (armMotion), ни LDR (armLdr). Мы также установили индикатор состояния на LOW, чтобы показать, что мы находимся в ручном режиме (в автоматическом режиме индикатор состояния включен).

if(selectedMode == 0) {
  armMotion = 0;
  armLdr = 0;
  // Status LED: off
  digitalWrite(statusLed, LOW);
}

Аналогичный процесс выполняется для настройки других режимов.

Wi-Fi соединение

Здесь мы подключаемся к сети Wi-Fi и печатаем IP-адрес ESP8266 в последовательном мониторе.

Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();

loop()

Эту часть кода легче понять, если мы объясним, что происходит при выполнении кода.

При доступе к веб-серверу вы увидите похожую веб-страницу.

В верхней части вы можете выбрать один из этих четырех различных режимов.

  • Ручной (режим 0)
  • Авто PIR (режим 1)
  • Авто LDR (режим 2)
  • Авто PIR и LDR (режим 3)

Ручной режим

Например, если вы выбрали ручной режим, выполняется следующая часть кода.

if(header.indexOf("GET /?mode=") >= 0) {
  pos1 = header.indexOf('=');
  pos2 = header.indexOf('&');
  valueString = header.substring(pos1+1, pos2);
  selectedMode = valueString.toInt();
  EEPROM.write(1, selectedMode);
  EEPROM.commit();
  configureMode();
}

Сохраняет выбранный режим в переменной selectedMode и сохраняет его во флэш-памяти с помощью:

EEPROM.write(1, selectedMode);

Внешний вид веб-страницы меняется в соответствии с выбранным режимом. В этом случае, поскольку мы выбрали ручной режим, который соответствует 0, следующий оператор if верен, и на веб-странице появятся две кнопки для управления выводом.

if(selectedMode == 0) {
  if(outputState == "off") {
    client.println("<p><button class=\"button\" onclick=\"outputOn()\">ON</button></p>");
  } 
  else {
    client.println("<p><button class=\"button button2\" onclick=\"outputOff()\">OFF</button></p>");
  }
}

Когда вы нажимаете кнопки включения и выключения, запускается следующий код и один из этих двух операторов if, который включает или выключает вывод.

// Change the output state - turn GPIOs on and off
else if(header.indexOf("GET /?state=on") >= 0) {
  outputOn();
} 
else if(header.indexOf("GET /?state=off") >= 0) {
  outputOff();
}

Автоматический режим PIR

Теперь в выпадающем меню выберите режим Auto PIR.

На веб-странице отображается новое поле ввода. Это поле позволяет вам ввести число от 0 до 255, чтобы указать количество секунд, в течение которых выход должен оставаться включенным после обнаружения движения.

Когда вы меняете номер, он вызывает следующую часть кода и изменяет переменную таймера.

else if(header.indexOf("GET /?timer=") >= 0) {
  pos1 = header.indexOf('=');
  pos2 = header.indexOf('&');
  valueString = header.substring(pos1+1, pos2);
  timer = valueString.toInt();
  EEPROM.write(2, timer);
  EEPROM.commit();
  Serial.println(valueString);
}

В этом режиме (режим 1) отображается только поле ввода для таймера.

else if(selectedMode == 1) {
  client.println("<p>Timer (0 and 255 in seconds): <input type=\"number\" name=\"txt\" value=\"" + 
  String(EEPROM.read(2)) + "\" onchange=\"setTimer(this.value)\" min=\"0\" max=\"255\"></p>");
}

Автоматический режим LDR

Выберите режим Auto LDR, и появится новое поле ввода.

Это устанавливает пороговое значение LDR, и вы можете ввести число от 0 до 100, чтобы указать% яркости. Когда вы изменяете это поле, он вызывает следующую часть кода, чтобы обновить пороговое значение LDR:

else if(header.indexOf("GET /?ldrthreshold=") >= 0) {
  pos1 = header.indexOf('=');
  pos2 = header.indexOf('&');
  valueString = header.substring(pos1+1, pos2);
  ldrThreshold = valueString.toInt();
  EEPROM.write(3, ldrThreshold);
  EEPROM.commit();
  Serial.println(valueString);
}

Это режим 2, и он отобразит поле ввода порогового значения ldr.

else if(selectedMode == 2) {
  client.println("<p>LDR Threshold (0 and 100%): <input type=\"number\" name=\"txt\" value=\"" + 
  String(EEPROM.read(3)) + "\" onchange=\"setThreshold(this.value)\" min=\"0\" max=\"100\"></p>");
}

Автоматический режим PIR и LDR

Выбор режимов Auto PIR и LDR активирует как PIR, так и LDR. Он также загружает новую веб-страницу с двумя полями ввода.

Оба поля ввода работают так же, как мы описали ранее.

Показания датчика

Наконец, есть кнопка для запроса и отображения показаний температуры.

if(header.indexOf("GET /?sensor") >= 0) {
  sensors.requestTemperatures(); 
  temperatureString = " " + String(sensors.getTempCByIndex(0)) + "C " + 
  String(sensors.getTempFByIndex(0)) + "F"; 

  client.println("<p>");
  client.println(temperatureString);
  client.println("</p>");

Есть также кнопка, которую вы можете нажать, чтобы удалить эти показания.

client.println("<p><a href=\"/\"><button>Remove Sensor Readings</button></a></p>");

Вот как вы настраиваете настройки вашего мультисенсора. Затем, в соответствии с выбранным режимом и настройками, выполняется другая часть цикла (), чтобы проверить, должен ли выход быть включен или выключен.

Управление выходным состоянием

Например, при обнаружении движения вызывается функция detectsMovement (), которая запускает таймер.

void detectsMovement() {
  if(armMotion || (armMotion && armLdr)) {
    Serial.println("MOTION DETECTED!!!");
    startTimer = true;
    lastMeasure = millis();
  } 
}

Затем, в зависимости от истекшего времени, он включает или выключает выход.

// Mode selected (1): Auto PIR
if(startTimer && armMotion && !armLdr) {
  if(outputState == "off") {
    outputOn();
  }
  else if((now - lastMeasure > (timer * 1000))) {
    outputOff();
    startTimer = false; 
  }
}

Существует также следующий раздел кода для включения или выключения выхода в соответствии с пороговым значением освещенности.

// Mode selected (2): Auto LDR
// Read current LDR value and turn the output accordingly
if(armLdr && !armMotion) {
  int ldrValue = map(analogRead(ldr), 0, 1024, 0, 100); 
  //Serial.println(ldrValue);
  if(ldrValue > ldrThreshold && outputState == "on") {
    outputOff();
  }
  else if(ldrValue < ldrThreshold && outputState == "off") {
    outputOn();
  }
  delay(100);
}

Наконец, следующий фрагмент кода запускается, когда выбран автоматический режим PIR и LDR и обнаружено движение.

// Mode selected (3): Auto PIR and LDR
if(startTimer && armMotion && armLdr) {
  int ldrValue = map(analogRead(ldr), 0, 4095, 0, 100);
  //Serial.println(ldrValue);
  if(ldrValue > ldrThreshold) {
    outputOff();
    startTimer = false;
  }
  else if(ldrValue < ldrThreshold && outputState == "off") {
    outputOn();
  }
  else if(now - lastMeasure > (timer * 1000)) {
    outputOff();
    startTimer = false;
  }
}

Загрузка кода

Нажмите кнопку загрузки, чтобы загрузить код на ESP8266. Убедитесь, что вы выбрали правильную плату и COM-порт.

Тестирование мультисенсорного шилда

Откройте последовательный монитор со скоростью 112500 бод. Нажмите кнопку включения ESP8266, чтобы напечатать IP-адрес ESP.

Откройте браузер и введите IP-адрес ESP8266. Следующая страница должна загрузиться.

Мы подключили релейный модуль к выходной клеммной колодке, поэтому мы управляем лампой 12 В, но вы можете управлять любым выходным сигналом по вашему желанию.

Теперь выберите каждый режим, попробуйте установить разные настройки, чтобы проверить, все ли работает правильно.

Например, выберите Ручной режим и включите и выключите лампу.

Выберите режим Auto PIR. В этом режиме лампа включается на количество секунд, которое вы установили при обнаружении движения.

В режиме LDR вы можете установить пороговое значение, при котором лампа загорится. Когда я закрываю LDR, яркость падает ниже порога, и лампа загорается.

В режимах Auto PIR и LDR я могу установить таймер и порог LDR.

Если движение обнаружено, но интенсивность света выше порога, ничего не происходит. Но если я закрываю LDR, что означает отсутствие света и обнаружение движения, лампа включается на количество секунд, которое я определил в настройках.

Вы также можете нажать кнопку «Просмотр показаний датчика», чтобы запросить последние показания температуры.

Добавить комментарий

Restricted HTML

  • Допустимые HTML-теги: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Строки и абзацы переносятся автоматически.
  • Адреса веб-страниц и email-адреса преобразовываются в ссылки автоматически.