项目要求
本项目要求基于搭配带屏12指神探的传感器扩展板实现恒温控制系统。
- 至少使用一种自控算法控制,如PID等
- 可用按键或拨轮控制目标温度
- 可在LCD屏上显示目标温度及实时温度
板卡介绍
12指神探的传感器扩展板搭载了多款常见传感器和功能模块,包括麦克风、蜂鸣器、红外收发、霍尔效应开关、加热电阻等。为进阶操作准备了温湿度传感器、六轴传感器、接近/环境光/IR传感器、颜色传感器等。可拆卸的传感器模块可通过连接线延伸使用空间范围。提供了初学和进阶操作两种使用方式,支持直接获取数字或模拟量,以及通过编写协议进行数据交流。并且配备了240*240分辨率的LCD彩屏,两个可程控按键和一个拨轮,丰富了人机交互功能。白色外壳设计美观且方便使用。
实现思路
通过12指神探的传感器扩展板获取温度数据,利用加热电阻和PID自控算法实现恒温控制。将采集到的数据显示在带屏版的12指神探的LCD彩屏上,实现实时监测和人机交互。
实现框图
实现方法
- 将温湿度传感器模块连接至扩展板,并正确插入方向。
- 利用NSHT30获取温度数据。
首先,可以从 CSDN 上下载 NSHT30 的驱动。
from machine import I2C, Pin
import time
__version__ = '0.2.1'
__author__ = 'Roberto Sánchez'
__license__ = "Apache License 2.0. https://www.apache.org/licenses/LICENSE-2.0"
# I2C address B 0x45 ADDR (pin 2) connected to VDD
DEFAULT_I2C_ADDRESS = 0x44
class SHT30():
"""
SHT30 sensor driver in pure python based on I2C bus
References:
* https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf
* https://www.wemos.cc/sites/default/files/2016-11/SHT30-DIS_datasheet.pdf
* https://github.com/wemos/WEMOS_SHT3x_Arduino_Library
* https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/11_Sample_Codes_Software/Humidity_Sensors/Sensirion_Humidity_Sensors_SHT3x_Sample_Code_V2.pdf
"""
POLYNOMIAL = 0x131 # P(x) = x^8 + x^5 + x^4 + 1 = 100110001
ALERT_PENDING_MASK = 0x8000 # 15
HEATER_MASK = 0x2000 # 13
RH_ALERT_MASK = 0x0800 # 11
T_ALERT_MASK = 0x0400 # 10
RESET_MASK = 0x0010 # 4
CMD_STATUS_MASK = 0x0002 # 1
WRITE_STATUS_MASK = 0x0001 # 0
# MSB = 0x2C LSB = 0x06 Repeatability = High, Clock stretching = enabled
MEASURE_CMD = b'\x2C\x10'
STATUS_CMD = b'\xF3\x2D'
RESET_CMD = b'\x30\xA2'
CLEAR_STATUS_CMD = b'\x30\x41'
ENABLE_HEATER_CMD = b'\x30\x6D'
DISABLE_HEATER_CMD = b'\x30\x66'
def __init__(self, scl_pin=21, sda_pin=20, delta_temp = 0, delta_hum = 0, i2c_address=DEFAULT_I2C_ADDRESS):
self.i2c = I2C(0, scl=Pin(scl_pin), sda=Pin(sda_pin))
self.i2c_addr = i2c_address
self.set_delta(delta_temp, delta_hum)
time.sleep_ms(50)
def init(self, scl_pin=5, sda_pin=4):
"""
Init the I2C bus using the new pin values
"""
self.i2c.init(scl=Pin(scl_pin), sda=Pin(sda_pin))
def is_present(self):
"""
Return true if the sensor is correctly conneced, False otherwise
"""
return self.i2c_addr in self.i2c.scan()
def set_delta(self, delta_temp = 0, delta_hum = 0):
"""
Apply a delta value on the future measurements of temperature and/or humidity
The units are Celsius for temperature and percent for humidity (can be negative values)
"""
self.delta_temp = delta_temp
self.delta_hum = delta_hum
def _check_crc(self, data):
# calculates 8-Bit checksum with given polynomial
crc = 0xFF
for b in data[:-1]:
crc ^= b;
for _ in range(8, 0, -1):
if crc & 0x80:
crc = (crc << 1) ^ SHT30.POLYNOMIAL;
else:
crc <<= 1
crc_to_check = data[-1]
return crc_to_check == crc
def send_cmd(self, cmd_request, response_size=6, read_delay_ms=100):
"""
Send a command to the sensor and read (optionally) the response
The responsed data is validated by CRC
"""
#self.i2c.start();
self.i2c.writeto(self.i2c_addr, cmd_request);
if not response_size:
# self.i2c.stop();
return
time.sleep_ms(read_delay_ms)
data = self.i2c.readfrom(self.i2c_addr, response_size)
# self.i2c.stop();
for i in range(response_size//3):
if not self._check_crc(data[i*3:(i+1)*3]): # pos 2 and 5 are CRC
raise SHT30Error(SHT30Error.CRC_ERROR)
if data == bytearray(response_size):
raise SHT30Error(SHT30Error.DATA_ERROR)
return data
def clear_status(self):
"""
Clear the status register
"""
return self.send_cmd(SHT30.CLEAR_STATUS_CMD, None);
def reset(self):
"""
Send a soft-reset to the sensor
"""
return self.send_cmd(SHT30.RESET_CMD, None);
def status(self, raw=False):
"""
Get the sensor status register.
It returns a int value or the bytearray(3) if raw==True
"""
data = self.send_cmd(SHT30.STATUS_CMD, 3, read_delay_ms=20);
if raw:
return data
status_register = data[0] << 8 | data[1]
return status_register
def measure(self, raw=False):
"""
If raw==True returns a bytearrya(6) with sensor direct measurement otherwise
It gets the temperature (T) and humidity (RH) measurement and return them.
The units are Celsius and percent
"""
data = self.send_cmd(SHT30.MEASURE_CMD, 6);
if raw:
return data
t_celsius = (((data[0] << 8 | data[1]) * 175) / 0xFFFF) - 45 + self.delta_temp;
rh = (((data[3] << 8 | data[4]) * 100.0) / 0xFFFF) + self.delta_hum;
return t_celsius, rh
def measure_int(self, raw=False):
"""
Get the temperature (T) and humidity (RH) measurement using integers.
If raw==True returns a bytearrya(6) with sensor direct measurement otherwise
It returns a tuple with 4 values: T integer, T decimal, H integer, H decimal
For instance to return T=24.0512 and RH= 34.662 This method will return
(24, 5, 34, 66) Only 2 decimal digits are returned, .05 becomes 5
Delta values are not applied in this method
The units are Celsius and percent.
"""
data = self.send_cmd(SHT30.MEASURE_CMD, 6);
if raw:
return data
aux = (data[0] << 8 | data[1]) * 175
t_int = (aux // 0xffff) - 45;
t_dec = (aux % 0xffff * 100) // 0xffff
aux = (data[3] << 8 | data[4]) * 100
h_int = aux // 0xffff
h_dec = (aux % 0xffff * 100) // 0xffff
return t_int, t_dec, h_int, h_dec
class SHT30Error(Exception):
"""
Custom exception for errors on sensor management
"""
BUS_ERROR = 0x01
DATA_ERROR = 0x02
CRC_ERROR = 0x03
def __init__(self, error_code=None):
self.error_code = error_code
super().__init__(self.get_message())
def get_message(self):
if self.error_code == SHT30Error.BUS_ERROR:
return "Bus error"
elif self.error_code == SHT30Error.DATA_ERROR:
return "Data error"
elif self.error_code == SHT30Error.CRC_ERROR:
return "CRC error"
else:
return "Unknown error"
在 main 函数中,首先通过实例化一个 SHT30 类,然后调用 measure 的方法就可以得到当前传感器的温度。
from sht30 import SHT30
sensor = SHT30()
temperature, humidity = sensor.measure()
- 使用PID算法控制加热电阻,实现恒温控制。
PID(Proportional-Integral-Derivative)算法是一种经典的控制算法,常用于工程领域中对系统进行控制。它结合了比例(Proportional)、积分(Integral)和微分(Derivative)三个部分,通过对系统的当前状态、历史状态以及状态变化速率的综合考量,来调节控制器的输出,使系统能够快速且稳定地达到期望的状态或轨迹。其中,比例项使控制器根据当前偏差的大小进行调节;积分项能够消除系统静态误差,并且积累系统误差以对其进行补偿;微分项则根据误差变化率对系统进行预测和调节,以提高系统的动态性能和稳定性。PID算法的参数调节和优化是一个关键的问题,不同的系统需要根据其特性和要求来选择合适的参数配置,以达到最佳的控制效果。PID算法被广泛应用于各种自动控制系统中,包括温度控制、速度控制、位置控制等领域。
通过合理地调节PID算法的参数,我们可以实现对恒温系统的精确控制,使系统能够快速、稳定地达到设定的温度,并在温度变化时及时调整以保持稳定。
这里,使用 PID 算法对整个系统进行控制。
from pid import PID
pid = PID(1000, 0.2, 10000, setpoint=set_temp, sample_time=None)
control = pid(temperature)
上面代码的含义就是将P设置为1000,I设置为0.2,D设置为10000。通过测试,这个参数可以快速地使温度稳定下来。
- 通过PWM调整加热模块。
从原理图中可以看到,加热模块连接的是 22 脚,所以直接将 PWM 连接到 22 脚,通过调节占空比就可以使加热模块定时启动。
from machine import PWM
out = PWM(Pin(22))
out.freq(50)
out_duty += control
out_duty = min(65536, max(0, int(out_duty)))
out.duty_u16(out_duty)
- 将数据通过SPI协议传输至LCD屏幕并显示。
屏幕使用的是 st7789 驱动。可以直接使用电子森林提供的 st7789 驱动进行屏幕输出。
display.text(font1, "set_temp="+str(set_temp), 10, 190)
display.text(font1, "real_temp="+str(temperature), 10, 210)
这样,就可以在屏幕上显示设定的温度和当前的温度。
- 通过拨轮进行人机交互,调节设定温度等参数。
if buttonValueL == 0:
set_temp -= 1
if buttonValueR == 0:
set_temp += 1
if buttonValueL == 0 or buttonValueR == 0:
pid = PID(1000, 0.2, 10000, setpoint=set_temp, sample_time=None)
这里直接使用 GPIO 监测电平。当拨轮向左滚动时,将设定的温度下降一度,反之则向上升一度。
实物演示
遇到问题
- 初期接触板卡操作不熟悉,需要花费较多时间学习和调试。
- 在PID算法调优过程中,需要考虑系统响应速度和稳定性的平衡。
总结感想
通过本次实验,我深入了解了12指神探的传感器扩展板和带屏版的12指神探的功能特点和应用场景。在实践中,我掌握了基于这款板卡设计恒温控制系统的方法,并对PID算法有了更深入的理解。感谢“硬禾学堂”对本项目的赞助和支持,为我提供了宝贵的学习机会和资源支持。