/* * ===================================================================
* ПРОЄКТ: Автоматизація преса (Дипломна робота)
* ВЕРСІЯ: v1.7r34 (Фікс екрана, перехід на автономну бібліотеку DHT22)
* СТРУКТУРА: Повний скетч (9 блоків, розширене коментування)
* ===================================================================
*/
// ==========================================
// БЛОК №1: БІБЛІОТЕКИ ТА CONFIG
// ==========================================
#include <Wire.h> // Бібліотека для шини I2C
#include <SPI.h> // Бібліотека для шини SPI
#include <Adafruit_GFX.h> // Базова графічна бібліотека Adafruit
#include <Adafruit_SSD1306.h> // Драйвер для OLED дисплея SSD1306
#include <Encoder.h> // Бібліотека для точного зчитування сигналів енкодера
#include <DHT22.h> // Автономна бібліотека DHT22 (в самому низу твого списку)
// --- Налаштування дисплея ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- Температурні константи та змінні ---
const int HYSTERESIS = 5;
#define DHTPIN 9 // Датчик підключений до піна D9
DHT22 dht(DHTPIN); // Ініціалізація об'єкта без зайвих залежностей
float currentTemp = 0.0;
unsigned long lastDhtReadTime = 0;
const unsigned long dhtInterval = 2000;
unsigned int cycleCount = 0;
// --- Піни виконавчих механізмів ---
const int pinLedRed = 7;
const int pinLedGreen = 10;
const int pinBuzzer = 8;
const int pinActuatorDown = 11;
const int pinActuatorUp = 12;
const int pinHeater = 13;
// --- Піни кнопок та датчиків ---
const int pinNewButton = 2;
const int pinButtonEmergency = 3;
const int pinButtonMenu = A3;
const int pinButtonStart = A0;
const int pinLimitSwitch1 = A1;
const int pinLimitSwitch2 = A2;
// --- Конфігурація енкодера ---
const int pinEncoderA = 4;
const int pinEncoderB = 5;
const int pinEncoderBtn = 6;
Encoder myEnc(pinEncoderA, pinEncoderB);
long oldPosition = -999;
// --- Скінченні автомати (State Machines) ---
enum SystemState { IDLE, EMERGENCY_STATE };
SystemState currentState = IDLE;
enum PressState { PRESS_IDLE, PRESS_DOWN, PRESS_HOLD, PRESS_UP };
PressState currentPressState = PRESS_IDLE;
// --- Змінні таймерів та системних параметрів ---
unsigned long pressStartMillis = 0;
String statusText = "INIT";
bool menuState = false;
int menuIndex = 0;
int targetTemp = 25;
int maxTemp = 30;
int pressTime = 15;
// Прототипи функцій
void readTemperature();
void handleLED();
void displayUpdate();
void handleMenu();
void handlePressCycle();
void handleEmergency();
void initStatistics();
void updateStats();
// ==========================================
// БЛОК №2: SETUP (НАЛАШТУВАННЯ)
// ==========================================
void setup() {
// Першим ділом запускаємо дисплей
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
for (;;);
}
// Для цієї бібліотеки dht.begin() НЕ потрібен, вона автономна
pinMode(pinLedGreen, OUTPUT);
pinMode(pinLedRed, OUTPUT);
pinMode(pinBuzzer, OUTPUT);
pinMode(pinActuatorDown, OUTPUT);
pinMode(pinActuatorUp, OUTPUT);
pinMode(pinHeater, OUTPUT);
pinMode(pinNewButton, INPUT_PULLUP);
pinMode(pinButtonEmergency, INPUT_PULLUP);
pinMode(pinButtonMenu, INPUT_PULLUP);
pinMode(pinEncoderBtn, INPUT_PULLUP);
pinMode(pinEncoderA, INPUT_PULLUP);
pinMode(pinEncoderB, INPUT_PULLUP);
pinMode(pinButtonStart, INPUT_PULLUP);
pinMode(pinLimitSwitch1, INPUT_PULLUP);
pinMode(pinLimitSwitch2, INPUT_PULLUP);
digitalWrite(pinActuatorDown, LOW);
digitalWrite(pinActuatorUp, LOW);
digitalWrite(pinLedGreen, HIGH);
digitalWrite(pinLedRed, HIGH);
digitalWrite(pinBuzzer, LOW);
digitalWrite(pinHeater, HIGH);
maxTemp = targetTemp + HYSTERESIS;
displayUpdate();
initStatistics();
}
// ==========================================
// БЛОК №3: LOOP (ГОЛОВНИЙ ЦИКЛ)
// ==========================================
void loop() {
handleMenu();
readTemperature();
handleEmergency();
handleLED();
handlePressCycle();
displayUpdate();
}
// ==========================================
// БЛОК №4: ТЕМПЕРАТУРНИЙ ДАТЧИК
// ==========================================
void readTemperature() {
if (millis() - lastDhtReadTime >= dhtInterval) {
lastDhtReadTime = millis();
// Зчитуємо дані через метод нової бібліотеки
float t = dht.getTemperature();
// Перевірка на помилку зчитування (якщо повернуло спеціальний прапорець помилки)
if (t == -999.0 || isnan(t)) {
currentTemp = -100.0;
} else {
currentTemp = t;
}
}
}
// ==========================================
// БЛОК №5: КЕРУВАННЯ ІНДИКАЦІЄЮ
// ==========================================
void handleLED() {
if (currentState == EMERGENCY_STATE) return;
if (currentPressState == PRESS_DOWN) return;
if (currentTemp >= (float)targetTemp && currentTemp <= (float)maxTemp) {
digitalWrite(pinLedRed, HIGH);
digitalWrite(pinLedGreen, LOW);
digitalWrite(pinBuzzer, LOW);
} else {
digitalWrite(pinLedRed, LOW);
digitalWrite(pinLedGreen, HIGH);
digitalWrite(pinBuzzer, HIGH);
}
}
// ==========================================
// БЛОК №6: МЕНЮ ТА ЕНКОДЕР
// ==========================================
void handleMenu() {
static int lastMenuBtn = HIGH;
static unsigned long lastMenuDebounce = 0;
int currentMenuBtn = digitalRead(pinButtonMenu);
if (currentMenuBtn == LOW && lastMenuBtn == HIGH && (millis() - lastMenuDebounce > 300)) {
menuState = !menuState;
if (menuState && menuIndex == 0) menuIndex = 1;
lastMenuDebounce = millis();
}
lastMenuBtn = currentMenuBtn;
static int lastEncBtn = HIGH;
int currentEncBtn = digitalRead(pinEncoderBtn);
if (currentEncBtn == LOW && lastEncBtn == HIGH) {
if (menuState) {
menuIndex++;
if (menuIndex > 2) menuIndex = 1;
}
delay(50);
}
lastEncBtn = currentEncBtn;
long newPosition = myEnc.read();
if (newPosition != oldPosition) {
long delta = (newPosition - oldPosition) / 4;
if (delta != 0) {
oldPosition = newPosition;
if (menuState) {
if (menuIndex == 1) {
targetTemp += delta;
if (targetTemp < 1) targetTemp = 1;
maxTemp = targetTemp + HYSTERESIS;
}
else if (menuIndex == 2) {
pressTime += delta;
if (pressTime < 1) pressTime = 1;
}
}
}
}
}
// ===================================================================
// БЛОК №7: ЦИКЛ ПРЕСА
// ===================================================================
void handlePressCycle() {
if (currentState == EMERGENCY_STATE) {
currentPressState = PRESS_IDLE;
return;
}
if (currentPressState != PRESS_IDLE) {
digitalWrite(pinLedGreen, HIGH);
digitalWrite(pinLedRed, HIGH);
}
switch (currentPressState) {
case PRESS_IDLE:
digitalWrite(pinActuatorDown, LOW);
digitalWrite(pinActuatorUp, LOW);
if (digitalRead(pinNewButton) == LOW) {
if (currentTemp >= (float)targetTemp && currentTemp <= (float)maxTemp && currentTemp != -100.0) {
pressStartMillis = millis();
currentPressState = PRESS_DOWN;
} else {
statusText = "TEMP BLOCK!";
}
}
break;
case PRESS_DOWN:
statusText = "PRESSING DOWN";
digitalWrite(pinActuatorUp, LOW);
digitalWrite(pinActuatorDown, HIGH);
if (millis() - pressStartMillis >= 3000) {
digitalWrite(pinActuatorDown, LOW);
pressStartMillis = millis();
currentPressState = PRESS_HOLD;
}
break;
case PRESS_HOLD:
statusText = "HOLD: " + String(pressTime - (millis() - pressStartMillis) / 1000) + "s";
digitalWrite(pinActuatorDown, LOW);
digitalWrite(pinActuatorUp, LOW);
if (millis() - pressStartMillis >= ((unsigned long)pressTime * 1000)) {
pressStartMillis = millis();
currentPressState = PRESS_UP;
}
break;
case PRESS_UP:
statusText = "RELEASING UP";
digitalWrite(pinActuatorDown, LOW);
digitalWrite(pinActuatorUp, HIGH);
if (millis() - pressStartMillis >= 3000) {
digitalWrite(pinActuatorUp, LOW);
statusText = "CYCLE DONE";
updateStats();
currentPressState = PRESS_IDLE;
}
break;
}
}
// ==========================================
// БЛОК №8: БЕЗПЕКА ТА АВАРІЙНЕ БЛОКУВАННЯ
// ==========================================
void handleEmergency() {
bool eStopPressed = (digitalRead(pinButtonEmergency) == LOW);
bool sensorError = (currentTemp <= -100.0);
static unsigned long emergencyStartMillis = 0;
static bool lastButtonState = HIGH;
static unsigned long lastDebounceTime = 0;
bool btnClicked = false;
if (eStopPressed != (lastButtonState == LOW)) {
if (millis() - lastDebounceTime > 50) {
if (eStopPressed) {
btnClicked = true;
}
lastButtonState = eStopPressed ? LOW : HIGH;
}
lastDebounceTime = millis();
}
if (btnClicked || sensorError) {
if (currentState != EMERGENCY_STATE) {
currentState = EMERGENCY_STATE;
emergencyStartMillis = millis();
}
else if (btnClicked && !sensorError && (millis() - emergencyStartMillis >= 5000)) {
digitalWrite(pinBuzzer, LOW);
digitalWrite(pinHeater, HIGH);
currentState = IDLE;
statusText = "OK";
return;
}
}
if (currentState == EMERGENCY_STATE) {
digitalWrite(pinHeater, LOW);
digitalWrite(pinActuatorDown, LOW);
digitalWrite(pinLedRed, LOW);
digitalWrite(pinLedGreen, HIGH);
digitalWrite(pinBuzzer, HIGH);
if (millis() - emergencyStartMillis < 5000) {
digitalWrite(pinActuatorUp, HIGH);
statusText = "EMERGENCY UP";
} else {
digitalWrite(pinActuatorUp, LOW);
if (sensorError) {
statusText = "ERR: SENSOR";
} else {
statusText = "EMERGENCY LOCK";
}
}
}
else {
if (currentPressState == PRESS_IDLE) {
if (currentTemp >= (float)targetTemp && currentTemp <= (float)maxTemp) {
statusText = "OK";
} else {
statusText = "NOT READY";
}
}
}
}
// ==========================================
// БЛОК №9: ОНОВЛЕННЯ ДИСПЛЕЯ
// ==========================================
void displayUpdate() {
static unsigned long lastDisplayTime = 0;
if (millis() - lastDisplayTime < 100) return;
lastDisplayTime = millis();
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Temp: ");
if (currentTemp <= -100.0) {
display.print("Err");
} else {
display.print(currentTemp, 1);
display.print(" C");
}
display.setCursor(0, 10);
display.print("SYS: "); display.print(statusText);
display.setCursor(0, 20);
if (menuState && menuIndex == 1) display.print("> ");
display.print("Range: "); display.print(targetTemp);
display.print("-"); display.print(maxTemp); display.print(" C");
display.setCursor(0, 30);
if (menuState && menuIndex == 2) display.print("> ");
display.print("Press Time: "); display.print(pressTime); display.print(" s");
display.setCursor(0, 42);
display.print("Total Cycles: ");
display.print(cycleCount);
display.setCursor(0, 54);
if (menuState) {
display.print("MENU: EDIT ");
display.print(menuIndex == 1 ? "TEMP" : "TIME");
} else {
display.print("MENU: MONITOR MODE");
}
display.display();
}
// ===================================================================
// БЛОК №10: ПІДПРОГРАМА СТАТИСТИКИ ТА РОБОТИ З EEPROM
// ===================================================================
#include <EEPROM.h>
const int eepromAddr = 0;
void initStatistics() {
unsigned int savedCycles = 0;
EEPROM.get(eepromAddr, savedCycles);
if (savedCycles == 65535) {
cycleCount = 0;
EEPROM.put(eepromAddr, cycleCount);
} else {
cycleCount = savedCycles;
}
}
void updateStats() {
cycleCount++;
EEPROM.put(eepromAddr, cycleCount);
}