1. Введение

Данный проект представляет собой компактную метеостанцию на базе микроконтроллера Arduino, способную измерять температуру окружающей среды и уровень освещенности. Полученные данные выводятся на ЖК-дисплей, а в случае превышения заданного температурного порога система оповещает пользователя звуковым сигналом.

Проект может использоваться в бытовых условиях для мониторинга микроклимата в помещении или на улице, а также в образовательных целях для изучения основ программирования микроконтроллеров и работы с аналоговыми датчиками.

2. Цель программы

Разработать автоматизированную систему, которая:

  • Измеряет температуру с помощью аналогового датчика (LM35 или аналогичного).
  • Определяет уровень освещенности с помощью фоторезистора.
  • Выводит данные на 16×2 символьный ЖК-дисплей с графическими символами.
  • Генерирует звуковые оповещения при превышении температурного порога (20°C).
  • Воспроизводит мелодию в нормальном режиме работы.

3. Описание компонентов

3.1. Аппаратная часть

  • Микроконтроллер Arduino (любая совместимая плата, например, Arduino Uno).
  • Датчик температуры (LM35 или аналогичный), подключенный к аналоговому входу A1.
  • Фоторезистор для измерения освещенности, подключенный к аналоговому входу A0.
  • Пьезоизлучатель (buzzer) для звуковых оповещений, подключенный к цифровому выходу 9.
  • ЖК-дисплей 16×2 с управлением через библиотеку LiquidCrystal (подключен к пинам 12, 11, 5, 4, 3, 2).

3.2. Программная часть

  • Библиотека LiquidCrystal – для управления ЖК-дисплеем.
  • Аналоговые входы – считывание данных с датчиков.
  • Генерация звука – с помощью функции tone().
  • Пользовательские символы – отображение графических элементов (солнце, дождь, смайлы и др.).

4. Структура программы

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int temperaturePin = A1;
const int lightPin = A0;
const int buzzerPin = 9;

float voltage, degreesC;

char* tempText[] = {
  "Cold weather!",     // < 15
  "Nice weather!",     // 15–25
  "Heat rising!",      // 25–30
  "Hot! Drink water!"  // >30
};

char* lightText[] = {
  "Sunny",    // < 85
  "Cloudy",   // 85–170
  "Rainy"     // >170
};

byte smile[8] = {
  0b00000, 0b01010, 0b01010, 0b00000,
  0b10001, 0b01110, 0b00000, 0b00000
};

byte rain[8] = {
  0b00100, 0b00100, 0b01010, 0b01010,
  0b10001, 0b10001, 0b10001, 0b01110
};

byte degrees[8] = {
  0b01110, 0b01010, 0b01110, 0b00000,
  0b00000, 0b00000, 0b00000, 0b00000
};

byte sun[8] = {
  0b00100, 0b01010, 0b11111, 0b11111,
  0b00100, 0b00100, 0b00000, 0b00000
};

byte sad[8] = {
  0b00000, 0b01010, 0b01010, 0b00000,
  0b01110, 0b10001, 0b00000, 0b00000
};

const int songLength = 18;
char notes[] = "cdfda ag cdfdg gf ";
int beats[] = {1,1,1,1,1,1,4,4,2,1,1,1,1,1,1,4,4,2};
int tempo = 150;

void setup() {
  Serial.begin(9600);
  pinMode(buzzerPin, OUTPUT);

  lcd.begin(16, 2);
  lcd.createChar(1, degrees);
  lcd.createChar(2, smile);
  lcd.createChar(3, rain);
  lcd.createChar(4, sun);
  lcd.createChar(5, sad);

  lcd.setCursor(0, 0);
  lcd.print("Weather Station");
  lcd.setCursor(0, 1);
  lcd.print("Initializing...");
  delay(1000);
}

void loop() {
  // Считываем температуру (предполагаем LM35 или аналогичный)
  voltage = analogRead(temperaturePin) * 0.004882814;
  degreesC = (voltage - 0.5) * 100.0;


  // Считываем освещенность
  int lightLevel = analogRead(lightPin);
  lightLevel = map(lightLevel, 300, 800, 0, 255);
  lightLevel = constrain(lightLevel, 0, 255);

  Serial.print("Temp: ");
  Serial.print(degreesC);
  Serial.print(" C | Light: ");
  Serial.println(lightLevel);

  // Проверка тревоги: температура > 25 °C
  if (degreesC > 20) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("!!! ALARM !!!");
    lcd.setCursor(0, 1);
    lcd.print("Temp:");
    lcd.print(degreesC, 1);
    lcd.write(byte(1)); // значок градуса
    lcd.print("C");

    tone(buzzerPin, 1000);  // Постоянный тон
    delay(500);
    noTone(buzzerPin);
    delay(200);
  } else {
    noTone(buzzerPin);
    displayData(degreesC, lightLevel);
    playMelody();
  }
}

