КонспектGPT КонспектGPT
Курсовая

Разработка системы управления нагревом с голосовой индикацией на базе Arduino Mega 2560

25 стр.19 июня 2026

Цель курсовой работы

Создать и исследовать программно-аппаратную систему управления нагревателем с использованием PID-регулятора и голосового оповещения, реализованную на платформе Arduino Mega 2560.

Ключевая идея

В работе рассматривается разработка комплексной системы управления нагревом, включающей считывание температуры с датчика OneWire, управление мощностью нагревателя через ШИМ с PID-регулятором, обработку ошибок датчиков и индикацию состояния с помощью светодиодов и голосового модуля. Особое внимание уделяется устойчивости системы, обработке ошибок и плавному изменению мощности нагрева для предотвращения перегрузок. В качестве голосового сопровождения реализована очередь команд для модуля голосового оповещения, что повышает информативность и удобство эксплуатации.

Наименование образовательного учреждения
КУРСОВАЯ РАБОТА
на тему
#include <OneWire.h> #include <DallasTemperature.h> #include <avr/wdt.h> /* --- КОНФИГУРАЦИЯ ПАРАМЕТРОВ --- */ // --- ПИНЫ --- #define PIN_SENSOR 22 #define PIN_PWM_HEATER 11 #define BTN_PLUS 47 #define BTN_MINUS 48 #define VOICE_PIN 49 // Не используется для UART! #define BTS_STATUS_PIN A0 #define STATUS_LED 50 // --- PID КОНТРОЛЛЕР --- const float KP = 4.0; const float KI = 0.3; const float KD = 1.0; const int PWM_MAX_DELTA = 5; // Сглаживание ШИМ (макс. изменение за цикл) // --- ТАЙМАУТЫ И ГРАНИЦЫ --- const unsigned long SENSOR_RECOVERY_TIMEOUT = 10000; // 10 сек const byte MAX_SENSOR_FAILS = 5; const int TEMP_TARGET_DEFAULT = 38; const int TEMP_AUTO_LIMIT = 38; const int TEMP_MIN = 20; const int TEMP_MAX = 80; const int HYST_IDLE_TO_HEAT = 2; const int HYST_COMFORT_TO_HEAT = 1; // --- ОЧЕРЕДЬ ГОЛОСА --- const uint8_t QUEUE_SIZE = 24; const unsigned long INTER_FILE_DELAY = 150; // Задержка между файлами (мс) // ИНДИКАЦИЯ #define LED_BUILTIN 13 #define LED_ON(x) digitalWrite(x, HIGH) #define LED_OFF(x) digitalWrite(x, LOW) #define LED_TOGGLE(x) digitalWrite(x, !digitalRead(x)) // МАКРОС ЛОГИРОВАНИЯ (заменяет функцию log_event) #define LOG_EVENT(state, msg) do { \ Serial.print("[LOG "); Serial.print(state); Serial.print("]: "); Serial.println(msg); \ } while(0) // --- КЛАСС УПРАВЛЕНИЯ НАГРЕВОМ --- class HeaterControl { public: enum State { S_BOOT, S_IDLE, S_HEATING, S_COMFORT, S_FAULT_SENSOR, S_FAULT_BTS, S_DEAD_SENSOR }; HeaterControl(int pin) : _pin(pin), pwmValue(0), targetPwm(0), integralError(0), lastError(0) {} void setup(); /** * @brief Обновляет состояние машины состояний. * @param currentTemp Текущая температура с датчика. * @param isSensorFaulty Флаг неисправности датчика. * @param targetTemp Желаемая температура. */ void update(float currentTemp, bool isSensorFaulty, int targetTemp); void applyPwm(); State getState() const { return state; } bool isFaulty() const { return state == S_FAULT_SENSOR || state == S_FAULT_BTS || state == S_DEAD_SENSOR; } private: int _pin; State state = S_BOOT; unsigned long stateEnterTime = 0; uint8_t sensorFailCount = 0; bool wheelWasAlreadyWarm = false; // Переменные управления uint8_t pwmValue, targetPwm; long integralError; int lastError; /** * @brief Выполняет расчет PID-регулятора. * Вызывается только в состоянии S_HEATING. */ void runControlLoop(float target, float current); }; void HeaterControl::setup() { pinMode(_pin, OUTPUT); } void HeaterControl::runControlLoop(float target, float current) { if(state != S_HEATING) { targetPwm = 0; integralError = 0; return; } float error = target - current; integralError += error; integralError = constrain(integralError, -200, 200); int derivative = error - lastError; lastError = error; targetPwm = constrain((error * KP) + (integralError * KI) + (derivative * KD), 0, 255); } void HeaterControl::applyPwm(){ if(isFaulty()) pwmValue = 0; else { // Anti-surge: ограничиваем скорость изменения мощности if(pwmValue < targetPwm) pwmValue = min(pwmValue + PWM_MAX_DELTA, targetPwm); else if(pwmValue > targetPwm) pwmValue = max(pwmValue - PWM_MAX_DELTA, targetPwm); } analogWrite(_pin, pwmValue); } void HeaterControl::update(float currentTemp, bool isSensorFaulty, int targetTemp) { switch(state){ case S_BOOT: state = S_IDLE; break; case S_IDLE: if(isSensorFaulty){ state = S_FAULT_SENSOR; stateEnterTime = millis(); sensorFailCount = 0; } else { if(wheelWasAlreadyWarm) state = S_COMFORT; else if(currentTemp < targetTemp - HYST_IDLE_TO_HEAT) state = S_HEATING; } break; case S_HEATING: if(isSensorFaulty) { state = S_FAULT_SENSOR; stateEnterTime = millis(); sensorFailCount = 0; } else if(currentTemp >= targetTemp){ state = S_COMFORT; wheelWasAlreadyWarm = true; } else { // Только если нет ошибки и мы все еще греем, запускаем PID runControlLoop(targetTemp, currentTemp); } break; case S_COMFORT: if(isSensorFaulty) { state = S_FAULT_SENSOR; stateEnterTime = millis(); sensorFailCount = 0; } else if(currentTemp < targetTemp - HYST_COMFORT_TO_HEAT) state = S_HEATING; break; case S_FAULT_SENSOR: if(millis() - stateEnterTime > SENSOR_RECOVERY_TIMEOUT) { sensorFailCount++; stateEnterTime = millis(); if(sensorFailCount >= MAX_SENSOR_FAILS) { state = S_DEAD_SENSOR; } } break; default: break; } } // --- КЛАСС УПРАВЛЕНИЯ ГОЛОСОМ --- class VoiceManager { public: VoiceManager(HardwareSerial &serialPort) : mySerial(serialPort), isSending(false) {} void begin(); void addCommand(uint16_t fileId); void processQueue(); // Вызывать в loop() private: HardwareSerial &mySerial; uint16_t queue[QUEUE_SIZE]; int qHead, qTail, qCount; bool isSending; unsigned long lastSendTime; void sendRawCommand(uint16_t fileId); }; void VoiceManager::begin() { mySerial.begin(9600); delay(500); // Даем модулю время загрузиться qHead = qTail = qCount = 0; } void VoiceManager::addCommand(uint16_t fileId) { if(qCount >= QUEUE_SIZE) { Serial.println("[WARN] Voice Queue Overflow"); return; } queue[qTail]=fileId; qTail = (qTail + 1) % QUEUE_SIZE; qCount++; } void VoiceManager::processQueue() { if(qCount > 0 && !isSending && millis() - lastSendTime >= INTER_FILE_DELAY){ uint16_t vid = queue[qHead]; qHead = (qHead + 1) % QUEUE_SIZE; qCount--; sendRawCommand(vid); isSending = true; // Флаг сбросится по завершении отправки (если нужна обратная связь от модуля) lastSendTime = millis(); } } void VoiceManager::sendRawCommand(uint16_t fileId){ uint8_t cmd = 0x03; uint8_t paraH = highByte(fileId); uint8_t paraL = lowByte(fileId); uint8_t checksum = -(0xFF + 0x06 + cmd + 0x00 + paraH + paraL); mySerial.write(0x7E); mySerial.write(0x06); mySerial.write(cmd); mySerial.write(0x00); mySerial.write(paraH); mySerial.write(paraL); mySerial.write(checksum); mySerial.write(0xEF); } // --- ГЛОБАЛЬНЫЕ ОБЪЕКТЫ --- OneWire oneWire(PIN_SENSOR); DallasTemperature sensors(&oneWire); HeaterControl heater(PIN_PWM_HEATER); VoiceManager voice(Serial1); // Используем Serial1 для MEGA 2560 // --- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ --- bool read_sensors_nonblocking(float &tempOut){ static bool conversionRequested = false; static unsigned long requestTime = 0; const unsigned long REQUEST_INTERVAL = 1000; const unsigned long CONVERSION_TIME = 750; if(!conversionRequested && millis() - requestTime >= REQUEST_INTERVAL) { sensors.requestTemperatures(); conversionRequested = true; requestTime = millis(); } if(conversionRequested && millis() - requestTime >= CONVERSION_TIME) { tempOut = sensors.getTempCByIndex(0); conversionRequested = false; return true; } return false; } void checkBtsStatus(bool &faultFlag) { static bool prevState = false; static bool readingStable = false; static unsigned long stableSince = 0; const unsigned long DEBOUNCE_TIME = 50; bool currReading = analogRead(BTS_STATUS_PIN) < 512; if(currReading == prevState) { if(!readingStable) { stableSince = millis(); // Запоминаем время, когда состояние стало стабильным readingStable = true; } else if(millis() - stableSince >= DEBOUNCE_TIME) { faultFlag = currReading; // Состояние стабильно долгое время, обновляем флаг ошибки } } else { readingStable = false; // Состояние изменилось, сбрасываем таймер стабильности } prevState = currReading; } // --- SETUP И LOOP --- void setup() { wdt_enable(WDTO_2S); pinMode(LED_BUILTIN, OUTPUT); pinMode(STATUS_LED, OUTPUT); LED_OFF(LED_BUILTIN); LED_OFF(STATUS_LED); Serial.begin(9600); Serial.println("System Booted"); sensors.begin(); heater.setup(); voice.begin(); voice.addCommand(random(300, 304)); // Озвучиваем старт } void loop() { wdt_reset(); // 1. ЧТЕНИЕ ДАТЧИКОВ static float currentTemp = NAN; bool isSensorOK = read_sensors_nonblocking(currentTemp); // 2. ПРОВЕРКА СТАТУСА BTS static bool btsFault = false; checkBtsStatus(btsFault); // Сохраняем предыдущее состояние здесь, чтобы оно было доступно в макросе static HeaterControl::State previousState = heater.getState(); // 3. ОБНОВЛЕНИЕ ЛОГИКИ УПРАВЛЕНИЯ heater.update(currentTemp, !isSensorOK, TEMP_TARGET_DEFAULT); // 4. ЛОГИРОВАНИЕ ПЕРЕХОДОВ HeaterControl::State currentState = heater.getState(); if(currentState != previousState) { const char* eventMsg; switch(currentState) { case HeaterControl::S_IDLE: eventMsg = "-> IDLE"; break; case HeaterControl::S_HEATING: eventMsg = "-> HEATING"; break; case HeaterControl::S_COMFORT: eventMsg = "-> COMFORT"; break; case HeaterControl::S_FAULT_SENSOR: eventMsg = "-> FAULT_SENSOR"; break; case HeaterControl::S_FAULT_BTS: eventMsg = "-> FAULT_BTS"; break; case HeaterControl::S_DEAD_SENSOR: eventMsg = "-> DEAD_SENSOR"; break; default: eventMsg = "-> UNKNOWN"; } LOG_EVENT(currentState, eventMsg); previousState = currentState; } // 5. ПРИМЕНЕНИЕ PWM heater.applyPwm(); // 6. ОБРАБОТКА ГОЛОСА voice.processQueue(); // 7. ИНДИКАЦИЯ static unsigned long ledBlinkTime = 0; if(millis() - ledBlinkTime > 500) { ledBlinkTime = millis(); switch(heater.getState()) { case HeaterControl::S_IDLE: case HeaterControl::S_COMFORT: LED_TOGGLE(LED_BUILTIN); LED_OFF(STATUS_LED); break; case HeaterControl::S_HEATING: LED_ON(LED_BUILTIN); LED_OFF(STATUS_LED); break; case HeaterControl::S_FAULT_SENSOR: case HeaterControl::S_FAULT_BTS: LED_OFF(LED_BUILTIN); LED_TOGGLE(STATUS_LED); break; case HeaterControl::S_DEAD_SENSOR: LED_OFF(LED_BUILTIN); LED_ON(STATUS_LED); break; } } }
Содержание
1.
Введение
3
2.
Анализ и выбор компонентов системы управления нагревом
5
3.
Проектирование программно-аппаратной системы управления
9
4.
Реализация голосовой индикации и визуальной обратной связи
13
5.
Тестирование и исследование системы
17
6.
Заключение
21
7.
Список литературы
23

