Одно показание датчика — случайность. Несколько показаний подряд — уже картина. Сегодня научим станцию копить показания и считать по ним среднее, находить минимум/максимум и замечать выброс — по одному показанию за раз, «на лету».

После урока сможешь: объявлять массив и перебирать его циклом, считать сумму/среднее/минимум/максимум, вести скользящее окно последних N показаний по кругу, находить выброс. В конце — станция со скользящим средним по потоку показаний.
Вспомни: в Python-курсе ты уже собирал «Анализ лога датчика» — список показаний, sum/min/max, фильтрация (занятие 5 первого курса). Массив в C++ — тот же список, но с фиксированным размером, заданным заранее, и индексация тоже начинается с 0.
Массив: набор значений под одним именем
Массив хранит несколько значений одного типа под одним именем, доступ — по индексу в квадратных скобках. Размер задаётся один раз и не меняется.
float pokazaniya[5] = {22.0, 23.0, 21.0, 24.0, 22.0};
Serial.println(pokazaniya[0]);
Serial.println(pokazaniya[2]);
22.00
21.00
Массив pokazaniya[5] — пять ячеек, индексы 0..4:
индекс: 0 1 2 3 4
+------+------+------+------+------+
значение: | 22.0 | 23.0 | 21.0 | 24.0 | 22.0 |
+------+------+------+------+------+
pokazaniya[0] -> 22.0 (первый элемент — индекс 0, не 1)
pokazaniya[4] -> 22.0 (последний элемент массива размером 5 — индекс 4)
Перебор циклом: сумма и среднее
Как и с списком в Python, массив перебирают циклом for по индексу от 0 до размера минус один.
float pokazaniya[5] = {22.0, 23.0, 21.0, 24.0, 22.0};
float summa = 0;
for (int i = 0; i < 5; i = i + 1) {
summa = summa + pokazaniya[i];
}
float srednee = summa / 5;
Serial.println(srednee);
22.40
Минимум и максимум без готовых функций
В C++ для массива нет готовых min()/max(), как sum()/min()/max() у списка в Python — их пишут циклом самостоятельно: берут первый элемент как «пока лучший» и сравнивают с остальными.
float pokazaniya[5] = {22.0, 23.0, 21.0, 24.0, 22.0};
float minimum = pokazaniya[0];
float maksimum = pokazaniya[0];
for (int i = 1; i < 5; i = i + 1) {
if (pokazaniya[i] < minimum) {
minimum = pokazaniya[i];
}
if (pokazaniya[i] > maksimum) {
maksimum = pokazaniya[i];
}
}
Скользящее окно: обновляем по кругу
Станция работает непрерывно — новые показания приходят постоянно, а массив фиксированного размера. Решение: скользящее окно — новое показание записывается на место САМОГО СТАРОГО, а индекс записи движется по кругу. Для этого используют остаток от деления %: как только индекс доходит до конца массива, % возвращает его в начало.
Представь еженедельник на 5 страниц — по числу рабочих дней. Когда наступает новый понедельник, ты не заводишь новую страницу: ты стираешь запись прошлого понедельника и пишешь поверх неё. Так же работает окно из 5 ячеек — шестое показание не добавляется отдельно, а перезаписывает самое старое.
const int RAZMER_OKNA = 5;
float okno[RAZMER_OKNA];
int indeks = 0;
void zapisatPokazanie(float novoe) {
okno[indeks] = novoe;
indeks = (indeks + 1) % RAZMER_OKNA;
}
Круговая запись индекса (RAZMER_OKNA = 5): indeks: 0 -> 1 -> 2 -> 3 -> 4 -> (0+1)%5=0 -> 1 -> ... На шаге, где indeks=4: indeks=(4+1)%5 = 5%5 = 0 -- вернулись в начало, следующая запись перезапишет САМЫЙ СТАРЫЙ элемент (индекс 0)
Фильтр выбросов: fabs() и порог
Выброс — показание, которое сильно отличается от среднего по окну. Модуль разности дробных чисел считает fabs(a - b) (f — от float, как и в других именах этой библиотеки). Если модуль разности больше порога — это выброс.
float srednee = 22.4;
float novoe_pokazanie = 45.0;
float porog = 5.0;
if (fabs(novoe_pokazanie - srednee) > porog) {
Serial.println("Выброс");
} else {
Serial.println("Норма");
}
Выброс
Миссия: скользящее среднее по потоку
Подключи потенциометр к A0. Каждые 500 мс (через millis, без delay) читай показание, записывай в скользящее окно из 5 ячеек по кругу, считай среднее по окну и печатай его вместе с новым показанием.
const int RAZMER_OKNA = 5;
float okno[RAZMER_OKNA];
int indeks = 0;
unsigned long previousMillis = 0;
const long interval = 500;
void setup() {
Serial.begin(9600);
for (int i = 0; i < RAZMER_OKNA; i = i + 1) {
okno[i] = 0;
}
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
float novoe = map(analogRead(A0), 0, 1023, 0, 100);
okno[indeks] = novoe;
indeks = (indeks + 1) % RAZMER_OKNA;
float summa = 0;
for (int i = 0; i < RAZMER_OKNA; i = i + 1) {
summa = summa + okno[i];
}
float srednee = summa / RAZMER_OKNA;
Serial.print("Показание: ");
Serial.print(novoe);
Serial.print(" | Среднее по окну: ");
Serial.println(srednee);
}
}
Проверь себя
Контрольный вопрос. Что такое массив?
Подсказка: Это разбиралось в блоке «Массив: набор значений под одним именем».
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. С какого числа начинается индексация элементов массива?
Подсказка: Так же, как индексация списка в Python.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Что выведет программа?
Исходный код для этого задания:
float pokazaniya[4] = {10.0, 20.0, 30.0, 40.0}; Serial.println(pokazaniya[2]);Подсказка: Индекс 2 — это третий по счёту элемент (0, 1, 2).
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Массив объявлен как float pokazaniya[5]. Какой индекс у последнего элемента?
Подсказка: Индексы идут от 0, значит последний индекс — это размер минус один.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Чему равно среднее значение этого массива? Впиши число (без дробной части, если оно целое).
Исходный код для этого задания:
float pokazaniya[4] = {10.0, 20.0, 30.0, 40.0}; // среднее = сумма всех элементов / количество элементовПодсказка: Сложи все четыре числа и раздели на количество элементов массива.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Массив объявлен как float pokazaniya[5]. В цикле перебора написано условие i <= 5 вместо i < 5. К чему это приведёт?
Исходный код для этого задания:
for (int i = 0; i <= 5; i = i + 1) { summa = summa + pokazaniya[i]; }Подсказка: У массива размером 5 индексы только 0..4 — индекса 5 не существует.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. indeks равен 3, RAZMER_OKNA равен 5. Чему станет равен indeks после (indeks + 1) % RAZMER_OKNA? Впиши число.
Подсказка: Сначала посчитай indeks + 1, потом раздели на RAZMER_OKNA и возьми остаток.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Задание. Напиши код сам. Допиши недостающую часть цикла вместо ___, чтобы summa накопила сумму всех элементов массива pokazaniya размером 5.
Исходный код для этого задания:
float summa = 0; for (int i = 0; i < 5; i = i + 1) { summa = summa + ___; }Подсказка: Обратись к элементу массива по текущему индексу цикла i.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Задание. Напиши код сам (2 строки внутри функции записи). Есть массив okno, индекс indeks и размер RAZMER_OKNA. Запиши значение novoe в текущую позицию окна, затем сдвинь indeks на следующий по кругу.
Исходный код для этого задания:
void zapisatPokazanie(float novoe) { // напиши: записать novoe в okno[indeks], затем сдвинуть indeks по кругу }Подсказка: Первая строка — присваивание в ячейку массива по индексу; вторая — indeks = (indeks + 1) % RAZMER_OKNA.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Окно размером 3: okno = {10, 20, 30}, indeks = 0 (следующая запись пойдёт в ячейку 0). Записали новое показание 99. Каким стал массив okno после записи? Впиши три числа через запятую в порядке индексов 0,1,2.
Исходный код для этого задания:
// okno[0]=10, okno[1]=20, okno[2]=30, indeks=0 // zapisatPokazanie(99);Подсказка: Новое значение перезаписывает ячейку по ТЕКУЩЕМУ indeks (0), остальные ячейки не меняются.
Онлайн-проверка ответа появится позже
Задание. Напиши код сам (цикл, около 8 строк). Найди минимум и максимум массива pokazaniya размером 5 без готовых функций min/max — используй первый элемент как стартовое значение обеих переменных.
Исходный код для этого задания:
float pokazaniya[5] = {22.0, 23.0, 21.0, 24.0, 22.0}; // напиши поиск minimum и maksimum цикломПодсказка: Начни minimum и maksimum со значения pokazaniya[0], затем в цикле от i=1 сравнивай каждый следующий элемент и обновляй при необходимости.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Задание. Мини-проект. Дан массив okno из 5 сохранённых показаний {22.0, 23.0, 21.0, 24.0, 22.0} и новое показание novoe_pokazanie = 45.0, порог porog = 5.0. Напиши код (около 8 строк), который: (1) циклом посчитает среднее по okno, (2) сравнит novoe_pokazanie со средним через fabs(), (3) если разница больше porog — выведет «Выброс», иначе «Норма», (4) выведет и само посчитанное среднее. Принято, если: среднее по этим числам посчитано верно (22.4), разница с novoe_pokazanie сравнена именно с porog через fabs(), и по этим данным программа печатает «Выброс».
Исходный код для этого задания:
float okno[5] = {22.0, 23.0, 21.0, 24.0, 22.0}; float novoe_pokazanie = 45.0; float porog = 5.0; // напиши расчёт среднего, сравнение и вывод по описанию вышеПодсказка: Сначала цикл суммирования и деления на 5 (как в блоке «Перебор циклом»), затем if (fabs(novoe_pokazanie — srednee) > porog) с выводом «Выброс»/«Норма» в каждой ветке, и отдельная строка с выводом srednee.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Что дальше
Теперь станция копит показания, считает среднее, минимум/максимум и находит выброс. Код при этом становится длинным и повторяющимся. На следующем занятии разберём функции — как вынести повторяющиеся куски в отдельные именованные блоки, и соберём читаемый текстовый дашборд.
Итог: ты работаешь с массивами, считаешь сумму/среднее/минимум/максимум циклом, ведёшь скользящее окно по кругу и находишь выброс через fabs(). Дальше — функции и структурированный дашборд.
