ESP32:Примеры/WiFi-мультидатчик на базе ESP32: температура, влажность, движение, яркость и управление реле

Материал из Онлайн справочника
Перейти к навигации Перейти к поиску

Перевод: Максим Кузьмин (Cubewriter) Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Pixel Art Mini Meow Animated.gif Черновик


Содержание

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

В этом примере мы покажем, как создать систему на базе ESP32, оснащенную несколькими датчиками и коммуницирующую через WiFi. Она будет состоять из PIR-датчика движения, фоторезистора, датчика температуры и влажности DHT22, реле и статусного RGB-светодиода.

WiFi multisensor ESP32.png

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

Чтобы лучше понимать некоторые концепты, используемые в этом проекте, рекомендуем ознакомиться с этими руководствами:

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

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

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

Статусный индикатор

Наша система также будет оснащена RGB-светодиодом, меняющим цвет в зависимости от выбранного режима:

Status LED.png
  • Ручной режим – красный цвет;
  • Режим PIR-датчика – зеленый цвет;
  • Режим фоторезистора – синий цвет;
  • Режим PIR-датчика и фоторезистора – фиолетовый цвет;

Данные от датчиков

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

Кроме того, эти данные можно будет убрать с экрана при помощи кнопки Remove Sensor Readings («Спрятать данные от датчика»), чтобы оптимизировать производительность веб-сервера.

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

Кроме того, для этого проекта нам нужно будет, чтобы ESP32 помнила последнее состояние выходного контакта и другие настройки – на случай сброса или внезапного отключения питания. Поэтому мы будем записывать все эти данные в flash-память ESP32.

Примечание: О том, как записывать данные в энергонезависимую flash-память ESP32, читайте в руководстве «Как запомнить последнее состояние контакта».

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

Перед загрузкой кода нам нужно установить в IDE Arduino две библиотеки – «DHT sensor» и «Adafruit Sensor». Они позволят без труда считывать данные с датчика DHT22.

Устанавливаем библиотеку «DHT sensor»

  1. Кликните тут, чтобы скачать ZIP-архив библиотеки. Он должен загрузиться в папку «Загрузки»;
  2. Распакуйте этот ZIP-архив. У вас должна получиться папка «DHT-sensor-library-master»;
  3. Переименуйте эту папку на «DHT»;
  4. Переместите папку «DHT» в папку «libraries» (туда устанавливаются все библиотеки IDE Arduino), которая находится внутри папки, куда установлена ваша IDE Arduino;
  5. Перезапустите IDE Arduino;

Устанавливаем библиотеку «Adafruit Sensor»

  1. Кликните тут, чтобы скачать ZIP-архив библиотеки. Он должен загрузиться в папку «Загрузки»;
  2. Распакуйте этот ZIP-архив. У вас должна получиться папка «Adafruit_Sensor-master»;
  3. Переименуйте эту папку на «Adafruit_Sensor»;
  4. Переместите папку «Adafruit_Sensor» в папку «libraries» (туда устанавливаются все библиотеки IDE Arduino), которая находится внутри папки, куда установлена ваша IDE Arduino;
  5. Перезапустите IDE Arduino.

Что нужно сделать перед загрузкой кода

Вставьте в двух строчках ниже SSID и пароль для своей WiFi-сети:

1 // вставьте внизу SSID и пароль для своей WiFi-сети:
2 const char* ssid = "REPLACE_WITH_YOUR_SSID";
3 const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Вставив SSID и пароль, загрузите этот код на ESP32 (перед этим убедитесь, что в IDE Arduino выбраны правильная плата и COM-порт).

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

Подключаем библиотеки

Начинаем с подключения необходимых библиотек.

1 #include <WiFi.h>
2 #include <EEPROM.h>
3 #include "DHT.h"
4 #include <Adafruit_Sensor.h>

Задаем учетные данные для подключения к WiFi-сети

В этих двух переменных необходимо задать SSID и пароль для своей WiFi-сети.

1 const char* ssid = "REPLACE_WITH_YOUR_SSID"; 
2 const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Задаем датчик DHT

В следующем фрагменте выбираем тип датчика DHT. В нашем случае выбран DHT22.

1 //#define DHTTYPE DHT11 // DHT 11 
2 //#define DHTTYPE DHT21 // DHT 21 (AM2301) 
3 #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321

Затем задаем контакт, к которому подключен датчик DHT, и создаем объект для него.

1 // задаем контакт, к которому подключен DHT-датчик:
2 const int DHTPin = 27;
3 // инициализируем DHT-датчик:
4 DHT dht(DHTPin, DHTTYPE);

Далее задаем вспомогательные переменные для хранения данных о температуре (в градусах Цельсия и Фаренгейта) и влажности.

1 static char celsiusTemp[7]; 
2 static char fahrenheitTemp[7]; 
3 static char humidityTemp[7];

Задаем размер flash-памяти

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

1 #define EEPROM_SIZE 4

Нам нужно будет сохранить в flash-память 4 значения: последнее состояние контакта для реле (адрес 0), выбранный режим (адрес 1), значение для таймера (адрес 2) и пороговое значение для фоторезистора (адрес 3). Таким образом, нам понадобится 4 байта flash-памяти.

  • Адрес 0: Последнее состояние контакта реле («0» = выкл, «1» = вкл);
  • Адрес 1: Выбранный режим («0» = Manual, «1» = Auto PIR, «2» = Auto LDR, «3» = Auto PIR and LDR);
  • Адрес 2: Таймер (время в секундах между «0» и «255»);
  • Адрес 3: Пороговое значение для фоторезистора (яркость в процентах между «0» и «100»).

Более подробно о flash-памяти ESP32 можно почитать в руководстве «Как запомнить последнее состояние контакта».

Задаем GPIO-контакты