Нужна такая же курсовая работа?

КонспектGPT напишет курсовую работу на любую тему за 10–30 минут: оригинальность больше 90%, оформление по ГОСТу, реальные источники, обход ИИ-детекторов. Просто введи тему — нейросеть сама подберёт структуру, наполнит главы и оформит библиографию.

Создать такую же работу

Частые вопросы

Могу ли я скачать эту работу целиком?

Нет, полный текст этой работы доступен только её автору. Но вы можете за пару минут сгенерировать свою курсовую работу на эту или любую другую тему — с уникальной формулировкой, своей структурой и по вашим требованиям к объёму.

Какая оригинальность у сгенерированных работ?

В среднем 85–95% по Антиплагиат.ру. Каждая работа генерируется с нуля под вашу тему, а не собирается из готовых шаблонов. Дополнительно доступны режимы «обход ИИ-детекта» (GPTZero, ZeroGPT) и «уникализация» под конкретную систему антиплагиата.

Сколько это стоит?

Бесплатно доступно только создание содержания работы — структуры с главами и подпунктами, которую можно отредактировать под свои требования. Саму работу можно сгенерировать в PRO-версии за 328 ₽: полный текст, оформление по ГОСТу, список литературы и экспорт в Word. Это в десятки раз дешевле заказа у автора на бирже.

