基于ESP32 WiFi实现的恒温自动控制系统
实现一个恒温自动控制系统,通过电流给IO扩展板上加温电阻加热,并通过温度传感器感知板上温度的变化,并在LCD屏上显示温度。用旋转编码器设定目标温度,并且通过程序控制加热功率。温度偏离设定温度±3°C彩灯变为红色。
标签
嵌入式系统
MPU
显示
ESP32
2023寒假在家练
student
更新2023-03-27
南京邮电大学
748

项目描述

完成的项目要求:

项目4 - 实现一个恒温自动控制系统

  • IO扩展板上有一处加温电阻,将加热区域用物体(纸巾等)包裹起来,通过电流给电阻加热,并通过温度传感器感知板上温度的变化,测温以及在LCD屏上的温度显示。

要求:使用按键设定目标温度,并且通过程序控制加热功率,使得温度尽快尽量稳定的维持在目标温度。温度偏离设定温度±3°C彩灯变为红色。

(注意,加热电阻满占空比开启后温度较高)

 

根据项目的要求,实现一个恒温自动控制系统。通过电流给IO扩展板上加温电阻加热,并通过温度传感器感知板上温度的变化,并在LCD屏上显示温度及曲线。用旋转编码器设定目标温度,并且通过程序控制加热功率。温度偏离设定温度±3°C彩灯变为红色。

FoBQboa4ERzA5RWKzJb_6eF9Mkza

开发环境:Arduino 1.8.19

编程语言:C

FuJ7CNIH57Gy5G5dW9dReuSnV29-

本次实验的实验框图如上所示,以ESP32为核心,通过各种通讯方式与外设相互通信。其中温度传感器和旋转编码器为输入,LCD、加温电阻和LED为输出。

Fh6O7pQq78ARRl_iMAB06kwDygd4

本次设计的软件流程图如上所述,首先对各种外设进行初始化。通过定时器进行任务调度,包括读取温度,读取输入,根据输入执行相应的温度控制,将波形和数值通过LCD显示,并控制加温电阻和LED。

硬件介绍

ESP32-S2 是一款高度集成、高性价比、低功耗、主打安全的单核 Wi-Fi SoC,具备强大的功能和丰富的 IO 接口。使用乐鑫ESP-IF开发环境,我们可以通过USB对其编程,作为带wifi的MCU单独使用,也可以烧录AT固件,作为WiFi透传模块与RP2040游戏机套件结合使用。

ESP32-S2 WiFi模块是物联网、可穿戴电子设备和智能家居等应用场景的理想选择,另搭配输入控制、输出显示以及传感器感知和控制的套件,使其功能更加完善。

配套的ESP32 S2 开发板除了ESP32wifi模组之外还集成了USB TYPE -C接口,两个按键,一个电源指示灯,一个用户LED灯,2排10pin的排针,将重要IO引出。使用USB供电或通过排针3.3V供电。

本扩展板包含如下功能:

  • 按键、旋转编码器输入 - 以模拟信号的方式
  • 双电位计控制输入 - 以数字信号的方式
  • RGB三色LED显示
  • 1.44寸128*128 LCD,SPI总线访问
  • MMA7660三轴姿态传感器
  • 电阻加热
  • 温度传感器
  • 与ESP32-S2核心模块的接口

Fm2D9uG9ctxBtMjxIbZFGc4tRnhl

实现的功能

PWM控加温电阻

加温电阻的原理为,通过V_HEAT控制mos管的开关,从而控制加温电阻的电流的通断。当mos管导通时,电阻由于电流导通发热,温度升高;当模式管关断时,电阻上没有流经电流,电阻的温度受热辐射的影响,温度降低,向环境温度靠近。

V_HEAT的预留接口在J2 MSP430_IF上,需要用杜邦线将其与J6 STEP_IF(NP)的CA_PWD管脚相连接。

当当前温度低于设定温度时,PWM占空比根据温度差设定,加温电阻升温;当当前温度高于环境温度时,PWM占空比为0,相当于接地状态。

	int cal_val;
	if(meai<target){
		cal_val = (target-meai)*100;
		if(cal_val>255)
			cal_val = 255;
		analogWrite(ca_pwd, cal_val);
		// digitalWrite(ca_pwd, HIGH);
	}
	else{
		analogWrite(ca_pwd, 0);
		digitalWrite(ca_pwd, LOW);
	}

 

温度的控制最好通过PID算法进行控制,但是由于环境比较理想,要求的精度也不是很高,这里只用了P进行实现。

I2c温度读取温度

首先对测温电路进行研究,观察原理图可以看到,温度测量芯片的通信协议为I2C协议。