В этом фрагменте мы задаем, какие GPIO-контакты будут использоваться для реле, RGB-светодиода, PIR-датчика движения и фоторезистора.

1 const int output = 2;
2 const int redRGB = 14;
3 const int greenRGB = 12;
4 const int blueRGB = 13;
5 const int motionSensor = 25;
6 const int ldr = 33;

Также создаем строковую переменную «outputState» для хранения значения контакта, к которому подключено реле. Это значение будет показано на веб-странице.

String outputState = "off";

Задаем таймеры

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

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

Примечание: Более подробно о прерываниях и таймерах в работе с ESP32 можно почитать в руководстве «Использование ESP32 вместе с PIR-датчиком движения».

Задаем переменные для режима и настроек

Во фрагменте ниже мы инициализируем переменные для хранения информации о выбранном режиме и настройках.

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

Задаем переменные для веб-сервера

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

1 // переменные для декодирования HTTP-запроса GET:
2 String valueString = "0";
3 int pos1 = 0;
4 int pos2 = 0;
5 // переменная для хранения HTTP-запроса:
6 String header;
7 // создаем объект сервера и задаем ему номер порта «80»:
8 WiFiServer server(80);

Более подробно о веб-серверах можно почитать в руководстве «Веб-сервер на базе ESP32: управление выходными контактами».

Блок setup()

В блоке setup() сначала инициализируем датчик DHT.

1 dht.begin();

Далее инициализируем последовательную коммуникацию на скорости 115200 бод (для отладочных целей).

Прерывания

Переключаем контакт, к которому подключен PIR-датчик движения, в режим «INPUT_PULLUP», а также задаем для этого контакта прерывание в режиме «RISING».

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

Flash-память

Эта часть кода инициализирует flash-память с размером, который мы задали ранее, а также считывает байты, хранящиеся в этой памяти.

 1   Serial.println("start...");  //  "запуск..."
 2   if(!EEPROM.begin(EEPROM_SIZE)) {
 3     Serial.println("failed to initialise EEPROM");
 4                //  "не удалось инициализировать EEPROM"
 5     delay(1000);
 6   }
 7   
 8   // раскомментируйте строчки ниже, чтобы проверить,
 9   // сохранились ли данные на flash-память:
10   Serial.println(" bytes read from Flash . Values are:");
11              //  " с flash-памяти считаны следующие данные:"
12   for(int i = 0; i < EEPROM_SIZE; i++) {
13     Serial.print(byte(EEPROM.read(i))); 
14     Serial.print(" ");
15   }

RGB-светодиод

В этом фрагменте мы делаем выходными (OUTPUT) контакты, к которым подключены реле и ножки RGB-светодиода:

1 pinMode(output, OUTPUT);
2 pinMode(redRGB, OUTPUT);
3 pinMode(greenRGB, OUTPUT);
4 pinMode(blueRGB, OUTPUT);

Считываем данные с flash-памяти

Далее считываем данные с flash-памяти и записываем в переменную «outputState» самое последнее сохраненное значение контакта, к которому подключено реле. Это значение хранится на адресе «0», поэтому для его чтения нужно воспользоваться функцией EEPROM.read(0).

То есть мы проверяем, что это за значение – «1» или «0» – чтобы потом записать в строковую переменную «outputState» значение «on» или «off». Это строковое значение будет затем показано на веб-странице в браузере.

1 if(!EEPROM.read(0)) {
2 outputState = "off";
3 digitalWrite(output, HIGH);
4 }
5 else {
6  outputState = "on";
7  digitalWrite(output, LOW);
8 }

Также обновляем переменные, где хранятся все остальные значения, записываемые на flash-память – данные о выбранном режиме, таймере и пороговом значении для фоторезистора.

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

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

1 configureMode();

Функция configureMode()

Давайте разберем, как работает эта функция.

К примеру, если пользователь выбрал ручной режим (Manual), у нас не будет работать ни PIR-датчик движения («armMotion»), ни фоторезистор («armLdr»). Но нам нужно переключить RGB-светодиод в красный цвет.

1 // ручной режим:
2 if(selectedMode == 0) {
3   armMotion = 0;
4   armLdr = 0;
5   // цвет RGB-светодиода - красный:
6   digitalWrite(redRGB, LOW);
7   digitalWrite(greenRGB, HIGH);
8   digitalWrite(blueRGB, HIGH);
9 }

Аналогичным образом задаются настройки и для других режимов. Мы меняем значения в переменных «armMotion» и «armLdr» для активации/деактивации PIR-датчика движения и фоторезистора, а также задаем значения для ножек RGB-светодиода, чтобы переключить его в цвет, соответствующий выбранному режиму.

Теперь давайте вернемся в блок setup().

Подключаемся к WiFi

В этом блоке мы подключаемся к WiFi-сети и печатаем в мониторе порта IP-адрес ESP32.

 1 // подключаемся к WiFi-сети при помощи заданных выше SSID и пароля: 
 2 Serial.print("Connecting to ");  //  "Подключаемся к "
 3 Serial.println(ssid);
 4 WiFi.begin(ssid, password);
 5 while (WiFi.status() != WL_CONNECTED) {
 6   delay(500);
 7   Serial.print(".");
 8 }
 9 // печатаем локальный IP-адрес и запускаем веб-сервер:
10 Serial.println("");
11 Serial.println("WiFi connected.");  //  "Подключились к WiFi."
12 Serial.println("IP address: ");  //  "IP-адрес: "
13 Serial.println(WiFi.localIP());
14 server.begin();

Блок loop()

В блоке loop() мы показываем в браузере страницу веб-сервера, а также выполняем действия, соответствующие выбранному режиму и его настройкам.

В выпадающем меню, где написано «Change mode» («Переключить режим»), можно выбрать один из следующих вариантов:

Manual (Ручной режим)