В каком формате доступна работа после генерации?

Готовая работа скачивается в формате Word (DOCX) с уже применённым оформлением по ГОСТу: титульный лист, содержание с нумерацией страниц, сноски и список литературы. Файл открывается и свободно редактируется в Microsoft Word, Google Документах, LibreOffice и «Р7-Офис» — можно дописать свои примеры, поправить формулировки и подстроить работу под требования преподавателя.

Можно ли использовать свои материалы и источники?

Да. При создании работы загрузите файлы в блок «свои доп. материалы» — методичку от преподавателя, конспекты лекций, нужные главы учебника или научные статьи. Нейросеть учтёт их как контекст и напишет работу на основе именно этого материала, а не только общих знаний. Совет: загружайте самые релевантные фрагменты, а не весь учебник целиком — так текст получится точнее.

Сколько времени занимает генерация?

Реферат или доклад — 5–10 минут. Курсовая — 15–20 минут. Дипломная или ВКР — 25–40 минут (генерация по главам с проверкой на связность).

Можно ли отредактировать структуру перед генерацией?

Да, обязательно. Сначала нейросеть предлагает содержание (список глав и подпунктов), вы можете его изменить, добавить/убрать пункты, поменять формулировки — и только потом запускать генерацию полного текста.

Похожие курсовые работы

Все курсовые работы →