Arduino

Фоторезистор и датчик звука: аналоговые датчики освещённости и звука

1 просмотров

Введение

Фоторезистор и микрофонный модуль датчика звука — два простых аналоговых датчика окружающей среды, часто используемые вместе для проектов вроде автоматического ночника или звукового триггера. В статье разберём считывание обоих датчиков и передачу данных для визуализации и реагирования на стороне Qt.

Концепция

Фоторезистор меняет своё электрическое сопротивление в зависимости от освещённости — в типичной схеме делителя напряжения (фоторезистор плюс постоянный резистор, обычно 10 кОм из комплекта) это изменение сопротивления преобразуется в изменение напряжения, считываемое через analogRead(). Микрофонный модуль обычно содержит встроенный усилитель и компаратор, выдающий либо аналоговый уровень громкости звука (через analogRead()), либо цифровой сигнал превышения порога громкости (через digitalRead()) — конкретное поведение зависит от модели модуля и часто настраивается потенциометром чувствительности непосредственно на самой плате модуля.

Пример кода

// firmware/src/main.cpp — фоторезистор и датчик звука
#define LIGHT_SENSOR_PIN A3
#define SOUND_SENSOR_PIN A4

void setup()
{
    Serial.begin(9600);
    delay(100);
    Serial.println("READY");
}

void loop()
{
    static unsigned long lastLightTime = 0, lastSoundTime = 0;
    static int soundPeak = 0;

    if (millis() - lastLightTime >= 500) {
        const int lightValue = analogRead(LIGHT_SENSOR_PIN);
        // Инверсия — у делителя напряжения с фоторезистором рост освещённости
        // часто соответствует УМЕНЬШЕНИЮ считываемого значения, в зависимости от схемы подключения
        const int lightPercent = map(lightValue, 0, 1023, 100, 0);
        Serial.print("LIGHT:");
        Serial.println(lightPercent);
        lastLightTime = millis();
    }

    // Звук считывается значительно чаще, поскольку громкие события могут быть короткими
    const int soundValue = analogRead(SOUND_SENSOR_PIN);
    soundPeak = max(soundPeak, soundValue);

    if (millis() - lastSoundTime >= 100) {
        Serial.print("SOUND:");
        Serial.println(soundPeak);
        soundPeak = 0; // сброс пикового значения для следующего интервала измерения
        lastSoundTime = millis();
    }
}

Пояснения к коду

map(lightValue, 0, 1023, 100, 0) — обратное масштабирование (конечный диапазон указан в обратном порядке, 100 затем 0), что используется, когда физическая схема подключения даёт инвертированную зависимость (меньше света → выше сопротивление фоторезистора → определённое изменение напряжения в делителе, в зависимости от того, к какому плечу делителя подключён АЦП) — конкретное направление инверсии зависит от точной схемы подключения, и при первой настройке стоит эмпирически проверить, действительно ли увеличение освещённости даёт ожидаемое увеличение процента, корректируя порядок аргументов map() при необходимости.

Для звукового датчика используется паттерн накопления пикового значения (soundPeak = max(soundPeak, soundValue)) внутри каждого короткого интервала (100 мс) перед отправкой — поскольку звуковые события (хлопок, стук) часто очень короткие, считывание значения только в фиксированные, редкие моменты времени легко «пропустит» сам пик громкости; накопление максимума за интервал гарантирует, что короткий, но громкий звук будет зафиксирован, даже если он произошёл между двумя точками обычного периодического считывания.

Подводные камни

  • Прямая зависимость показаний фоторезистора от конкретной схемы делителя напряжения (значение второго резистора в паре с фоторезистором) — без знания точного номинала использованного резистора (10 кОм в типичном наборе) и точной схемы подключения сложно заранее предсказать направление и диапазон изменения считываемого значения; эмпирическая проверка (закрыть датчик рукой и посмотреть, увеличилось или уменьшилось считанное значение) — самый практичный способ определить корректное направление инверсии для конкретной собранной схемы.
  • Чувствительность микрофонного модуля, регулируемая физическим потенциометром на самой плате модуля, может быть выставлена слишком высоко или слишком низко при первой настройке — слишком высокая чувствительность приводит к постоянным ложным срабатываниям даже от фонового шума, а слишком низкая — к пропуску реальных звуковых событий; начальная физическая калибровка чувствительности модуля обязательна перед программной отладкой, поскольку программный код не может компенсировать совершенно неправильно настроенную аппаратную чувствительность.
  • Считывание звукового сигнала только в отдельные, дискретные моменты времени (без накопления пикового значения, как показано в исправленном примере) — частая первая, наивная реализация, которая просит надёжно отображать «уровень громкости в реальном времени», но реально пропускает короткие, важные события; необходимость явного накопления максимума за интервал — не всегда очевидна новичкам до того, как они на практике замечают, что громкие, но короткие звуки «не замечаются» датчиком.
  • Постоянное считывание звукового датчика без какой-либо периодичности внутри loop() (как в примере, в отличие от датчика освещённости, опрашиваемого только раз в 500 мс) добавляет небольшую, но не нулевую постоянную нагрузку на каждую итерацию цикла — для проектов с очень большим количеством одновременных задач в loop() это нужно явно учитывать в общем бюджете времени одной итерации, особенно если в том же проекте присутствует требовательное к времени мультиплексирование.