К примеру, если выбрать ручной режим (Manual), будет выполнен вот этот фрагмент кода:

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

Этот фрагмент сохраняет значение, соответствующее выбранному режиму, в переменную «selectedMode» и на flash-память в адрес «1» при помощи EEPROM.write(1, selectedMode);.

Внешний вид веб-страницы изменится согласно выбранному режиму. В нашем случае – то есть поскольку мы выбрали ручной режим (0) – правдивым будет условие, следующее ниже, и в результате на веб-странице будет показана одна из двух кнопок: «ON» или «OFF». С помощью этой кнопки мы можем поменять значение контакта, к которому подключено реле.

 1 if(selectedMode == 0) {
 2  if(outputState == "off") {
 3  client.println("<p><button class=\"button\" onclick=\"outputOn()\">ON<
 4 /button></p>");
 5  }
 6 else {
 7  client.println("<p><button class=\"button
 8 button2\"onclick=\"outputOff()\">OFF</button></p>");
 9 }
10 }

Когда вы кликаете на кнопки «ON» и «OFF», за дело берется код ниже. В нем два оператора else if() включают или выключают выходной контакт, к которому подключено реле.

1 else if(header.indexOf("GET /?state=on") >= 0) {
2  outputOn();
3 }
4 else if(header.indexOf("GET /?state=off") >= 0) {
5 outputOff();
6 }

Auto PIR (Режим PIR-датчика движения)

Теперь выберите при помощи выпадающего меню режим Auto PIR (Режим PIR-датчика).

На веб-странице появилось новое поле – «Timer (0 and 255 in seconds)». В него можно вписать целочисленное значение между «0» и «255», и это количество секунд, в течение которых реле должно оставаться включенным после обнаружения движения.

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

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

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

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

Auto LDR (Режим фоторезистора)

Выберите режим фоторезистора. На веб-странице должно появиться новое поле «LDR Threshold».

В этом поле задается пороговое значение яркости для фоторезистора – от «0» до «100» (в процентах). Когда вы меняете значение в этом поле, вызывается фрагмент кода ниже (он обновляет пороговое значение для фоторезистора).

 1 // задаем пороговое значение для фоторезистора:
 2 else if(header.indexOf("GET /?ldrthreshold=") >= 0) {
 3   pos1 = header.indexOf('=');
 4   pos2 = header.indexOf('&');
 5   valueString = header.substring(pos1+1, pos2);
 6   ldrThreshold = valueString.toInt();
 7   EEPROM.write(3, ldrThreshold);
 8   EEPROM.commit();
 9   Serial.println(valueString);
10 }

Это режим 2, поэтому нам нужно показать поле для ввода порогового значения фоторезистора.

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

Auto PIR and LDR (Режим PIR-датчика и фоторезистора)

Если выбрать этот режим, в системе активируются и PIR-датчик движения, и фоторезистор. Кроме того, в браузере загрузится новая веб-страница с двумя полями для ввода данных – «Timer» и «LDR Threshold».

Оба этих поля работают так же, как и в режимах Auto PIR и Auto LDR.

Данные от датчика температуры и влажности

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

 1 // считываем и показываем данные от датчика DHT:
 2 if(header.indexOf("GET /?sensor") >= 0) {
 3   // данные от датчика 
 4   // могут приходить с 2-секундной задержкой
 5   // (это очень медленный датчик):
 6   float h = dht.readHumidity();
 7   // считываем температуру
 8   // в градусах Цельсия (по умолчанию):
 9   float t = dht.readTemperature();
10   // считываем температуру
11   // в градусах Фаренгейта (если «isFahrenheit» = «true»):
12   float f = dht.readTemperature(true);
13   // проверяем, удалось ли прочесть данные с датчика,
14   // и если не удалось прочесть хоть какие-то из них,
15   // выходим, чтобы попробовать снова:
16   if (isnan (h) || isnan(t) || isnan(f)) {
17     Serial.println("Failed to read from DHT sensor!");
18                 // "Не удалось прочесть
19                 //  данные от датчика DHT!"
20     strcpy(celsiusTemp,"Failed");
21                    //  "Данные прочесть не удалось"
22     strcpy(fahrenheitTemp, "Failed");
23                    //  "Данные прочесть не удалось"
24     strcpy(humidityTemp, "Failed");  
25                    //  "Данные прочесть не удалось"     
26     }
27   else {
28     // рассчитываем температуру 
29     // (в Цельсиях и Фаренгейтах) и влажность:
30     float hic = dht.computeHeatIndex(t, h, false);       
31     dtostrf(hic, 6, 2, celsiusTemp);             
32     float hif = dht.computeHeatIndex(f, h);
33     dtostrf(hif, 6, 2, fahrenheitTemp);         
34     dtostrf(h, 6, 2, humidityTemp);
35   }
36   client.println("<p>");
37   client.println(celsiusTemp);
38   client.println("*C</p><p>");
39   client.println(fahrenheitTemp);
40   client.println("*F</p></div><p>");
41   client.println(humidityTemp);
42   client.println("%</p></div>");

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

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

Итак, эти фрагменты кода отвечают за установку режима и его настроек. После этого запускается другая часть блока loop(), которая, исходя из заданных режима и настроек, проверяет, что нужно сделать с устройством вывода данных (реле) – включить или выключить.

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

К примеру, при обнаружении движения будет вызвана функция detectsMovement(), которая напишет в монитор порта сообщение «MOTION DETECTED!!!» («ОБНАРУЖЕНО ДВИЖЕНИЕ!») и запустит таймер.

1 void detectsMovement() {
2   if(armMotion || (armMotion && armLdr)) {
3     Serial.println("MOTION DETECTED!!!");
4                //  "ОБНАРУЖЕНО ДВИЖЕНИЕ!!!"
5     startTimer = true;
6     lastMeasure = millis();
7   }  
8 }

Затем (в зависимости от того, сколько прошло времени) реле либо включится, либо выключится.

 1 // выбранный режим (1) – Режим PIR-датчика движения (Auto PIR):
 2   if(startTimer && armMotion && !armLdr) {
 3     if(outputState == "off") {
 4       outputOn();
 5     }
 6     else if((now - lastMeasure > (timer * 1000))) {
 7       outputOff();
 8       startTimer = false;     
 9     }
10   }

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

 1 // выбранный режим (2) – Режим фоторезистора (Auto LDR):
 2 // считываем текущее значение фоторезистора
 3 // и соответствующим образом переключаем контакт реле:
 4 if(armLdr && !armMotion) {
 5   int ldrValue = map(analogRead(ldr), 0, 4095, 0, 100); 
 6   //Serial.println(ldrValue);
 7   if(ldrValue > ldrThreshold && outputState == "on") {
 8     outputOff();
 9   }
10   else if(ldrValue < ldrThreshold && outputState == "off") {
11     outputOn();
12   }
13   delay(100);
14 }

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

 1 // выбранный режим (3) –
 2 // режим PIR-датчика движения и фоторезистора:
 3 if(startTimer && armMotion && armLdr) {
 4   int ldrValue = map(analogRead(ldr), 0, 4095, 0, 100);
 5   //Serial.println(ldrValue);
 6   if(ldrValue > ldrThreshold) {
 7     outputOff();
 8     startTimer = false;
 9   }
10   else if(ldrValue < ldrThreshold && outputState == "off") {
11     outputOn();
12   }
13   else if(now - lastMeasure > (timer * 1000)) {
14     outputOff();
15     startTimer = false;
16   }
17 }

Вот так и работает этот код. Кроме того, многие его фрагменты снабжены поясняющими комментариями – чтобы в нем было проще разобраться.

Проверяем WiFi-мультидатчик

Откройте монитор порта на скорости 115200 бод.

Нажмите на кнопку EN на ESP32, чтобы она напечатала в мониторе порта свой IP-адрес.

Затем откройте браузер и впишите этот IP-адрес в адресную строку. В результате должна загрузиться вот такая страница:

Esp32 wifi multisensor testing 1.PNG

Теперь попробуйте выбрать с помощью выпадающего меню разные режимы и задать разные настройки, чтобы проверить, правильно ли все работает. К примеру, выберите «Ручной» режим (Manual), чтобы включить/выключить лампу.

Esp32 wifi multisensor testing 2.PNG

Кликните на кнопку «View Sensor Readings» («Посмотреть данные от датчика»), чтобы в нижней части страницы появились самые последние данные от датчика температуры и влажности DHT.

Esp32 wifi multisensor testing 3.PNG

Выберите другие режимы и проверьте, меняется ли цвет RGB-светодиода. Также попробуйте задать собственные таймер и пороговое значение, чтобы проверить, как они работают.

Необходимое оборудование

Схема

Pixel Art Mini Meow Animated.gif На этой схеме изображена 36-контактная версия платы ESP32 DEVKIT DOIT V1. Если вы используете какую-то другую модель, обязательно сверьтесь с ее распиновкой.


Подключите эти компоненты друг к другу согласно схеме ниже. Подсоедините контакт данных PIR-датчика движения к контакту GPIO25, а фоторезистор – к GPIO33. Контакт данных датчика температуры и влажности DHT22 нужно подключить к GPIO27, а ножки RGB-светодиода – к контактам GPIO12, GPIO13 и GPIO14.

Кроме того, наш мультидатчик будет управлять устройством вывода данных – т.е. реле – подключенным к контакту GPIO2. С помощью этого реле мы будем управлять лампой, но вместо нее можно подключить и другое устройство.

Esp32 multi sensor wifi 1.PNG

Чтобы было понятнее, во время сборки цепи советуем сверяться с таблицами ниже.

Подключение PIR-датчика движения

PIR-датчик движения ESP32
VCC 3.3V (или VIN – в зависимости от модели вашего PIR-датчика)
Данные GPIO25
GND GND

Важно: PIR-датчик движения AM312, используемый в этом примере, работает на 3.3 вольтах. Но если вы, к примеру, используете PIR-датчик движения вроде HC-SR501, то он работает на 5 вольтах. И в этом случае вы можете либо модифицировать его, чтобы он работал на 3.3 вольтах, либо просто запитать его от контакта VIN.

Подключение RGB-светодиода

RGB-светодиод ESP32
Ножка для красного цвета GPIO14
Общий анод 3.3V
Ножка для зеленого цвета GPIO12
Ножка для синего цвета GPIO13

Подключение DHT22

DHT22 ESP32
Контакт 1 VIN (5 вольт)
Контакт 2 GPIO27 (через подтягивающий резистор на 10 кОм)
Контакт 3 Не нужно подключать
Контакт 4 GND

Подключение фоторезистора

Фоторезистор ESP32
Контакт 1 3.3V
Контакт 2 GPIO33 (через подтягивающий резистор на 10 кОм)

Подключение реле

Реле ESP32
VCC VIN (5V)
IN1 GPIO2
IN2 Не нужно подключать
GND GND
Pixel Art Mini Meow Animated.gif На этой схеме изображена 36-контактная версия платы ESP32 DEVKIT DOIT V1. Если вы используете какую-то другую модель, обязательно сверьтесь с ее распиновкой.


Создание корпуса

Этот корпус состоит из двух частей: дна и крышки. В крышке есть три круглых отверстия.

Esp32 wifi multi sensor Building an Enclosure 1.PNG

Одно из этих отверстий – для фоторезистора, второе – для PIR-датчика движения, а третье – для держателя RGB-светодиода. Кроме того, сбоку на крышке есть слот для датчика DHT, а также два прямоугольных выреза – для проводов реле и USB-кабеля (для питания ESP32).

Esp32 wifi multi sensor Building an Enclosure 2.PNG

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

Esp32 wifi multi sensor Building an Enclosure 3.PNG

Наконец, у дна и крышки есть по 4 отверстия для 4 скрепляющих винтов.

Esp32 wifi multi sensor Building an Enclosure 4.PNG

Если у вас есть 3D-принтер, вы можете сделать такой корпус сами. Необходимые STL-файлыSketchUp-файл) для его печати можно скачать по этой ссылке.

Примечание: Советуем увеличить размер корпуса – так внутри него будет проще уместить все компоненты.

Esp32 wifi multi sensor Building an Enclosure 5.PNG

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

Esp32 wifi multi sensor Building an Enclosure 6.PNG

Далее проект нужно подготовить к установке в корпус – отключить компоненты от контактной макетной платы и подключить их друг к другу при помощи одной большой печатной макетной платы и двух маленьких кусочков, отрезанных от печатной макетной платы. Большая печатная макетная плата понадобится для подключения ESP32, а два кусочка – для подключения PIR-датчика движения и DHT-датчика. К дешевым компонентам вроде фоторезистора и RGB-светодиода провода можно припаять напрямую.

Esp32 wifi multi sensor Building an Enclosure 7.PNG

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

Esp32 wifi multi sensor Building an Enclosure 8.PNG

Запитав ESP32 от USB-кабеля и подключив реле к лампе, закройте корпус и прикрепите крышку ко дну при помощи 4 шурупов.

Esp32 wifi multi sensor Building an Enclosure 9.PNG

Код

  1 /*********
  2   Руи Сантос
  3   Более подробно о проекте на: http://randomnerdtutorials.com  
  4 *********/
  5 
  6 // загружаем библиотеки:
  7 #include <WiFi.h>
  8 #include <EEPROM.h>
  9 #include "DHT.h"
 10 #include <Adafruit_Sensor.h>
 11 
 12 // вставьте в переменные ниже SSID и пароль для своей WiFi-сети:
 13 const char* ssid     = "REPLACE_WITH_YOUR_SSID";
 14 const char* password = "REPLACE_WITH_YOUR_PASSWORD";
 15 
 16 // три строчки ниже – для датчиков DHT11, DHT21 и DHT22;
 17 // вам нужно убрать знаки комментария у строчки с датчиком,
 18 // который вы используете, и две другие закомментировать:
 19 //#define DHTTYPE DHT11   // DHT 11
 20 //#define DHTTYPE DHT21   // DHT 21 (AM2301)
 21 #define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
 22 
 23 // задаем контакт, к которому подключен датчик DHT:
 24 const int DHTPin = 27;
 25 // инициализируем датчик DHT:
 26 DHT dht(DHTPin, DHTTYPE);
 27 
 28 // временные переменные для температуры и влажности:
 29 static char celsiusTemp[7];
 30 static char fahrenheitTemp[7];
 31 static char humidityTemp[7];
 32 
 33 // размер используемой flash-памяти;
 34 // адрес 0: последнее состояние контакта для реле (0 = выкл, 1 = вкл)
 35 // адрес 1: выбранный режим
 36 // (0 = ручной режим, 1 = режим PIR-датчика, 2 = режим фоторезистора,
 37 //  3 = режим PIR-датчика и фоторезистора)
 38 // адрес 2: таймер (время между 0 и 255 секундами)
 39 // адрес 3: пороговое значение для фоторезистора (между 0% и 100%)
 40 #define EEPROM_SIZE 4
 41 
 42 // задаем GPIO-контакты для
 43 // устройства вывода данных (реле), RGB-светодиода, 
 44 // PIR-датчика движения и фоторезистора:
 45 const int output = 2;
 46 const int redRGB = 14;
 47 const int greenRGB = 12;
 48 const int blueRGB = 13;
 49 const int motionSensor = 25;
 50 const int ldr = 33;
 51 // в этой строке будет храниться текущее состояние контакта для реле:
 52 String outputState = "off";
 53 
 54 // вспомогательные переменные для таймера:
 55 long now = millis();
 56 long lastMeasure = 0;
 57 boolean startTimer = false;
 58 
 59 // вспомогательные переменные
 60 // для хранения данных о выбранном режиме и настройках режимов:
 61 int selectedMode = 0;
 62 int timer = 0;
 63 int ldrThreshold = 0;
 64 int armMotion = 0;
 65 int armLdr = 0;
 66 String modes[4] = { "Manual", "Auto PIR", "Auto LDR", "Auto PIR and LDR" };
 67 
 68 // переменные для декодирования HTTP-запроса GET:
 69 String valueString = "0";
 70 int pos1 = 0;
 71 int pos2 = 0;
 72 // переменная для хранения HTTP-запроса:
 73 String header;
 74 // создаем объект сервера и задаем ему номер порта «80»:
 75 WiFiServer server(80);
 76 
 77 void setup() {
 78   // инициализируем датчик DHT:
 79   dht.begin();
 80 
 81   // запускаем последовательную коммуникацию (в отладочных целях): 
 82   Serial.begin(115200);
 83 
 84   // задаем режим «INPUT_PULLUP» для контакта,
 85   // к которому подключен PIR-датчик движения,
 86   // а затем задаем для этого контакта прерывание
 87   // и выставляем у него режим «RISING»:
 88   pinMode(motionSensor, INPUT_PULLUP);
 89   attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
 90   
 91   Serial.println("start...");  //  "запуск..."
 92   if(!EEPROM.begin(EEPROM_SIZE)) {
 93     Serial.println("failed to initialise EEPROM");
 94                //  "не удалось инициализировать EEPROM"
 95     delay(1000);
 96   }
 97   
 98   // раскомментируйте строчки ниже, чтобы проверить,
 99   // сохранились ли данные на flash-память:
100   Serial.println(" bytes read from Flash . Values are:");
101              //  " с flash-памяти считаны следующие данные:"
102   for(int i = 0; i < EEPROM_SIZE; i++) {
103     Serial.print(byte(EEPROM.read(i))); 
104     Serial.print(" ");
105   }
106   
107   // делаем контакты реле и RGB-светодиода выходными контактами: 
108   pinMode(output, OUTPUT);
109   pinMode(redRGB, OUTPUT);
110   pinMode(greenRGB, OUTPUT);
111   pinMode(blueRGB, OUTPUT);
112 
113   // считываем данные с flash-памяти,
114   // а затем сохраняем их во вспомогательные переменные
115 
116   // присваиваем контакту, к которому подключено реле, 
117   // самое последнее значение, хранящееся в flash-памяти:
118   if(!EEPROM.read(0)) {
119     outputState = "off";
120     digitalWrite(output, HIGH);
121   }
122   else {
123     outputState = "on";
124     digitalWrite(output, LOW);
125   }
126   selectedMode = EEPROM.read(1);
127   timer = EEPROM.read(2);
128   ldrThreshold = EEPROM.read(3);
129   configureMode();
130   
131   // подключаемся к WiFi-сети при помощи заданных выше SSID и пароля: 
132   Serial.print("Connecting to ");  //  "Подключаемся к "
133   Serial.println(ssid);
134   WiFi.begin(ssid, password);
135   while (WiFi.status() != WL_CONNECTED) {
136     delay(500);
137     Serial.print(".");
138   }
139   // печатаем локальный IP-адрес и запускаем веб-сервер:
140   Serial.println("");
141   Serial.println("WiFi connected.");  //  "Подключились к WiFi."
142   Serial.println("IP address: ");  //  "IP-адрес: "
143   Serial.println(WiFi.localIP());
144   server.begin();
145 }
146 
147 void loop() {
148   WiFiClient client = server.available();  // запускаем прослушку
149                                            // входящих клиентов;
150   if (client) {                            // при подключении
151                                            // нового клиента
152     Serial.println("New Client.");         // печатаем сообщение
153                                            // об этом в монитор порта;
154     String currentLine = "";               // делаем строку 
155                                            // для хранения данных,
156                                            // пришедших от клиента;                                           
157     while (client.connected()) {           // пока клиент подключен,
158       if (client.available()) {            // и если у клиента 
159                                            // есть байты, которые
160                                            // можно прочесть,
161         char c = client.read();            // считываем байт
162         Serial.write(c);                   // и печатаем его
163                                            // в мониторе порта
164         header += c;
165         if (c == '\n') {                   // если считанный байт – 
166                                            // это символ новой строки,
167           // если вам попалось два символа новой строки подряд,
168           // то это значит, что строка пуста;
169           // это конец HTTP-запроса клиента, поэтому пора слать ответ: 
170           if (currentLine.length() == 0) {
171             // в начале HTTP-заголовка всегда пишется код ответа
172             // (например, «HTTP/1.1 200 OK»), а также тип контента,
173             // чтобы клиент всегда знал, что получает;
174             // после этого пишем пустую линию:
175             client.println("HTTP/1.1 200 OK");
176             client.println("Content-type:text/html");
177             client.println("Connection: close");
178                        //  "Соединение: отключено"
179             client.println();                     
180             // показываем веб-страницу:
181             client.println("<!DOCTYPE html><html>");
182             client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
183             client.println("<link rel=\"icon\" href=\"data:,\">");
184             // используем CSS, чтобы задать стили
185             // для кнопок «ON» и «OFF»;
186             // не стесняйтесь экспериментировать с атрибутами
187             // «background-color» и «font-size»,
188             // чтобы дизайн этих кнопок  
189             // максимально соответствовал вашим предпочтениям:
190             client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
191             client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
192             client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
193             client.println(".button2 {background-color: #555555;}</style></head>");
194             
195             // пример запроса: 
196             // GET /?mode=0& HTTP/1.1 – он задает ручной режим (0):
197             if(header.indexOf("GET /?mode=") >= 0) {
198               pos1 = header.indexOf('=');
199               pos2 = header.indexOf('&');
200               valueString = header.substring(pos1+1, pos2);
201               selectedMode = valueString.toInt();
202               EEPROM.write(1, selectedMode);
203               EEPROM.commit();
204               configureMode();
205             }
206             // меняем значение GPIO-контакта,
207             // к которому подключено реле,
208             // включая и выключая его:
209             else if(header.indexOf("GET /?state=on") >= 0) {
210               outputOn();
211             } 
212             else if(header.indexOf("GET /?state=off") >= 0) {
213               outputOff();
214             }
215             // задаем таймер:
216             else if(header.indexOf("GET /?timer=") >= 0) {
217               pos1 = header.indexOf('=');
218               pos2 = header.indexOf('&');
219               valueString = header.substring(pos1+1, pos2);
220               timer = valueString.toInt();
221               EEPROM.write(2, timer);
222               EEPROM.commit();
223               Serial.println(valueString);
224             }
225             // задаем пороговое значение для фоторезистора:
226             else if(header.indexOf("GET /?ldrthreshold=") >= 0) {
227               pos1 = header.indexOf('=');
228               pos2 = header.indexOf('&');
229               valueString = header.substring(pos1+1, pos2);
230               ldrThreshold = valueString.toInt();
231               EEPROM.write(3, ldrThreshold);
232               EEPROM.commit();
233               Serial.println(valueString);
234             }
235             
236             // заголовок веб-страницы:
237             client.println("<body><h1>ESP32 Web Server</h1>");
238             // выпадающее меню для выбора режима:
239             client.println("<p><strong>Mode selected:</strong> " + modes[selectedMode] + "</p>");
240             client.println("<select id=\"mySelect\" onchange=\"setMode(this.value)\">");
241             client.println("<option>Change mode");
242             client.println("<option value=\"0\">Manual");
243             client.println("<option value=\"1\">Auto PIR");
244             client.println("<option value=\"2\">Auto LDR");
245             client.println("<option value=\"3\">Auto PIR and LDR</select>");
246           
247             // показываем текущее состояние контакта для реле,
248             // а также кнопки «ON» и «OFF» для управления им: 
249             client.println("<p>GPIO - State " + outputState + "</p>");
250             // если значение контакта для реле – это «выкл»,
251             // на экране будет нарисована кнопка «ON»:       
252             if(selectedMode == 0) {
253               if(outputState == "off") {
254                 client.println("<p><button class=\"button\" onclick=\"outputOn()\">ON</button></p>");
255               } 
256               else {
257                 client.println("<p><button class=\"button button2\" onclick=\"outputOff()\">OFF</button></p>");
258               }
259             }
260             else if(selectedMode == 1) {
261               client.println("<p>Timer (0 and 255 in seconds): <input type=\"number\" name=\"txt\" value=\"" + 
262                               String(EEPROM.read(2)) + "\" onchange=\"setTimer(this.value)\" min=\"0\" max=\"255\"></p>");
263             }
264             else if(selectedMode == 2) {
265               client.println("<p>LDR Threshold (0 and 100%): <input type=\"number\" name=\"txt\" value=\"" + 
266                               String(EEPROM.read(3)) + "\" onchange=\"setThreshold(this.value)\" min=\"0\" max=\"100\"></p>");
267             }
268             else if(selectedMode == 3) {
269               client.println("<p>Timer (0 and 255 in seconds): <input type=\"number\" name=\"txt\" value=\"" + 
270                                String(EEPROM.read(2)) + "\" onchange=\"setTimer(this.value)\" min=\"0\" max=\"255\"></p>");
271               client.println("<p>LDR Threshold (0 and 100%): <input type=\"number\" name=\"txt\" value=\"" + 
272                                String(EEPROM.read(3)) + "\" onchange=\"setThreshold(this.value)\" min=\"0\" max=\"100\"></p>");            
273             }
274             // считываем и показываем данные от датчика DHT:
275             if(header.indexOf("GET /?sensor") >= 0) {
276               // данные от датчика 
277               // могут приходить с 2-секундной задержкой
278               // (это очень медленный датчик):
279               float h = dht.readHumidity();
280               // считываем температуру
281               // в градусах Цельсия (по умолчанию):
282               float t = dht.readTemperature();
283               // считываем температуру
284               // в градусах Фаренгейта (если «isFahrenheit» = «true»):
285               float f = dht.readTemperature(true);
286               // проверяем, удалось ли прочесть данные с датчика,
287               // и если не удалось прочесть хоть какие-то из них,
288               // выходим, чтобы попробовать снова:
289               if (isnan (h) || isnan(t) || isnan(f)) {
290                 Serial.println("Failed to read from DHT sensor!");
291                             // "Не удалось прочесть
292                             //  данные от датчика DHT!"
293                 strcpy(celsiusTemp,"Failed");
294                                //  "Данные прочесть не удалось"
295                 strcpy(fahrenheitTemp, "Failed");
296                                //  "Данные прочесть не удалось"
297                 strcpy(humidityTemp, "Failed");  
298                                //  "Данные прочесть не удалось"     
299               }
300               else {
301                 // рассчитываем температуру 
302                 // (в Цельсиях и Фаренгейтах) и влажность:
303                 float hic = dht.computeHeatIndex(t, h, false);       
304                 dtostrf(hic, 6, 2, celsiusTemp);             
305                 float hif = dht.computeHeatIndex(f, h);
306                 dtostrf(hif, 6, 2, fahrenheitTemp);         
307                 dtostrf(h, 6, 2, humidityTemp);
308                 // функции Serial.print() ниже можно удалить
309                 // (они нужны лишь для отладочных целей):
310                 /*Serial.print("Humidity: "); Serial.print(h); Serial.print(" %\t Temperature: "); 
311                 Serial.print(t); Serial.print(" *C "); Serial.print(f); 
312                 Serial.print(" *F\t Heat index: "); Serial.print(hic); Serial.print(" *C "); 
313                 Serial.print(hif); Serial.print(" *F"); Serial.print("Humidity: "); 
314                 Serial.print(h); Serial.print(" %\t Temperature: "); Serial.print(t);
315                 Serial.print(" *C "); Serial.print(f); Serial.print(" *F\t Heat index: ");
316                 Serial.print(hic); Serial.print(" *C "); Serial.print(hif); Serial.println(" *F");*/
317               }
318               client.println("<p>");
319               client.println(celsiusTemp);
320               client.println("*C</p><p>");
321               client.println(fahrenheitTemp);
322               client.println("*F</p></div><p>");
323               client.println(humidityTemp);
324               client.println("%</p></div>");
325               client.println("<p><a href=\"/\"><button>Remove Sensor Readings</button></a></p>");
326             }
327             else {
328               client.println("<p><a href=\"?sensor\"><button>View Sensor Readings</button></a></p>");
329             }
330             client.println("<script> function setMode(value) { var xhr = new XMLHttpRequest();"); 
331             client.println("xhr.open('GET', \"/?mode=\" + value + \"&\", true);"); 
332             client.println("xhr.send(); location.reload(true); } ");
333             client.println("function setTimer(value) { var xhr = new XMLHttpRequest();");
334             client.println("xhr.open('GET', \"/?timer=\" + value + \"&\", true);"); 
335             client.println("xhr.send(); location.reload(true); } ");
336             client.println("function setThreshold(value) { var xhr = new XMLHttpRequest();");
337             client.println("xhr.open('GET', \"/?ldrthreshold=\" + value + \"&\", true);"); 
338             client.println("xhr.send(); location.reload(true); } ");
339             client.println("function outputOn() { var xhr = new XMLHttpRequest();");
340             client.println("xhr.open('GET', \"/?state=on\", true);"); 
341             client.println("xhr.send(); location.reload(true); } ");
342             client.println("function outputOff() { var xhr = new XMLHttpRequest();");
343             client.println("xhr.open('GET', \"/?state=off\", true);"); 
344             client.println("xhr.send(); location.reload(true); } ");
345             client.println("function updateSensorReadings() { var xhr = new XMLHttpRequest();");
346             client.println("xhr.open('GET', \"/?sensor\", true);"); 
347             client.println("xhr.send(); location.reload(true); }</script></body></html>");
348             // HTTP-ответ заканчивается еще одной пустой строкой:
349             client.println();
350             // выходим из цикла while():
351             break;
352           } else { // если получили символ новой строки,
353                    // очищаем переменную «currentLine»:
354             currentLine = "";
355           }
356         } else if (c != '\r') {  // если получили какие-нибудь данные
357                                  // (кроме символа возврата каретки),
358           currentLine += c;      // добавляем их в конец «currentLine» 
359         }
360       }
361     }
362     // очищаем переменную «header»:
363     header = "";
364     // отключаем соединение:
365     client.stop();
366     Serial.println("Client disconnected.");
367                //  "Клиент отключился."
368   }
369   
370   // запускаем таймер для включения/выключения контакта реле
371   // согласно заданному времени и/или значению от фоторезистора:
372   now = millis();
373   
374   // выбранный режим (1) – Режим PIR-датчика движения (Auto PIR):
375   if(startTimer && armMotion && !armLdr) {
376     if(outputState == "off") {
377       outputOn();
378     }
379     else if((now - lastMeasure > (timer * 1000))) {
380       outputOff();
381       startTimer = false;     
382     }
383   }  
384   
385   // выбранный режим (2) – Режим фоторезистора (Auto LDR):
386   // считываем текущее значение фоторезистора
387   // и соответствующим образом переключаем контакт реле:
388   if(armLdr && !armMotion) {
389     int ldrValue = map(analogRead(ldr), 0, 4095, 0, 100); 
390     //Serial.println(ldrValue);
391     if(ldrValue > ldrThreshold && outputState == "on") {
392       outputOff();
393     }
394     else if(ldrValue < ldrThreshold && outputState == "off") {
395       outputOn();
396     }
397     delay(100);
398   }
399   
400   // выбранный режим (3) –
401   // режим PIR-датчика движения и фоторезистора:
402   if(startTimer && armMotion && armLdr) {
403     int ldrValue = map(analogRead(ldr), 0, 4095, 0, 100);
404     //Serial.println(ldrValue);
405     if(ldrValue > ldrThreshold) {
406       outputOff();
407       startTimer = false;
408     }
409     else if(ldrValue < ldrThreshold && outputState == "off") {
410       outputOn();
411     }
412     else if(now - lastMeasure > (timer * 1000)) {
413       outputOff();
414       startTimer = false;
415     }
416   }
417 }
418 
419 // проверяем, обнаружены ли движение (с помощью PIR-датчика)
420 // и затемнение (с помощью фоторезистора), а затем запускаем таймер:
421 void detectsMovement() {
422   if(armMotion || (armMotion && armLdr)) {
423     Serial.println("MOTION DETECTED!!!");
424                //  "ОБНАРУЖЕНО ДВИЖЕНИЕ!!!"
425     startTimer = true;
426     lastMeasure = millis();
427   }  
428 }
429 void configureMode() {
430   // ручной режим:
431   if(selectedMode == 0) {
432     armMotion = 0;
433     armLdr = 0;
434     // цвет RGB-светодиода - красный:
435     digitalWrite(redRGB, LOW);
436     digitalWrite(greenRGB, HIGH);
437     digitalWrite(blueRGB, HIGH);
438   }
439   // режим PIR-датчика движения:
440   else if(selectedMode == 1) {
441     outputOff();
442     armMotion = 1;
443     armLdr = 0;
444     // цвет RGB-светодиода – зеленый:
445     digitalWrite(redRGB, HIGH);
446     digitalWrite(greenRGB, LOW);
447     digitalWrite(blueRGB, HIGH);
448   }
449   // режим фоторезистора:
450   else if(selectedMode == 2) {
451     armMotion = 0;
452     armLdr = 1;
453     // цвет RGB-светодиода – синий:    
454     digitalWrite(redRGB, HIGH);
455     digitalWrite(greenRGB, HIGH);
456     digitalWrite(blueRGB, LOW);
457   }
458   // режим PIR-датчика движения и фоторезистора:
459   else if(selectedMode == 3) {
460     outputOff();
461     armMotion = 1;
462     armLdr = 1;
463     // цвет RGB-светодиода – фиолетовый:    
464     digitalWrite(redRGB, LOW);
465     digitalWrite(greenRGB, HIGH);
466     digitalWrite(blueRGB, LOW);
467   }
468 }	
469 
470 // переключаем контакт реле с «on» («вкл») на «off» («выкл»):
471 void outputOn() {
472   Serial.println("GPIO on");
473   outputState = "on";
474   digitalWrite(output, LOW);
475   EEPROM.write(0, 1);
476   EEPROM.commit();
477 }
478 void outputOff() { 
479   Serial.println("GPIO off");
480   outputState = "off";
481   digitalWrite(output, HIGH);
482   EEPROM.write(0, 0);
483   EEPROM.commit();
484 }

См.также

Внешние ссылки