基于Arduino UNO的智能温控风扇
本文主要介绍一个基于Arduino UNO的智能温控风扇demo,其主要实现两种切换模式,分别为自动模式以及手动模式。在自动模式下,风扇可根据DS18B20检测到的环境温度,并根据温度梯度达到不同风速,同时通过2Y0A21模块检测是否有人,若无人,则30s后关闭风扇;在手动模式下,可通过红外遥控以及按键控制风速。
文章目录
前言
本文主要介绍一个基于Arduino UNO的智能温控风扇demo,其主要实现两种切换模式,分别为自动模式以及手动模式。在自动模式下,风扇可根据DS18B20检测到的环境温度,并根据温度梯度达到不同风速,同时通过2Y0A21模块检测是否有人,若无人,则30s后关闭风扇;在手动模式下,可通过红外遥控以及按键控制风速。在这两种模式下,均实现在LCD12864屏幕上。显示实时温度以及距离,还有当前所处模式。
硬件模块包含:Arduino UNO板,LCD12864,DS18B20,2Y0A21,小电机,L298N(2.1A双路电机模块),红外遥控器(含红外接收器),轻触按键,上拉电阻,面包板,若干杜邦线。
一、硬件模块
硬件模块包含:
Arduino UNO板,
LCD12864,
DS18B20,
2Y0A21,
小电机,
L298N(2.1A双路电机模块),
红外遥控器(含红外接收器),
轻触按键,
上拉电阻,面包板,若干杜邦线。
二、接线
硬件模块包含:Arduino UNO板,LCD12864,DS18B20,2Y0A21,小电机,L298N(2.1A双路电机模块),红外遥控器(含红外接收器),轻触按键,上拉电阻,面包板,若干杜邦线。
1.屏幕接线
接线可参考链接: arduino如何驱动LCD12864显示器?(使用U8g2库)
LCD12864模块--------------------------------Arduino UNO模块
GND、PSB、BLK引脚--------------------------GND引脚
VCC、BLA引脚-----------------------------------5V引脚
RS引脚----------------------------------------------2引脚
R/W(SID)引脚-------------------------------------3引脚
E(CLK)引脚----------------------------------------4引脚
RST引脚--------------------------------------------5引脚
2.DS18B20接线
DS18B20模块----------------------------------Arduino UNO模块
GND引脚-------------------------------------------GND引脚
VCC引脚--------------------------------------------5V引脚
信号引脚---------------------------------------------6引脚
3.2Y0A21接线
2Y0A21模块----------------------------------Arduino UNO模块
GND引脚-------------------------------------------GND引脚
VCC引脚--------------------------------------------5V引脚
信号引脚--------------------------------------------A5引脚
4.驱动模块+电机接线
接线可参考链接: 2.5A双路电机驱动模块 正反转 PWM调速 双H桥 步进电机 超L298N
电机模块-----------------驱动模块--------------------Arduino UNO模块
a引脚--------------------MOTOR-A-----------------------10引脚
b引脚--------------------MOTOR-A-----------------------11引脚
------------------------GND引脚(排针)---------------GND引脚
---------------------------2V-10V引脚--------------------外接电源5V
---------------------------GND引脚-----------------------外接电源GND
5.红外接收器接线
红外接收器模块-----------------------------Arduino UNO模块
GND引脚-------------------------------------------GND引脚
VCC引脚--------------------------------------------5V引脚
信号引脚---------------------------------------------7引脚
6.轻触按键接线
轻触按键模块-----------------------------Arduino UNO模块
1引脚-------------------------------------------GND引脚
2引脚--------------------------------------------A3引脚
2引脚----------------上拉电阻----------------5V引脚
同时2引脚接一个上拉电阻之后连到Arduino UNO模块5V引脚
三、所需库
- IRremote库 版本:4.3.0
- OneWire库 版本:2.3.7
- DallasTemperature库 版本3.9.0
- U8glib库 版本1.19.1
四、源码
1.整合代码:完整功能代码(直接接线、安装对应的库即可)
#include <IRremote.h>
// #include <U8g2lib.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <U8glib.h>
#define irReceiverPin 7
#define motorPin1 10
#define motorPin2 11
#define ONE_WIRE_BUS 6
#define ds18b20TimeInterval 1000
#define pin A5
#define pushButton A3
IRrecv irrecv(irReceiverPin);
decode_results results;
//U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /*clock*/4, /*data*/3, /*cs*/2, /*reset*/5);
U8GLIB_ST7920_128X64_4X u8g(4, 3, 2);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;
unsigned long ds18b20Times = 0;
unsigned long lastPersonDetectedTime = 0;
float ds18b20Temp = 0;
unsigned long manu_flag = 0; // 存储红外信号值
bool automaticMode = true; // 初始为自动模式
int buttonState = 0;// 按键次数
void setup() {
Serial.begin(9600);
irrecv.enableIRIn();
// u8g2.begin();
if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
u8g.setColorIndex(255); // white
}
else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
u8g.setColorIndex(3); // max intensity
}
else if ( u8g.getMode() == U8G_MODE_BW ) {
u8g.setColorIndex(1); // pixel on
}
else if ( u8g.getMode() == U8G_MODE_HICOLOR ) {
u8g.setHiColorByRGB(255,255,255);
}
sensors.begin();
sensors.getAddress(insideThermometer, 0);
pinMode(pin, INPUT);
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
pinMode(pushButton, INPUT);
}
void loop() {
if (irrecv.decode(&results)) {
if (results.value == 0xFFA25D) { // 如果按下红外遥控的 CH- 按钮
automaticMode = true; // 切换到自动模式
} else if (results.value == 0xFFE21D) { // 如果按下红外遥控的 CH+ 按钮
automaticMode = false; // 切换到遥控模式
}
irrecv.resume(); // 准备接收下一个红外信号
}
if (automaticMode) {
controlFanAutomatic();
} else {
controlFanManual();
}
}
void controlFanAutomatic() {
if (millis() - ds18b20Times >= ds18b20TimeInterval) {
ds18b20Times = millis();
sensors.requestTemperatures();
ds18b20Temp = sensors.getTempC(insideThermometer);
uint16_t value = analogRead(pin);
uint16_t range = get_gp2d12(value);
// u8g2.firstPage();
u8g.firstPage();
do {
u8g.setFont(u8g_font_6x10); // 使用较小的字体
u8g.drawStr(0, 12, "Auto");
// 第一行显示温度
u8g.drawStr(0, 24, "Temp:");
u8g.setPrintPos(40, 24);
u8g.print(ds18b20Temp);
u8g.print(" C");
// 第二行显示距离
u8g.drawStr(0, 36, "Distance:");
u8g.setPrintPos(60, 36);
u8g.print(range);
u8g.print(" mm");
// 如果检测到人,则更新上次检测到人的时间
if (range < 500) {
lastPersonDetectedTime = millis();
if (ds18b20Temp < 26) {
analogWrite(motorPin1, 40);
analogWrite(motorPin2, 0);
u8g.drawStr(40, 12, "Mode0");
} else if (ds18b20Temp >= 26 && ds18b20Temp < 27) {
analogWrite(motorPin1, 50);
analogWrite(motorPin2, 0);
u8g.drawStr(40, 12, "Mode1");
} else if (ds18b20Temp >= 27 && ds18b20Temp < 28) {
analogWrite(motorPin1, 75);
analogWrite(motorPin2, 0);
u8g.drawStr(40, 12, "Mode2");
} else if (ds18b20Temp >= 28 && ds18b20Temp < 29) {
analogWrite(motorPin1, 100);
analogWrite(motorPin2, 0);
u8g.drawStr(40, 12, "Mode3");
} else if (ds18b20Temp >= 29 && ds18b20Temp < 30) {
analogWrite(motorPin1, 150);
analogWrite(motorPin2, 0);
u8g.drawStr(40, 12, "Mode4");
} else if (ds18b20Temp >= 30) {
analogWrite(motorPin1, 200);
analogWrite(motorPin2, 0);
u8g.drawStr(40, 12, "Mode5");
}
} else {
// 如果30秒内没有检测到人,则关闭电机
if (millis() - lastPersonDetectedTime >= 30000) {
analogWrite(motorPin1, 0);
analogWrite(motorPin2, 0);
}
}
} while (u8g.nextPage());
}
}
// 计算距离
uint16_t get_gp2d12(uint16_t value) {
if (value < 30)
value = 30;
return ((67870.0 / (value - 3.0)) - 40.0);
}
void controlFanManual() {
if (millis() - ds18b20Times >= ds18b20TimeInterval) {
ds18b20Times = millis();
sensors.requestTemperatures();
ds18b20Temp = sensors.getTempC(insideThermometer);
uint16_t value = analogRead(pin);
uint16_t range = get_gp2d12(value);
// u8g2.firstPage();
u8g.firstPage();
do {
u8g.setFont(u8g_font_6x10); // 使用较小的字体
u8g.drawStr(0, 12, "Manu");
// 第一行显示温度
u8g.drawStr(0, 24, "Temp:");
u8g.setPrintPos(40, 24);
u8g.print(ds18b20Temp);
u8g.print(" C");
// 第二行显示距离
u8g.drawStr(0, 36, "Distance:");
u8g.setPrintPos(60, 36);
u8g.print(range);
u8g.print(" mm");
if (digitalRead(pushButton) == 0){
delay(20);
while(digitalRead(pushButton) == 0);
delay(20);
buttonState++;
if (buttonState == 6){
buttonState = 0;
}
}
if (buttonState == 0) { // 按键0
analogWrite(motorPin1, 0); // 停止电机
analogWrite(motorPin2, 0); //
u8g.drawStr(40, 12, "Mode0");
buttonState = 0;
}
else if (buttonState == 1) { // 按键1
analogWrite(motorPin1, 50); //
analogWrite(motorPin2, 0); //
u8g.drawStr(40, 12, "Mode1");
buttonState = 1;
}
else if (buttonState == 2) { // 按键2
analogWrite(motorPin1, 75); //
analogWrite(motorPin2, 0); //
u8g.drawStr(40, 12, "Mode2");
buttonState = 2;
}
else if (buttonState == 3) { // 按键3
analogWrite(motorPin1, 100); //
analogWrite(motorPin2, 0); //
u8g.drawStr(40, 12, "Mode3");
buttonState = 3;
}
else if (buttonState == 4) { // 按键4
analogWrite(motorPin1, 150); //
analogWrite(motorPin2, 0); //
u8g.drawStr(40, 12, "Mode4");
buttonState = 4;
}
else if (buttonState == 5) { // 按键5
analogWrite(motorPin1, 200); //
analogWrite(motorPin2, 0); //
u8g.drawStr(40, 12, "Mode5");
buttonState = 5;
}
if (irrecv.decode(&results)) {
// 打印接收到的红外信号的值和位数
Serial.print("irCode: ");
Serial.print(results.value, HEX);
Serial.print(", bits: ");
Serial.println(results.bits);
manu_flag = results.value;
// 根据不同的红外信号值来控制电机转速
if (results.value == 0xFF6897) { // 按键0
buttonState = 0;
}
else if (results.value == 0xFF30CF) { // 按键1
buttonState = 1;
}
else if (results.value == 0xFF18E7) { // 按键2
buttonState = 2;
}
else if (results.value == 0xFF7A85) { // 按键3
buttonState = 3;
}
else if (results.value == 0xFF10EF) { // 按键4
buttonState = 4;
}
else if (results.value == 0xFF38C7) { // 按键5
buttonState = 5;
}
else {
// 其他按键值不进行任何操作
}
delay(300);
irrecv.resume(); // 准备接收下一个红外信号
}
}while (u8g.nextPage());
}
}
2.测试代码:自动模式代码(采用U8g2lib库显示)
#include <U8g2lib.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <IRremote.h>
U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /*clock*/4, /*data*/3, /*cs*/2 , /*reset*/5);
#define ONE_WIRE_BUS 6 // DS18B20
#define ds18b20TimeInterval 1000
#define pin A5 // 2Y0A21
// 89号引脚控制一个电机的两个引脚
const int motorPin1 = 10; // 第一个引脚连接到 8 号引脚
const int motorPin2 = 11; // 第二个引脚连接到 9 号引脚
unsigned long lastPersonDetectedTime = 0; // 上次检测到人的时间
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;
unsigned long ds18b20Times = 0;
float ds18b20Temp = 0;
void setup() {
u8g2.begin();
Serial.begin(9600);
sensors.begin();
if (!sensors.getAddress(insideThermometer, 0)) {
Serial.println("设备未上线!");
}
Serial.println("设备上线!");
pinMode(pin, INPUT);
//电机
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
}
void loop() {
printData();
}
void printData() {
if (millis() - ds18b20Times >= ds18b20TimeInterval) {
ds18b20Times = millis();
sensors.requestTemperatures();
ds18b20Temp = sensors.getTempC(insideThermometer);
uint16_t value = analogRead(pin);
uint16_t range = get_gp2d12(value);
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_6x10_tr); // 使用较小的字体
// 第一行显示温度
u8g2.drawStr(0, 12, "Temp:");
u8g2.setCursor(40, 12);
u8g2.print(ds18b20Temp);
u8g2.print(" C");
// 第二行显示距离
u8g2.drawStr(0, 24, "Distance:");
u8g2.setCursor(60, 24);
u8g2.print(range);
u8g2.print(" mm");
// 如果检测到人,则更新上次检测到人的时间
if (range<1000){
lastPersonDetectedTime = millis();
if (ds18b20Temp<26){
analogWrite(motorPin1, 0); //
analogWrite(motorPin2, 0); //
}
else if (ds18b20Temp>=26 && ds18b20Temp<27){
analogWrite(motorPin1, 50); //
analogWrite(motorPin2, 0); //
}
else if (ds18b20Temp>=27 && ds18b20Temp<28){
analogWrite(motorPin1, 75); //
analogWrite(motorPin2, 0); //
}
else if (ds18b20Temp>=28 && ds18b20Temp<29){
analogWrite(motorPin1, 100); //
analogWrite(motorPin2, 0); //
}
else if(ds18b20Temp>=29 && ds18b20Temp<30){
analogWrite(motorPin1, 150); //
analogWrite(motorPin2, 0); //
}
else if(ds18b20Temp>=30){
analogWrite(motorPin1, 200); //
analogWrite(motorPin2, 0); //
}
}
else{
if (millis() - lastPersonDetectedTime >= 10000) {
analogWrite(motorPin1, 0); //
analogWrite(motorPin2, 0); //
}
}
} while (u8g2.nextPage());
}
}
// 计算距离
uint16_t get_gp2d12(uint16_t value) {
if (value < 30)
value = 30;
return ((67870.0 / (value - 3.0)) - 40.0);
}
3.测试代码:遥控模式代码(未显示温度、距离)
#include <IRremote.h>
// 7号引脚接收红外信号
const int irReceiverPin = 7;
// 89号引脚控制一个电机的两个引脚
const int motorPin1 = 10; // 第一个引脚连接到 10 号引脚
const int motorPin2 = 11; // 第二个引脚连接到 11 号引脚
IRrecv irrecv(irReceiverPin);
decode_results results;
void setup() {
Serial.begin(9600);
irrecv.enableIRIn();
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
}
void loop() {
if (irrecv.decode(&results)) {
// 打印接收到的红外信号的值和位数
Serial.print("irCode: ");
Serial.print(results.value, HEX);
Serial.print(", bits: ");
Serial.println(results.bits);
// 根据不同的红外信号值来控制电机转速
if (results.value == 0xFF6897) { // 按键0
analogWrite(motorPin1, 0); // 停止电机
analogWrite(motorPin2, 0); //
}
else if (results.value == 0xFF30CF) { // 按键1
analogWrite(motorPin1, 100); //
analogWrite(motorPin2, 0); //
}
else if (results.value == 0xFF18E7) { // 按键2
analogWrite(motorPin1, 150); //
analogWrite(motorPin2, 0); //
}
else if (results.value == 0xFF7A85) { // 按键3
analogWrite(motorPin1, 200); //
analogWrite(motorPin2, 0); //
}
else {
// 其他按键值不进行任何操作
}
delay(300);
irrecv.resume(); // 准备接收下一个红外信号
}
}
五、遇到的一些问题
1.问题1
问题1:红外遥控测试时候会出现无论按什么键均打印FFFFFF的问题
原因分析:IRremote库版本过高
解决方法:换成低版本即可,本文所用版本的库能正常使用
参考方法链接: 【Arduino】红外遥控实验按遥控器没反应 解决
2.问题2
问题2:用Arduino UNO的5V给电机模块供电,电机转不动
原因分析:Arduino UNO上面的电源管理电流过低,不足以支撑电机转动
解决方法:外接5V电源给电机模块供电,同时注意要共地!
3.问题3
问题3:各模块代码整合的时候出现Arduino UNO的内存显示不足,例如
Sketch uses 1978 bytes (6%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
原因分析:经过测试发现,本项目中原本使用的U8g2lib库严重占用内存,换成小一些的屏幕显示库U8glib即可
解决方法:换成小一些的屏幕显示库U8glib即可
参考方法链接: 解决Arduino Uno上传代码时提示内存不足的问题