Фоторезистор
Датчик света: чем ярче, тем меньше его сопротивление. Подключим к Arduino Nano, спрячем в коробку и будем закрывать её окошко ширмой.
Датчик в коробке, ширма на окошке
Фоторезистор — это резистор из полупроводника, который «проводит» тем лучше, чем больше на него падает света. У самого ходового GL5528 в темноте не меньше 1 МОм, в сумерках (10 лк) — 10–20 кОм, при комнатном свете (100–300 лк) — 2–5 кОм, на ярком солнце — сотни ом.
Фоторезистор стоит в коробке, свет попадает только через окошко. Тяните ширму мышкой (или пальцем) или двигайте ползунок — опущенная ширма закрывает окошко целиком, и внутри полная темнота.
Закрытое окошко — это настоящая темнота при любом свете снаружи: остаётся только темновое сопротивление, 1 МОм, analogRead ≈ 10. Ползунок ниже — сколько света снаружи: от ночи до яркого солнца.
Как подключить к Arduino Nano
Сам по себе фоторезистор не выдаёт напряжение — он только меняет сопротивление. Arduino же умеет измерять напряжение. Поэтому к фоторезистору добавляют обычный постоянный резистор, и получается делитель напряжения — как потенциометр, только «ползунок» двигает свет.
- Одна нога фоторезистора — на +5V, другая — на пин A1.
- От A1 же — резистор 10 кОм на GND. У фоторезистора нет полярности, ноги можно не различать.
- Больше света → меньше сопротивление фоторезистора → напряжение на A1 ближе к 5 В →
analogReadбольше. - Если поменять местами фоторезистор и 10 кОм, всё станет наоборот: больше света — меньше число.
Почему 10 кОм: делитель чувствительнее всего, когда постоянный резистор примерно равен сопротивлению фоторезистора при «рабочем» освещении. Для комнаты подходит 10 кОм, для тёмного помещения — 47–100 кОм, для улицы — 1 кОм.
int light = analogRead(A1); // 0 — темно, 1023 — очень светло // в скетче sine_pm_am_crush_lfo2.ino: amDepth = map(light, LIGHT_MIN, LIGHT_MAX, 0, 255); // свет → глубина тремоло
Формулы на странице: U = 5 В × 10 кОм / (R + 10 кОм), сопротивление — по даташиту GL5528: R = 15 кОм × (лк / 10)−0.6, в полной темноте 1 МОм. Настоящий датчик может отличаться в 2–3 раза, поэтому LIGHT_MIN в скетче подбирают по факту — или Arduino находит его сам при включении, см. точку темноты.
Точка темноты: Arduino калибруется сам
// Фоторезистор с «точкой темноты»: при включении Arduino запоминает
// текущий свет как «полностью темно» и дальше считает свет от неё до солнца.
// Схема: +5V — фоторезистор — A1 — 10 кОм — GND. Звук на D9.
#include <Mozzi.h>
#include <Oscil.h>
#include <tables/sin2048_int8.h>
#define LDR_PIN A1
#define LIGHT_SUN 1000 // analogRead на солнце (GL5528 + 10 кОм: около 975–1020)
#define DARK_MARGIN 8 // запас над точкой темноты, чтобы шум АЦП не «шуршал»
#define CAL_SAMPLES 64 // сколько замеров усреднить при калибровке (64 × 5 мс ≈ 0,3 с)
Oscil<SIN2048_NUM_CELLS, MOZZI_AUDIO_RATE> aSin(SIN2048_DATA);
int darkPoint; // analogRead, который считаем «полностью темно»
int lightSm; // сглаженный analogRead × 16
uint8_t level = 0; // 0 (темно) … 255 (солнце)
// Усредняем несколько замеров — это и есть точка темноты.
// Вызывать ДО startMozzi(): обычный analogRead работает только до него.
int calibrateDark(uint8_t pin) {
analogRead(pin); // первый замер после смены пина выбрасываем
long sum = 0;
for (int i = 0; i < CAL_SAMPLES; i++) {
sum += analogRead(pin);
delay(5);
}
int dark = sum / CAL_SAMPLES + DARK_MARGIN;
return min(dark, LIGHT_SUN - 100); // включили на солнце — оставим хоть какой-то диапазон
}
void setup() {
darkPoint = calibrateDark(LDR_PIN);
lightSm = darkPoint << 4;
aSin.setFreq(220);
startMozzi();
}
void updateControl() {
int raw = mozziAnalogRead(LDR_PIN);
lightSm += ((raw << 4) - lightSm) >> 3; // сглаживание, как в скетче курса
int light = lightSm >> 4;
// стало темнее, чем при включении? сдвигаем точку темноты вниз
if (light + DARK_MARGIN < darkPoint) darkPoint = light + DARK_MARGIN;
light = constrain(light, darkPoint, LIGHT_SUN);
level = map(light, darkPoint, LIGHT_SUN, 0, 255);
}
AudioOutput updateAudio() {
return MonoOutput::from16Bit(aSin.next() * level); // громкость = свет
}
void loop() {
audioHook();
}