#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h> // 加载SPI库
#include <PID_v1.h> // 引入 PID 库
// NTC 热敏电阻相关参数
const float BETA = 3950.0; // NTC 的 Beta 值
const int analogPin1 = A0; // 第一个 NTC 传感器(上片)
const int analogPin2 = A1; // 第二个 NTC 传感器(下片)
const int buttonPin = 2; // 按钮输入引脚
const int relayPin1 = 3; // 继电器1 控制上片加热片
const int relayPin2 = 4; // 继电器2 控制下片加热片
const int fanPin = 5; // 风扇控制引脚(继电器或电机控制)
// 引脚定义
const int button1Pin = 10; // 控制电机1的按钮
const int button2Pin = 11; // 控制电机2的按钮
// 电机1控制引脚(方向/脉冲/使能)
const int motor1DirPin = 12;
const int motor1StepPin = 13;
const int motor1EnablePin = 6;
// 电机2控制引脚(方向/脉冲/使能)
const int motor2DirPin = 7;
const int motor2StepPin = 8;
const int motor2EnablePin = 9;
// 电机参数
const int STEPS_PER_REV = 200; // 每转步数
unsigned long stepDelay = 50; // 步进脉冲间隔(微秒)
// 电机状态机
enum MotorState { IDLE, RUNNING, DELAY };
MotorState motor1State = IDLE;
MotorState motor2State = IDLE;
// 电机控制变量
int motor1PressCount = 0; // 按钮1按下计数
long motor1TargetSteps = 0; // 目标步数
unsigned long motor1DelayStart = 0;
bool motor2Direction = false; // 电机2方向
long motor2TargetSteps = 0;
unsigned long motor2DelayStart = 0;
// 按钮防抖参数
unsigned long debounceDelay = 50;
unsigned long lastDebounce1 = 0;
unsigned long lastDebounce2 = 0;
int lastButton1State = HIGH;
int lastButton2State = HIGH;
// OLED 显示器的 I2C 地址和尺寸
#define SSD1306_I2C_ADDRESS 0x3C
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
// PID 控制相关参数
double setpoint1 = 75.0; // 目标温度(上片)
double setpoint2 = 65.0; // 目标温度(下片)
double input1, output1; // 上片的温度输入和输出
double input2, output2; // 下片的温度输入和输出
// PID 控制器
PID pid1(&input1, &output1, &setpoint1, 2.0, 5.0, 1.0, DIRECT); // 上片 PID
PID pid2(&input2, &output2, &setpoint2, 2.0, 5.0, 1.0, DIRECT); // 下片 PID
// 状态标识
enum State {
PREPARING,
HEATING,
HEATING_COMPLETE,
COOLING
};
State currentState = PREPARING;
unsigned long heatStartTime = 0; // 加热开始时间
unsigned long heatingDuration = 1200000; // 恒温时间(20分钟)
unsigned long heatingElapsedTime = 0; // 已经加热的时间
unsigned long heatingCompleteStartTime = 0; // 恒温开始时间
bool heatingCompleteFlag = false; // 恒温是否开始标志
bool heatingFlag = false; // 标记是否进入恒温阶段
// 温度控制阈值:偏差不得大于0.5°C
const float tempThreshold = 0.5;
void setup() {
Serial.begin(9600);
// 初始化按钮引脚
pinMode(button1Pin, INPUT_PULLUP);
pinMode(button2Pin, INPUT_PULLUP);
// 初始化电机控制引脚
pinMode(motor1DirPin, OUTPUT);
pinMode(motor1StepPin, OUTPUT);
pinMode(motor1EnablePin, OUTPUT);
pinMode(motor2DirPin, OUTPUT);
pinMode(motor2StepPin, OUTPUT);
pinMode(motor2EnablePin, OUTPUT);
// 初始禁用电机
digitalWrite(motor1EnablePin, HIGH);
digitalWrite(motor2EnablePin, HIGH);
//初始化温度模块按钮引脚
pinMode(buttonPin, INPUT_PULLUP);
pinMode(relayPin1, OUTPUT);
pinMode(relayPin2, OUTPUT);
pinMode(fanPin, OUTPUT);
digitalWrite(relayPin1, LOW);
digitalWrite(relayPin2, LOW);
digitalWrite(fanPin, LOW);
// 初始化 OLED 显示屏
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
} else {
Serial.println(F("OLED initialized successfully"));
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Preparing...");
display.display();
delay(2000);
// PID 初始化
pid1.SetMode(AUTOMATIC); // 上片 PID 自动控制
pid1.SetOutputLimits(0, 255); // 输出限制(0到255)
pid2.SetMode(AUTOMATIC); // 下片 PID 自动控制
pid2.SetOutputLimits(0, 255); // 输出限制(0到255)
}
void loop() {
handleMotor1Button();
handleMotor2Button();
// 处理电机1运动
static unsigned long lastStep1 = 0;
if(motor1State == RUNNING){
if(micros() - lastStep1 >= stepDelay){
digitalWrite(motor1StepPin, !digitalRead(motor1StepPin)); // 切换脉冲信号
if(digitalRead(motor1StepPin)){ // 只在上升沿计数
motor1TargetSteps--;
}
lastStep1 = micros();
if(motor1TargetSteps <= 0){
digitalWrite(motor1EnablePin, HIGH); // 禁用驱动器
motor1State = DELAY;
motor1DelayStart = millis();
}
}
}
else if(motor1State == DELAY && (millis() - motor1DelayStart) >= 500){
motor1State = IDLE;
}
// 处理电机2运动
static unsigned long lastStep2 = 0;
if(motor2State == RUNNING){
if(micros() - lastStep2 >= stepDelay){
digitalWrite(motor2StepPin, !digitalRead(motor2StepPin));
if(digitalRead(motor2StepPin)){
motor2TargetSteps--;
}
lastStep2 = micros();
if(motor2TargetSteps <= 0){
digitalWrite(motor2EnablePin, HIGH);
motor2State = DELAY;
motor2DelayStart = millis();
}
}
}
else if(motor2State == DELAY && (millis() - motor2DelayStart) >= 500){
motor2State = IDLE;
}
int analogValue1 = analogRead(analogPin1); // 读取第一个传感器
int analogValue2 = analogRead(analogPin2); // 读取第二个传感器
// 使用NTC公式计算温度
float temp1 = getTemperature(analogValue1); // 上片温度
float temp2 = getTemperature(analogValue2); // 下片温度
// 读取按钮状态
static bool lastButtonState = HIGH;
bool buttonState = digitalRead(buttonPin);
if (lastButtonState == HIGH && buttonState == LOW) {
currentState = HEATING;
heatStartTime = millis();
display.clearDisplay();
display.setCursor(0, 0);
display.print("Heating...");
display.display();
}
lastButtonState = buttonState;
// 根据当前状态更新显示信息
switch (currentState) {
case PREPARING:
display.clearDisplay();
display.setCursor(0, 0);
display.print("Ready...");
display.display();
break;
case HEATING:
// 更新 PID 输入并进行控制计算
input1 = temp1;
input2 = temp2;
pid1.Compute(); // 计算上片 PID 输出
pid2.Compute(); // 计算下片 PID 输出
// 控制上片加热器
if (output1 > 128) {
digitalWrite(relayPin1, HIGH); // 上片加热器开启
} else {
digitalWrite(relayPin1, LOW); // 上片加热器关闭
}
// 控制下片加热器
if (output2 > 128) {
digitalWrite(relayPin2, HIGH); // 下片加热器开启
} else {
digitalWrite(relayPin2, LOW); // 下片加热器关闭
}
// 显示当前温度
display.clearDisplay();
display.setCursor(0, 0);
display.print("Heating...");
display.setCursor(0, 10);
display.print("T1: ");
display.print(temp1);
display.print("C ");
display.setCursor(0, 20);
display.print("T2: ");
display.print(temp2);
display.print("C");
display.display();
// 判断是否达到目标温度
if (abs(temp1 - setpoint1) <= tempThreshold && abs(temp2 - setpoint2) <= tempThreshold) {
// 如果两个温度都在目标温度范围内,进入恒温状态
currentState = HEATING_COMPLETE;
heatingCompleteStartTime = millis(); // 记录恒温开始时间
display.clearDisplay();
display.setCursor(0, 0);
display.print("Heating...");
display.setCursor(0, 10);
display.print("Maintaining temperature");
display.display();
}
break;
case HEATING_COMPLETE:
heatingElapsedTime = millis() - heatingCompleteStartTime;
unsigned long remainingTime = heatingDuration - heatingElapsedTime;
unsigned long minutesLeft = remainingTime / 60000; // 剩余分钟数
unsigned long secondsLeft = (remainingTime % 60000) / 1000; // 剩余秒数
// 如果恒温时间超过设定时间,开始冷却过程
if (heatingElapsedTime >= heatingDuration) {
currentState = COOLING;
digitalWrite(relayPin1, LOW); // 停止加热
digitalWrite(relayPin2, LOW); // 停止加热
display.clearDisplay();
display.setCursor(0, 0);
display.print("Cooling down...");
display.display();
} else {
// 显示剩余恒温时间
display.clearDisplay();
display.setCursor(0, 0);
display.print("Heating complete");
display.setCursor(0, 10);
display.print("Time remaining: ");
display.print(minutesLeft);
display.print(":");
if (secondsLeft < 10) display.print("0");
display.print(secondsLeft);
display.display();
}
break;
case COOLING:
// 控制风扇开启,进行冷却
digitalWrite(fanPin, HIGH); // 启动风扇冷却
// 显示冷却信息
display.clearDisplay();
display.setCursor(0, 0);
display.print("Cooling...");
display.setCursor(0, 1);
display.print("T1: ");
display.print(temp1);
display.print("C ");
display.setCursor(8, 1);
display.print("T2: ");
display.print(temp2);
display.print("C");
display.display();
break;
}
delay(1000); // 延时 1 秒钟
}
// 根据NTC电阻值计算温度(使用常用的NTC公式)
float getTemperature(int analogValue) {
float resistance = (1023.0 / analogValue - 1.0) * 10000.0; // 计算NTC电阻值
float temperature = 1.0 / (log(resistance / 10000.0) / BETA + 1.0 / 298.15) - 273.15; // 根据NTC公式计算温度
return temperature;
}
void handleMotor1Button(){
int reading = digitalRead(button1Pin);
// 防抖处理
if(reading != lastButton1State){
lastDebounce1 = millis();
}
if((millis() - lastDebounce1) > debounceDelay){
// 检测下降沿且电机空闲
if(reading == LOW && lastButton1State == HIGH && motor1State == IDLE){
motor1PressCount = (motor1PressCount % 3) + 1; // 循环1-3
// 设置转动参数
switch(motor1PressCount){
case 1:
case 2:
digitalWrite(motor1DirPin, LOW); // 正转
motor1TargetSteps = 5 * STEPS_PER_REV;
break;
case 3:
digitalWrite(motor1DirPin, HIGH); // 反转
motor1TargetSteps = 10 * STEPS_PER_REV;
break;
}
digitalWrite(motor1EnablePin, LOW); // 启用驱动器
motor1State = RUNNING;
}
}
lastButton1State = reading;
}
void handleMotor2Button(){
int reading = digitalRead(button2Pin);
if(reading != lastButton2State){
lastDebounce2 = millis();
}
if((millis() - lastDebounce2) > debounceDelay){
if(reading == LOW && lastButton2State == HIGH && motor2State == IDLE){
motor2Direction = !motor2Direction; // 切换方向
digitalWrite(motor2DirPin, motor2Direction);
motor2TargetSteps = 25 * STEPS_PER_REV;
digitalWrite(motor2EnablePin, LOW); // 启用驱动器
motor2State = RUNNING;
}
}
lastButton2State = reading;
}