Всё готово для главного: соберём станцию мониторинга целиком. Сегодня — датчиковая часть: два датчика, скользящее окно и дашборд. На следующем занятии добавим пороговую сигнализацию и защитишь готовую станцию.

После урока сможешь: собрать станцию из двух датчиков (температура/влажность и освещённость), вести скользящее окно температуры, выводить структурированный дашборд. Это первая половина итогового проекта.
Второй датчик: освещённость (LDR)
Добавь в Wokwi фоторезистор (LDR) — компонент, меняющий сопротивление в зависимости от света. Подключи его по схеме делителя напряжения: один конец LDR к 5V, второй — к A0 И одновременно к резистору 10 кОм, второй конец резистора — к GND. Без резистора-делителя изменение сопротивления LDR не превратится в измеримое изменение НАПРЯЖЕНИЯ на A0 — считывать было бы нечего. Дальше всё как с потенциометром: analogRead(A0) и map().

const int PIN_LDR = A0;
int svetSyroe = analogRead(PIN_LDR);
int svetProcent = map(svetSyroe, 0, 1023, 0, 100);
Честно: без калибровки под конкретный LDR это приближённый «процент освещённости», не точный люкс-метр — для учебной станции достаточно, но не выдавай его за точное измерение освещённости в реальном приборе.
Собираем датчиковую часть целиком
Соедини всё, что уже умеешь: DHT22 на пине 4, LDR на A0, скользящее окно из 5 последних температур (занятие 7), функции vyvestiPokazanie и sredneeMassiva (занятие 8), неблокирующий интервал 2000 мс (занятие 5, ограничение по датчику DHT22 из занятия 6).
#include <DHT.h>
#define PIN_DHT 4
DHT dht(PIN_DHT, DHT22);
const int PIN_LDR = A0;
const int RAZMER_OKNA = 5;
float oknoTemperatury[RAZMER_OKNA];
int indeksOkna = 0;
unsigned long previousMillis = 0;
const long interval = 2000;
void vyvestiPokazanie(String nazvanie, float znachenie, String edinitsa) {
Serial.print(nazvanie);
Serial.print(": ");
Serial.print(znachenie);
Serial.println(edinitsa);
}
float sredneeMassiva(float arr[], int razmer) {
float summa = 0;
for (int i = 0; i < razmer; i = i + 1) {
summa = summa + arr[i];
}
return summa / razmer;
}
void setup() {
Serial.begin(9600);
dht.begin();
for (int i = 0; i < RAZMER_OKNA; i = i + 1) {
oknoTemperatury[i] = 0;
}
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
float temperatura = dht.readTemperature();
if (!isnan(temperatura)) {
oknoTemperatury[indeksOkna] = temperatura;
indeksOkna = (indeksOkna + 1) % RAZMER_OKNA;
}
float srednyayaTemperatura = sredneeMassiva(oknoTemperatury, RAZMER_OKNA);
int svetSyroe = analogRead(PIN_LDR);
int svetProcent = map(svetSyroe, 0, 1023, 0, 100);
vyvestiPokazanie("Температура", temperatura, " C");
vyvestiPokazanie("Средняя температура", srednyayaTemperatura, " C");
vyvestiPokazanie("Освещённость", svetProcent, " %");
Serial.println("----------");
}
}
Датчиковая часть станции — что откуда: DHT22 (пин 4) --> dht.readTemperature() --> окно (5) --> sredneeMassiva() LDR (A0) --> analogRead() --> map() --> процент освещённости Всё собирается в дашборд через vyvestiPokazanie(), раз в 2000 мс (millis)
Миссия: собери и проверь датчиковую часть
Собери схему в Wokwi (DHT22 на пине 4, LDR с резистором-делителем на A0), вставь код выше, запусти и понаблюдай за дашбордом минимум 15-20 секунд (окно должно заполниться новыми показаниями). На следующем занятии добавим сигнализацию.
Проверь себя
Контрольный вопрос. Почему температуру и влажность (DHT22) читаем не чаще раз в 2000 мс, а освещённость (LDR) можно читать на каждом проходе loop()?
Подсказка: Вспомни занятие про DHT22 — там разбиралось именно это ограничение.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Зачем в один дашборд выводить и температуру, и среднюю температуру, и освещённость сразу?
Подсказка: Подумай, зачем инженеру дашборд, а не отдельные разрозненные показания.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. analogRead(A0) для LDR вернул 511 (примерно половина диапазона). Что выведет map(511, 0, 1023, 0, 100)? Впиши число.
Подсказка: 511 — примерно половина от 1023. Половина выходного диапазона 0..100 — это сколько?
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Зачем в схеме с LDR нужен резистор-делитель (10 кОм между A0 и GND)?
Подсказка: Обратись к блоку «Второй датчик: освещённость (LDR)» — там объясняется назначение делителя.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. dht.readTemperature() провалился и вернул NaN на этом проходе. Что произойдёт с окном температур oknoTemperatury?
Исходный код для этого задания:
float temperatura = dht.readTemperature(); if (!isnan(temperatura)) { oknoTemperatury[indeksOkna] = temperatura; indeksOkna = (indeksOkna + 1) % RAZMER_OKNA; }Подсказка: Условие внутри if — !isnan(temperatura) — истинно, только когда чтение УСПЕШНО.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Задание. Напиши код сам. Допиши строку вместо ___, которая переводит сырое значение с LDR (0..1023) в проценты освещённости (0..100).
Исходный код для этого задания:
int svetSyroe = analogRead(PIN_LDR); int svetProcent = ___;Подсказка: Используй map() с теми же границами, что и в блоке «Второй датчик».
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Задание. Напиши код сам (3 строки). Внутри неблокирующего блока, после успешного чтения temperatura (уже проверено !isnan), запиши её в окно oknoTemperatury на позицию indeksOkna и сдвинь indeksOkna по кругу (используй RAZMER_OKNA).
Исходный код для этого задания:
float temperatura = dht.readTemperature(); if (!isnan(temperatura)) { // напиши 2 строки: запись в окно и сдвиг индекса по кругу } float srednyayaTemperatura = sredneeMassiva(oknoTemperatury, RAZMER_OKNA);Подсказка: Это тот же паттерн скользящего окна из занятия про массивы — запись по индексу, потом (indeksOkna + 1) % RAZMER_OKNA.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Представь гипотетическую ситуацию: в проекте завели ДВА независимых процесса с разными интервалами (например, LDR каждые 300 мс и дашборд каждые 2000 мс), но по ошибке использовали ОДНУ переменную previousMillis для обоих. Что произойдёт?
Подсказка: Вспомни занятие про millis(): у каждого независимого процесса должна быть СВОЯ переменная previousMillis.
Онлайн-проверка ответа появится позже
Задание. Мини-проект (часть датчикового ядра). Напиши функцию float minimumMassiva(float arr[], int razmer), которая находит наименьший элемент массива (по образцу maksimumMassiva из занятия про функции), и добавь в дашборд станции вызов vyvestiPokazanie(«Мин. температура», minimumMassiva(oknoTemperatury, RAZMER_OKNA), » C») сразу после строки со средней температурой. Принято, если: функция minimumMassiva находит именно наименьшее значение массива через цикл (без готовых функций min), и вызов добавлен в дашборд ПОСЛЕ строки «Средняя температура» с указанными аргументами.
Исходный код для этого задания:
// напиши функцию minimumMassiva и добавь вызов vyvestiPokazanie в дашборд по описанию вышеПодсказка: Функция — зеркало maksimumMassiva из занятия 8, только со знаком при сравнении; вызов — ещё одна строка vyvestiPokazanie сразу после существующей строки про среднюю температуру.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Задание. Мини-проект (часть датчикового ядра). По образцу minimumMassiva из предыдущего задания и maksimumMassiva из занятия про функции — добавь в дашборд станции строку с максимальной температурой по окну: вызов vyvestiPokazanie(«Макс. температура», maksimumMassiva(oknoTemperatury, RAZMER_OKNA), » C») сразу после строки с минимальной температурой. Принято, если: используется уже готовая (не переписанная заново) функция maksimumMassiva, и вызов добавлен в дашборд ПОСЛЕ строки «Мин. температура» с указанными аргументами.
Исходный код для этого задания:
// добавь вызов vyvestiPokazanie с maksimumMassiva в дашборд по описанию вышеПодсказка: Не нужно писать функцию заново — она уже есть с занятия про функции, нужен только новый вызов vyvestiPokazanie на новой строке дашборда.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Что дальше
Датчиковая часть готова: два датчика, скользящее окно, дашборд с минимумом/максимумом/средним. На следующем, последнем занятии добавим пороговую сигнализацию по этим показаниям и защитишь готовую станцию — объяснишь, как она работает.
Итог: у тебя собрана датчиковая часть станции — два датчика, скользящее окно, дашборд с агрегатами. Дальше — сигнализация и защита проекта.