void displayData(float temp, int light) {
  char* tempTextToDisplay;
  if (temp < 15) tempTextToDisplay = tempText[0];
  else if (temp < 20) tempTextToDisplay = tempText[1];
  else if (temp < 30) tempTextToDisplay = tempText[2];
  else tempTextToDisplay = tempText[3];

  char* lightTextToDisplay;
  byte lightIcon;

  if (light > 170) {
    lightTextToDisplay = lightText[2];
    lightIcon = 3;
  } else if (light > 85) {
    lightTextToDisplay = lightText[1];
    lightIcon = 4;
  } else {
    lightTextToDisplay = lightText[0];
    lightIcon = 2;
  }

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(tempTextToDisplay);
  lcd.setCursor(14, 0);
  lcd.write(byte(1)); // значок градуса

  lcd.setCursor(0, 1);
  lcd.print(lightTextToDisplay);
  lcd.setCursor(strlen(lightTextToDisplay) + 1, 1);
  lcd.write(lightIcon);

  delay(2000);
}

void playMelody() {
  for (int i = 0; i < songLength; i++) {
    int duration = beats[i] * tempo;
    if (notes[i] == ' ') {
      delay(duration);
    } else {
      tone(buzzerPin, frequency(notes[i]), duration);
      delay(duration);
    }
    delay(tempo / 10);
  }
}

int frequency(char note) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  int freqs[] = {262, 294, 330, 349, 392, 440, 494, 523};
  for (int i = 0; i < 8; i++) {
    if (names[i] == note) return freqs[i];
  }
  return 0;
}

4.1. Подключение библиотеки и инициализация дисплея

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
  • LiquidCrystal.h – библиотека для управления ЖК-дисплеем.
  • lcd(12, 11, 5, 4, 3, 2) – указываем пины Arduino, к которым подключён дисплей (RS, E, D4, D5, D6, D7).

4.2. Определение пинов и переменных

const int temperaturePin = A1;  // Пин датчика температуры (LM35)
const int lightPin = A0;        // Пин фоторезистора
const int buzzerPin = 9;        // Пин пьезопищалки

float voltage, degreesC;        // Для хранения напряжения и температуры
  • Датчики подключены к аналоговым входам A0 (освещённость) и A1 (температура).
  • buzzerPin – цифровой выход для звука.
  • voltage и degreesC – переменные для расчёта температуры.

4.3. Текстовые сообщения

char* tempText[] = {
  "Cold weather!",     // < 15°C
  "Nice weather!",     // 15–25°C
  "Heat rising!",      // 25–30°C
  "Hot! Drink water!"  // >30°C
};

char* lightText[] = {
  "Sunny",    // < 85
  "Cloudy",   // 85–170
  "Rainy"     // >170
};
  • tempText – сообщения в зависимости от температуры.
  • lightText – сообщения в зависимости от освещённости.

4.4. Пользовательские символы для дисплея

byte smile[8] = { ... };  // Улыбающийся смайл
byte rain[8] = { ... };   // Дождь
byte degrees[8] = { ... };// Знак градуса
byte sun[8] = { ... };    // Солнце
byte sad[8] = { ... };    // Грустный смайл

Каждый символ задаётся в виде 8-битного массива (8 строк по 5 пикселей).

4.5. Мелодия для бузера

const int songLength = 18;
char notes[] = "cdfda ag cdfdg gf ";  // Ноты
int beats[] = {1,1,1,1,1,1,4,4,2,1,1,1,1,1,1,4,4,2};  // Длительности
int tempo = 150;  // Скорость воспроизведения
  • Мелодия задаётся нотами (cdf и т. д.) и их длительностями.
  • tempo – скорость (чем больше, тем медленнее).

4.6. Функция setup() – начальная настройка

void setup() {
  Serial.begin(9600);  // Инициализация Serial-порта
  pinMode(buzzerPin, OUTPUT);  // Настройка пина бузера

  lcd.begin(16, 2);  // Инициализация дисплея (16x2)
  
  // Создание пользовательских символов
  lcd.createChar(1, degrees);
  lcd.createChar(2, smile);
  lcd.createChar(3, rain);
  lcd.createChar(4, sun);
  lcd.createChar(5, sad);

  // Приветственное сообщение
  lcd.setCursor(0, 0);
  lcd.print("Weather Station");
  lcd.setCursor(0, 1);
  lcd.print("Initializing...");
  delay(1000);
}
  • Настраивается связь с компьютером (Serial).
  • Пин бузера переводится в режим выхода.
  • Дисплей инициализируется, создаются кастомные символы.
  • Выводится заставка.

4.7. Функция loop() – основной цикл

