#include <Adafruit_BMP280.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <math.h>
#define BMP_SCK (13)
#define BMP_MISO (12)
#define BMP_MOSI (11)
#define BMP_CS (10)
#define DHTPIN 2
#define DHTTYPE DHT22
#define BUTTON_UP 5
#define BUTTON_DOWN 4
#define BUTTON_SELECT 6
#define BUTTON_BACK 3
#define TOTAL_PAGES 8
#define GREEN_LED 8
#define RED_LED 9
#define BUZZER 7
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 20, 4);
Adafruit_BMP280 bmp(BMP_CS);
int currentPage = 0;
#define GRAPH_WIDTH 20 // Ширина гистограммы (количество столбиков)
#define GRAPH_HEIGHT 4 // Высота гистограммы (максимальное количество строк)
#define MIN_TEMP -40 // Минимальная температура
#define MAX_TEMP 85 // Максимальная температура
// История температур и влажности для графика и статистики
float tempHistory[GRAPH_WIDTH]; // История температур
float humidHistory[GRAPH_WIDTH] = {0};
int historyIndex = 0;
float minTemp = 100.0;
float maxTemp = -100.0;
float lastTemp = 0;
float prevTemp = 0;
float prevHumidity = 0;
bool isEditingTemperature = false;
bool isEditingHumidity = false;
float criticalTemperature = 30.0; // Начальное значение
float criticalHumidity = 80.0; // Начальное значение
// История данных для вывода
#define HISTORY_SIZE 10
float tempHistoryArray[HISTORY_SIZE];
float humidHistoryArray[HISTORY_SIZE];
int historyPointer = 0;
// ---------------------------------- Функции работы с сенсорами -------------------------------------
void updateSensorData();
float calculateDewPoint();
void storeMeasurement(float temp, float humidity);
void checkWeatherForecast();
void updateGraph(float temp);
void updateExtremes(float temp);
void checkAlerts(float temp, float humidity);
// ----------------------------------- Функции для отображения --------------------------------------
void displayMenu();
void displayHistory();
void displayTrends(float temp, float humidity);
void displayExtremes();
void displayAlertsSettings();
void displayStats();
// ---------------------------------- Функции управления меню --------------------------------------
void navigateMenu();
// ----------------------------------- Функции для статистики и расчётов --------------------------------
float getAverageTemp();
float getAverageHumid();
// ----------------------------------------- Setup и Loop ------------------------------------------
void setup() {
lcd.init();
lcd.backlight();
bmp.begin();
dht.begin();
lcd.setCursor(0, 0);
lcd.print("Welcome to the");
lcd.setCursor(0, 1);
lcd.print("Weather");
lcd.setCursor(0, 2);
lcd.print("Station");
lcd.setCursor(0, 3);
lcd.print("made by Nursultan A.");
delay(2000);
lcd.clear();
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(BUTTON_SELECT, INPUT_PULLUP);
pinMode(BUTTON_BACK, INPUT_PULLUP);
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(BUZZER, OUTPUT);
updateSensorData();
}
void loop() {
navigateMenu();
float temp = dht.readTemperature();
float humidity = dht.readHumidity();
storeMeasurement(temp, humidity);
updateExtremes(temp);
checkAlerts(temp, humidity);
delay(100);
}
// --------------------------------- Функции работы с сенсорами -------------------------------------
void updateSensorData() {
lcd.setCursor(0, 0);
lcd.print("Temp: ");
lcd.print(dht.readTemperature());
lcd.print(" C ");
lcd.setCursor(0, 1);
lcd.print("Humid: ");
lcd.print(dht.readHumidity());
lcd.print(" % ");
lcd.setCursor(0, 2);
lcd.print("Dew Point: ");
lcd.print(calculateDewPoint(dht.readTemperature(), dht.readHumidity()));
lcd.print(" C");
lcd.setCursor(0, 3);
lcd.print("Pressure: ");
lcd.print(bmp.readPressure());
lcd.print(" Pa");
if (currentPage == 2) { // Страница с прогнозом погоды
checkWeatherForecast();
}
}
float calculateDewPoint(float temp, float humidity) {
const float a = 17.27;
const float b = 237.7;
float alpha = ((a * temp) / (b + temp)) + log(humidity / 100.0);
return (b * alpha) / (a - alpha);
}
void storeMeasurement(float temp, float humidity) {
tempHistoryArray[historyPointer] = temp;
humidHistoryArray[historyPointer] = humidity;
historyPointer = (historyPointer + 1) % HISTORY_SIZE; // Переход к следующему индексу
}
void checkWeatherForecast() {
int currentTemp = dht.readTemperature();
if (prevTemp != 0) {
if (currentTemp > prevTemp) {
lcd.setCursor(0, 1);
lcd.print("It's warming ");
} else if (currentTemp < prevTemp) {
lcd.setCursor(0, 1);
lcd.print("It's cooling ");
} else {
lcd.setCursor(0, 1);
lcd.print("It's stable ");
}
}
prevTemp = currentTemp;
}
void updateGraph(float temp) {
for (int i = 0; i < GRAPH_WIDTH; i++) {
int barHeight = map(tempHistory[i], MIN_TEMP, MAX_TEMP, 0, GRAPH_HEIGHT - 1); // Преобразуем температуру в высоту столбика
for (int j = 0; j < GRAPH_HEIGHT; j++) {
if (GRAPH_HEIGHT - j - 1 == barHeight) {
lcd.setCursor(i, j);
lcd.print("-"); // Столбик графика
}
}
}
if (temp != lastTemp) {
tempHistory[historyIndex] = temp; // Добавляем новое значение температуры
historyIndex = (historyIndex + 1) % GRAPH_WIDTH; // Если мы дошли до конца, начинаем с первого
}
lastTemp = temp;
}
void updateExtremes(float temp) {
if (temp < minTemp) {
minTemp = temp;
}
if (temp > maxTemp) {
maxTemp = temp;
}
}
void checkAlerts(float temp, float humidity) {
if (temp > criticalTemperature) {
lcd.setCursor(0, 1);
digitalWrite(GREEN_LED, LOW);
digitalWrite(RED_LED, HIGH);
tone(BUZZER, 1000, 500); // 1000 Гц на 500 мс
} else if (humidity > criticalHumidity) {
lcd.setCursor(0, 1);
digitalWrite(GREEN_LED, LOW);
digitalWrite(RED_LED, HIGH);
tone(BUZZER, 2000, 500); // 2000 Гц на 500 мс
} else {
lcd.setCursor(0, 1);
digitalWrite(RED_LED, LOW);
digitalWrite(GREEN_LED, HIGH);
noTone(BUZZER); // Выключаем сигнал
}
}
// ------------------------------------ Функции отображения -----------------------------------------
void displayMenu() {
lcd.clear();
switch (currentPage) {
case 0:
lcd.print("Data");
updateSensorData();
break;
case 1:
lcd.print("Temperature Graph");
updateGraph(dht.readTemperature());
break;
case 2:
lcd.print("Weather Forecast");
checkWeatherForecast();
break;
case 3:
lcd.print("History");
displayHistory();
break;
case 4:
lcd.print("Trends");
displayTrends(dht.readTemperature(), dht.readHumidity());
break;
case 5:
lcd.print("Extremes");
displayExtremes();
break;
case 6:
displayAlertsSettings();
break;
case 7:
lcd.print("Statistics");
displayStats();
break;
default:
lcd.print("Invalid Page");
break;
}
}
void displayHistory() {
int lastIndex = (historyPointer - 1 + HISTORY_SIZE) % HISTORY_SIZE;
lcd.setCursor(0, 1);
lcd.print("Last Temp: ");
lcd.print(tempHistoryArray[lastIndex]);
lcd.setCursor(0, 2);
lcd.print("Last Humid: ");
lcd.print(humidHistoryArray[lastIndex]);
}
void displayTrends(float temp, float humidity) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Trends Analysis");
lcd.setCursor(0, 1);
if (prevTemp != 0) {
float tempChange = temp - prevTemp;
if (fabs(tempChange) < 0.5) {
lcd.print("Temp: Stable ");
lcd.setCursor(0, 2);
lcd.print("--------------------");
} else if (tempChange > 0) {
lcd.print("Temp: Rising ");
lcd.setCursor(0, 2);
lcd.print("+");
lcd.print(tempChange, 1);
lcd.print(" C");
} else {
lcd.print("Temp: Falling ");
lcd.setCursor(0, 2);
lcd.print("-");
lcd.print(-tempChange, 1);
lcd.print(" C");
}
}
prevTemp = temp;
lcd.setCursor(0, 3);
lcd.print("Humid: ");
lcd.print(humidity);
}
void displayExtremes() {
lcd.setCursor(0, 0);
lcd.print("Min Temp: ");
lcd.print(minTemp);
lcd.setCursor(0, 1);
lcd.print("Max Temp: ");
lcd.print(maxTemp);
}
void displayAlertsSettings() {
lcd.setCursor(0, 0);
lcd.print("Critical Temp: ");
lcd.print(criticalTemperature);
}
void displayStats() {
lcd.setCursor(0, 0);
lcd.print("Avg Temp: ");
lcd.print(getAverageTemp());
lcd.setCursor(0, 1);
lcd.print("Avg Humid: ");
lcd.print(getAverageHumid());
}
// ------------------------------- Функции для расчета статистики -------------------------------
float getAverageTemp() {
float sum = 0;
for (int i = 0; i < HISTORY_SIZE; i++) {
sum += tempHistoryArray[i];
}
return sum / HISTORY_SIZE;
}
float getAverageHumid() {
float sum = 0;
for (int i = 0; i < HISTORY_SIZE; i++) {
sum += humidHistoryArray[i];
}
return sum / HISTORY_SIZE;
}
// ---------------------------- Функции управления меню и навигацией ----------------------------
void navigateMenu() {
if (digitalRead(BUTTON_UP) == LOW) {
currentPage = (currentPage - 1 + TOTAL_PAGES) % TOTAL_PAGES;
displayMenu();
delay(200);
}
if (digitalRead(BUTTON_DOWN) == LOW) {
currentPage = (currentPage + 1) % TOTAL_PAGES;
displayMenu();
delay(200);
}
}