#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();
void navigateMenu();
void displayMenu();
float calculateDewPoint();
void updateGraph(float temp);
void storeMeasurement(float temp, float humidity);
void displayHistory();
void displayAlertSettings();
void checkWeatherForecast();
void displayTrends(float temp, float humidity);
void updateExtremes(float temp);
void checkAlerts(float temp, float humidity);
void displayStats();
float getAverageTemp();
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();
}
}
void navigateMenu() {
if (isEditingTemperature || isEditingHumidity) {
if (!digitalRead(BUTTON_UP)) {
if (isEditingTemperature) {
criticalTemperature += 0.5;
} else if (isEditingHumidity) {
criticalHumidity += 1.0;
}
displayAlertsSettings();
delay(200);
} else if (!digitalRead(BUTTON_DOWN)) {
if (isEditingTemperature) {
criticalTemperature -= 0.5;
} else if (isEditingHumidity) {
criticalHumidity -= 1.0;
}
displayAlertsSettings();
delay(200);
} else if (!digitalRead(BUTTON_BACK)) {
isEditingTemperature = false;
isEditingHumidity = false;
displayAlertsSettings();
delay(200);
}
return;
}
// Обычная навигация
if (!digitalRead(BUTTON_UP)) {
currentPage = (currentPage + 1) % TOTAL_PAGES;
displayMenu();
delay(200);
} else if (!digitalRead(BUTTON_DOWN)) {
currentPage = (currentPage - 1 + TOTAL_PAGES) % TOTAL_PAGES;
displayMenu();
delay(200);
} else if (!digitalRead(BUTTON_SELECT)) {
if (currentPage == 6) {
if (!isEditingTemperature && !isEditingHumidity) {
isEditingTemperature = true;
}
}
displayAlertsSettings();
delay(200);
} else if (!digitalRead(BUTTON_BACK)) {
if (currentPage == 6) {
if (!isEditingTemperature && !isEditingHumidity) {
isEditingHumidity = true;
}
}
displayAlertsSettings();
delay(200);
}
}
void displayMenu() {
lcd.clear();
switch (currentPage) {
case 0:
lcd.print("Data");
updateSensorData();
break;
case 1:
updateGraph(dht.readTemperature());
lcd.clear();
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;
}
}
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 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 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 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: Falls ");
lcd.setCursor(0, 2);
lcd.print(tempChange, 1);
lcd.print(" C");
}
} else {
lcd.print("Temp: N/A ");
}
// Тренд влажности
lcd.setCursor(0, 3);
if (prevHumidity != 0) {
float humidityChange = humidity - prevHumidity;
if (fabs(humidityChange) < 1.0) { // Если изменения меньше 1%
lcd.print("Humid: Stable");
} else if (humidityChange > 0) {
lcd.print("Humid: Rising +");
lcd.print(humidityChange, 1);
lcd.print("%");
} else {
lcd.print("Humid: Falling ");
lcd.print(humidityChange, 1);
lcd.print("%");
}
} else {
lcd.print("Humid: N/A ");
}
prevTemp = temp;
prevHumidity = humidity;
}
void updateExtremes(float temp) {
if (temp < minTemp) {
minTemp = temp;
}
if (temp > maxTemp) {
maxTemp = temp;
}
}
void displayExtremes() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Extremes:");
lcd.setCursor(0, 1);
lcd.print("Min Temp: ");
lcd.print(minTemp, 1);
lcd.print(" C");
lcd.setCursor(0, 2);
lcd.print("Max Temp: ");
lcd.print(maxTemp, 1);
lcd.print(" C");
}
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 displayAlertsSettings() {
lcd.clear();
if (isEditingTemperature) {
lcd.setCursor(0, 0);
lcd.print("Editing Temp: ");
lcd.print(criticalTemperature, 1);
lcd.print(" C");
lcd.setCursor(0, 1);
lcd.print("Back: Save");
} else if (isEditingHumidity) {
lcd.setCursor(0, 0);
lcd.print("Editing Humid: ");
lcd.print(criticalHumidity, 1);
lcd.print(" %");
lcd.setCursor(0, 1);
lcd.print("Back: Save");
} else {
lcd.setCursor(0, 0);
lcd.print("Temp: ");
lcd.print(criticalTemperature, 1);
lcd.print(" C");
lcd.setCursor(0, 1);
lcd.print("Humid: ");
lcd.print(criticalHumidity, 1);
lcd.print(" %");
lcd.setCursor(0, 2);
lcd.print("Select: Edit Temp");
lcd.setCursor(0, 3);
lcd.print("Back: Edit Humid");
}
}
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 displayStats() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Avg Temp: ");
lcd.print(getAverageTemp(), 1);
lcd.print(" C");
lcd.setCursor(0, 1);
lcd.print("Avg Humid: ");
lcd.print(getAverageHumid(), 1);
lcd.print(" %");
lcd.setCursor(0, 2);
lcd.print("Min/Max T: ");
lcd.print(minTemp, 1);
lcd.print("/");
lcd.print(maxTemp, 1);
lcd.setCursor(0, 3);
lcd.print("Change: ");
float tempChange = tempHistoryArray[historyPointer] - tempHistoryArray[(historyPointer - 1 + HISTORY_SIZE) % HISTORY_SIZE];
float humidChange = humidHistoryArray[historyPointer] - humidHistoryArray[(historyPointer - 1 + HISTORY_SIZE) % HISTORY_SIZE];
lcd.print(tempChange > 0 ? "+" : "");
lcd.print(tempChange, 1);
lcd.print("C ");
lcd.print(humidChange > 0 ? "+" : "");
lcd.print(humidChange, 1);
lcd.print("%");
}
float getAverageHumid() {
float sum = 0;
for (int i = 0; i < HISTORY_SIZE; i++) {
sum += humidHistoryArray[i];
}
return sum / HISTORY_SIZE;
}
float getAverageTemp() {
float sum = 0;
for (int i = 0; i < HISTORY_SIZE; i++) {
sum += tempHistoryArray[i];
}
return sum / HISTORY_SIZE;
}