// задать пин, к которому подсоединен светодиод
#ifdef LED_BUILTIN              // для плат Elbear/Start можно использовать встроенный светодиод
uint8_t ledPin = LED_BUILTIN;
#elif defined(ARDUINO_ELSOMIK)  // для плат Elsomik нужно задать вывод
uint8_t ledPin = P0_0;  
#else
uint8_t ledPin = 2;
#endif
// задать пин, к которому подключается средний вывод потенциометра
#if defined(ARDUINO_ELSOMIK) || defined(ARDUINO_START_MIK32_V1)
// для плат Elsomik и Start задать пин в формате порт_вывод. Пин должен поддерживать АЦП измерения
const int sensorPin = P1_5;  
#else
// для плат Elbear задать номер аналогового вывода
const int sensorPin = A0;  
#endif

/*
 * Глобальны переменные для уровней яркости:
 * high (высокий), low (низкий).
 */
int lightLevel, high = 0, low = 1023;

// Объявление функций
void manualTune(); 
void autoTune();

// функция настройки запускается один раз при нажатии кнопки reset или подаче питания на плату
void setup() {
  // Настройка пинов для управления светодиодом:
  pinMode(ledPin, OUTPUT);

  // Для отладки, чтобы увидеть показания датчиков через "Serial Monitor".
  Serial.begin(9600);
}

// функция цикла выполняется снова и снова бесконечно
void loop() {
  lightLevel = analogRead(sensorPin);

  manualTune();  // Ручное изменение диапазона от светлого к темному
  // autoTune(); // Автоматическое изменение диапазона

  /*
   * Вышеупомянутые функции будут изменять "lightLevel" до максимамльных
   * значений от "Полностью Вкл" к "Полному Выкл". И это значит, что теперь
   * мы сможем регулировать яркость светодиода в широких пределах.
   */
  analogWrite(ledPin, lightLevel);

  /*
   * Выражение выше будет увеличивать яркость светодиода с увеличением 
   * сопротивления фоторезистора. Чтобы сделать наоборот, нужно в
   * analogWrite(ledPin, lightLevel) значение "lightLevel" на "255 - lightLevel".
   */

  /* 
   * Для отладки, чтобы увидть показания датчиков через "монитор порта",
   * расскомментируйте следующие строчки.
   */
  // Serial.println(lightLevel);  // Вывод данных с фоторезистора (0 - 1023)
  // delay(500);                  // пауза 500 мс
}

void manualTune()
{
  int lowValue = 0;
  int highValue = 1023;
  /*
   * Функция analogRead() возвращает значение в диапазоне от 0 до 1023,
   * а функция для управления светодиодом analogwrite() работает с диапазоном
   * от 0 до 255.
   * Функция map() может преобразовать значение одного диапазона в соответсвующее
   * значение другого диапазона.
   * Так map(lightLevel, 0, 1023, 0, 255) преобразует 0 в 0, 1023 в 255, а среднее
   * значение 500 станет приблизительно 125.
   */
  lightLevel = map(lightLevel, lowValue, highValue, 0, 255);
  /*
   * Функция map(lightLevel, 0, 1023, 0, 255) преобразует входное значение 
   * lightLevel даже если оно не входит в диапазон от 0 до 1023.
   * Для ограничения диапазона используется функция constrain().
   * Так constrain(lightLevel, 0, 255) преобразует все значения lightLevel
   * выше 255 в 255, а значения ниже 0 в 0.  
   */
  lightLevel = constrain(lightLevel, 0, 255);

  /*
   * Изменение освещенности может быть недостаточно чтобы функция analogRead()
   * вернула значение от 0 до 1023. В реальности диапазон может быть меньше.
   * Приблизительно от 300 (светло) до 800 (темно). 
   * Поэтому, если светодиод светит тускло или его яркость меняется слабо,
   * то можно уменьшить входной диапазон функции map().
   * Например, измените lowValue на 300, а highValue на 800.
   */
}

void autoTune()
{
  /*
   * В функции manualTune() входной диапазон функции map() нужно изменять
   * вручную. Но это можно автоматизировать.
   * В этой функции отслеживаются самые высокие и низкие значения lightLevel,
   * которые мы получаем функцией analogRead().
   * В начале программы объявлены переменные для входного диапазона low и high, 
   * которые и будут автоматически изменяться.
   * Низкому уровню специально присвоено максимальное значение 1023, чтобы
   * первое полученное значение lightLevel было гарантированно меньше значния 
   * low. По аналогии значение high = 0.
   */
  
  if(lightLevel < low)
  {
    low = lightLevel;
  }
  
  if(lightLevel > high)
  {
    high = lightLevel;
  }

  /*
   * После выполнения условий выше получится, что значению low соответсвует
   * минимальное значение из полученных lightLevel, а значнию high максимальное.
   */

  /* 
   * Если светодиод начинает мерцать, нужно в функции map добавить к low или high 
   * небольшое смещение для сужения диапазона.
   */
  lightLevel = map(lightLevel, low, high, 0, 255);
  lightLevel = constrain(lightLevel, 0, 255);
}