根据数据手册可以了解到,由于A0连接GND,所以设备地址为1001000。这也说明同一个I2C总线上最多只能挂载四个温度检测芯片。

DEVICE TWO-WIREADDRESS   A0 PIN CONNECTION
1001000   Ground
1001001   VDD
1001010   SDA
1001011   SCL

之后根据I2C的读写时序就可以进行通信了。其中指针寄存器的定义如下表所示,这里选择默认的温度寄存器。

BIT NO.   Name   Description
Bits 7:2   NA   P2 to P7mustalwaysbe 0 during the write command.
Bits 1:0   Pointer[1:0]   00:Temperature register(default)(R only)01: Configuration register(R/w)
                                    10: TLowregister(R/W)
                                    11: THIGH register(R/W)

这里采用的是12bit编码,最低位的单位是0.0625,即T0的单位是0.0625,T4的单位是1,而所以需要对第二个字节的读数除以256。

float readTemperaturec(){
    float temp = 0;
    uint16_t t = 0;

    Wire.beginTransmission(NTS112_ADDRESS);
    Wire.write(0x00);
    Wire.endTransmission();
    Wire.requestFrom((uint16_t)NTS112_ADDRESS, (uint8_t)2);
    t = Wire.read();
    temp = (float)t;
    t = Wire.read();
    temp +=(float)t / 256.00;
    Wire.endTransmission();

    return temp;
}

 

I2C的实现代码参考群里的一位大佬,由于聊天记录找不到了,无法特别致谢,在此表示抱歉。

 

旋转编码器设定温度

首先分析旋转编码器的原理,可以看到,他的输出接的是ESP32的模拟IO口。这里可以根据电阻网络分析是哪一个按键按下。

这里直接用ADC去读取相应的值,然后进行分析。结果如下:

静态误差小于100;

旋转误差大于300;

旋转按键大约1200;

按键大约4900。

旋转编码器按键切换调整

由于旋转编码器左旋和右旋的差异很小,尽管可以通过提高ADC的采样频率的方式来具体分析出旋转的方式,但是为了简化开发流程,这里选择使用按键来切换温度的加减调试,而不是通过左旋和右旋。也就是说,在某一个确定的模式下,左旋和右旋的调整结果是一致的,只有当按键按下以后,才会切换调整的方向。

int myinput() {
  	int res = 0;
//   int ana_t0 = 0;
//   int ana_t1 = 0;
  	int diff = 0;

	ana_t0 = ana_t1;
  	ana_t1 = analogRead(A0);
  	diff = ana_t0 - ana_t1;

  if (diff > 4000)
    res = BUTT;
  else if (diff > 1000)
    res =  PRES;
  else if (diff > 300)
    res =  REVO;
  else if (diff > 300 || (diff < -300 && diff > -1000))
    res =  REVO;
  else
    res =  0;

  return res;
}

 

根据观察的规律对ADC的采样值进行判断,差值决定哪个外设有效。

旋转编码器的左转和右转实际上波形是不一致的,但是由于我采用的采样率较低,所以没有进行区分。

	int meai = 0;
	meai = ((int)(mea*100))/100;

	if(Timer_fun_flag == true){
		mea = readTemperaturec();	// read temperature
		in1 = myinput();			// input detection
	}

	switch(in1){
		case BUTT:
		break;
		case PRES:
			dire_rota = !dire_rota;
			// target = target_temp;
		break;
		case REVO:
			if(dire_rota)
				target_temp = target_temp + 1;
			else
				target_temp = target_temp - 1;

			if(target_temp > 100)
				target_temp = 100;
			else if(target_temp < 0)
				target_temp = 0;
		break;
		default:
		break;
	}

 

通过旋转调整要设定的温度。

延时确认温度设置

温度调整完以后,需要确定调整后的温度为目标温度。这里本来想通过按键进行相应的确定,后来将方案更改为延时确定。只要一定时间内温度不再变化,那么当前的调整温度会同步为目标温度。当然也可以采用实时确定的方式,但是我感觉那样不优雅。

	if(set_val_flag == true){
		set_val_flag = false;
		if(target_temp_temp != target_temp)
			target_temp_temp = target_temp;
		else
			target = target_temp;
	}

     

通过定时器产生flag信号。若当前设定温度和实际设定温度不一致,则更新实际设定温度。

 

Spi LCD屏显示温度

LCD的开发花费了很大的功夫,一开始采用的是Adafruit的ST7735的库,但是由于开发板的原理图设计为软件SPI,而软件SPI由于Arduino的开发方式下速度很慢,导致屏幕刷新率低,一直看到屏幕在闪烁,所以之后切换为TFT_eSPI库。