void loop() {
  // 1. Чтение температуры
  voltage = analogRead(temperaturePin) * 0.004882814;  // Пересчёт в вольты
  degreesC = (voltage - 0.5) * 100.0;  // Пересчёт в градусы

  // 2. Чтение освещённости
  int lightLevel = analogRead(lightPin);
  lightLevel = map(lightLevel, 300, 800, 0, 255);  // Нормализация
  lightLevel = constrain(lightLevel, 0, 255);  // Ограничение 0–255

  // 3. Вывод в Serial-монитор
  Serial.print("Temp: ");
  Serial.print(degreesC);
  Serial.print(" C | Light: ");
  Serial.println(lightLevel);

  // 4. Проверка аварийного режима (температура > 20°C)
  if (degreesC > 20) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("!!! ALARM !!!");
    lcd.setCursor(0, 1);
    lcd.print("Temp:");
    lcd.print(degreesC, 1);
    lcd.write(byte(1));  // Символ градуса
    lcd.print("C");

    tone(buzzerPin, 1000);  // Включение бузера
    delay(500);
    noTone(buzzerPin);
    delay(200);
  } else {
    noTone(buzzerPin);  // Выключение бузера
    displayData(degreesC, lightLevel);  // Вывод данных
    playMelody();  // Проигрывание мелодии
  }
}
  1. Чтение температуры:
    • analogRead() считывает значение (0–1023).
    • Пересчитывается в вольты (* 0.004882814 – шаг АЦП для 5В).
    • Формула (voltage - 0.5) * 100.0 преобразует в °C (для LM35).
  2. Чтение освещённости:
    • analogRead(lightPin) получает сырое значение.
    • map() нормализует его в диапазон 0–255.
    • constrain() ограничивает возможные значения.
  3. Аварийный режим:
    • Если температура > 20°C, выводится предупреждение.
    • Бузер издаёт прерывистый звук.
  4. Нормальный режим:
    • Данные выводятся на дисплей (displayData()).
    • Проигрывается мелодия (playMelody()).

4.8. Функция displayData() – вывод на дисплей

void displayData(float temp, int light) {
  // Выбор текста для температуры
  char* tempTextToDisplay;
  if (temp < 15) tempTextToDisplay = tempText[0];
  else if (temp < 20) tempTextToDisplay = tempText[1];
  else if (temp < 30) tempTextToDisplay = tempText[2];
  else tempTextToDisplay = tempText[3];

  // Выбор текста и иконки для освещённости
  char* lightTextToDisplay;
  byte lightIcon;
  if (light > 170) {
    lightTextToDisplay = lightText[2];
    lightIcon = 3;  // Дождь
  } else if (light > 85) {
    lightTextToDisplay = lightText[1];
    lightIcon = 4;  // Солнце
  } else {
    lightTextToDisplay = lightText[0];
    lightIcon = 2;  // Смайл
  }

  // Вывод на дисплей
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(tempTextToDisplay);
  lcd.setCursor(14, 0);
  lcd.write(byte(1));  // Символ градуса

  lcd.setCursor(0, 1);
  lcd.print(lightTextToDisplay);
  lcd.setCursor(strlen(lightTextToDisplay) + 1, 1);
  lcd.write(lightIcon);  // Иконка погоды

  delay(2000);
}
  • В зависимости от температуры и освещённости выбираются текст и иконка.
  • Данные выводятся на две строки дисплея.

4.9. Функции для работы со звуком

playMelody() – проигрывание мелодии

void playMelody() {
  for (int i = 0; i < songLength; i++) {
    int duration = beats[i] * tempo;
    if (notes[i] == ' ') {
      delay(duration);  // Пауза
    } else {
      tone(buzzerPin, frequency(notes[i]), duration);  // Включение ноты
      delay(duration);
    }
    delay(tempo / 10);
  }
}
  • Перебирает ноты из массива notes.
  • Для каждой ноты вычисляется длительность (beats[i] * tempo).
  • Если символ пробел – пауза, иначе – звук.

frequency() – получение частоты ноты

int frequency(char note) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  int freqs[] = {262, 294, 330, 349, 392, 440, 494, 523};
  for (int i = 0; i &lt; 8; i++) {
    if (names[i] == note) return freqs[i];
  }
  return 0;
}
  • Сопоставляет ноту (cd, …) с частотой (в Гц).

5. Возможные улучшения

  1. Добавление датчика влажности (DHT11/DHT22).
  2. Логирование данных на SD-карту.
  3. Передача данных по Wi-Fi (ESP8266/ESP32).
  4. Настройка пороговых значений через потенциометр.

6. Заключение

Данная метеостанция демонстрирует базовые принципы работы с аналоговыми датчиками, ЖК-дисплеем и звуковой индикацией на Arduino. Проект может быть расширен для более сложных сценариев использования, таких как умный дом или системы мониторинга окружающей среды.

Код хорошо структурирован, содержит комментарии и может служить учебным примером для начинающих разработчиков.