在显示上面,按照题目的要求在LCD上显示温度。为了能够更加好看一点,所以采用了双温度曲线的方案,既显示设定的温度改变曲线和当前设定温度,也显示实际温度曲线和当前的实际温度。

int i = 0;

   if(disp_flag == true){
      disp_flag = false;
  		// update
  		temp_real[ARR_LEN-1] = meai;
  		temp_set[ARR_LEN-1] = target;
  		for(i=0; i<ARR_LEN-1; i++){
  			temp_real[i] = temp_real[i+1];
  			temp_set[i] = temp_set[i+1];
  		}
   }

		uint16_t transparent = 0;
		img.createSprite(128, 128);					// then create the sprite
		img.setColorDepth(8);         	// Set colour depth first
		img.fillSprite(0);

		img.setTextColor(TFT_GREEN);
		img.setCursor(5, 0);
		img.print(mea);

		img.setCursor(45, 0);
		img.setTextColor(TFT_MAGENTA);
		img.println(target_temp);

		img.fillCircle(3, 124-temp_real[0], CIR_RDS, TFT_GREEN);
		img.drawCircle(3, 124-temp_set[0], 	CIR_RDS, TFT_MAGENTA);
		for(i=1; i<ARR_LEN; i++){
			img.fillCircle(3+i*SPACED, 124-temp_real[i], CIR_RDS, TFT_GREEN);
			img.drawCircle(3+i*SPACED, 124-temp_set[i], 	CIR_RDS, TFT_MAGENTA);
			img.drawLine(3+(i-1)*SPACED, 124-temp_real[i-1], 	3+i*SPACED, 124-temp_real[i], TFT_GREEN);
			img.drawLine(3+(i-1)*SPACED, 124-temp_set[i-1], 	3+i*SPACED, 124-temp_set[i], TFT_MAGENTA);
		}
  // Push sprite to TFT screen CGRAM at coordinate x,y (top left corner)
  // Specify what colour is to be treated as transparent (black in this example)
  img.pushSprite(0, 0);
  // Delete Sprite to free memory, creating and deleting takes very little time.
  img.deleteSprite();
	// }

 

每次显示之前更新最新的数据。实际温度用绿色显示,曲线为绿色实点。设定温度为紫色显示,曲线为紫色虚点。

RGB彩灯点亮

RGB的点亮由高低电平驱动。根据原理图可以分析得到,三个灯都是低电平有效的,可以采用低电平的驱动方式。

	if(meai-target>3 || target-meai>3){
		digitalWrite(led3, LOW);
	}
	else{
		digitalWrite(led3, HIGH);
	}

 

当测量温度和设定温度的差距大于三度时,点亮红色LED,否则关闭红色LED。

Fl7ocV-sdZHnpdwy2je9szltQDMi

定时器中断

为了实现中断的效果,采用定时器中断的方式。

void Timer_fun(){
	Timer_fun_flag = true;
	
	cnt_tim_disp++;
	cnt_tim_set_val++;
	if(cnt_tim_disp>=10){
		cnt_tim_disp = 0;
		disp_flag = true;
	}

	if(cnt_tim_set_val>=10){
		cnt_tim_set_val = 0;
		set_val_flag = true;
	}
}

定时器的功能只用来计数和生成标志位,最终的功能实现仍由主函数实现。

 

总结

在设计中遇到的主要问题是LCD的刷新。一开始采用的是Adafruit的ST7735的库,但是由于开发板的原理图设计为软件SPI,而软件SPI由于Arduino的开发方式下速度很慢,导致屏幕刷新率低,一直看到屏幕在闪烁,所以之后切换为TFT_eSPI库。

 

在本次设计过程中,存在着一些妥协,很多功能由于时间关系没有实现的特别完美,是后续的改善方向。

1、本来希望用esp的工具进行开发的,但是在VSCODE中安装时产生了错误,多次尝试后均无果,最后只能选择Arduino的开发方式。有机会还是希望用原生的工具进行开发;

2、温度控制如果用PID可能曲线和精度会更加理想,需要进行进一步的开发;

3、旋转编码器的左旋右旋设定为一样的功能,可以通过提高采样精度来区分;

4、后续找相关示例,做一个开机动画的,将开机动画融入到主代码中。

致谢

感谢党和国家维护和平稳定的环境,感谢硬禾课堂提供的机会,感谢群内提供I2C例程和帮忙分析LCD刷新率的大佬们。

 

 

附件下载
exe1.ino
可下载编译的代码
团队介绍
南京邮电大学是国家工业和信息化部与江苏省人民政府共建的高校。 在长期的办学过程中,人才培养质量高、毕业生社会声誉好。
团队成员
student
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号