玩转ESP32:打造智能无线电风扇

本文详细描述了一名开发者利用ESP32制作电风扇的过程,涉及硬件选型、ESP32基础、风扇与机身控制,以及MQTT通信的原理和在项目中的应用。通过分享项目,作者展示了物联网技术在智能家居中的实践和MQTT在远程设备控制中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.介绍与准备

1.1项目背景与动机

        在这个寒假中,我抽出了点的时间,利用ESP32制作了一个简易的电风扇。这个项目的动机源于对物联网技术的兴趣以及对智能家居的向往。通过整理并记录这个项目,我希望能够与对此感兴趣的朋友们分享我的经验和成果。

1.2.硬件与软件准备

        硬件主要材料包括:esp32核心板一个、简易机械臂一个(包括两个舵机sg90和机械臂材料,ps:某宝可以直接购买获得)、L9110风扇模块电风扇一个、面包板(有需要的话)。

        软件的话采用,使用VSCODE上的platformio插件进行arduino编程。

2.ESP32基础

2.1. 什么是ESP32?

        ESP32是由乐鑫信息科技(Espressif Systems)开发的一款低成本、低功耗的系统级芯片(SoC),广泛应用于物联网(IoT)设备。它整合了Wi-Fi和蓝牙功能,提供了强大的性能和灵活性。

2.2. 主要特性:
  • 双核处理器: ESP32具备双核处理器,使其能够同时处理多个任务,提高系统的响应性。

  • Wi-Fi和蓝牙: ESP32支持2.4GHz Wi-Fi和蓝牙4.2/Bluetooth LE,使其成为连接智能设备的理想选择。

  • 丰富的外设: ESP32集成了各种外设,如模数转换器(ADC)、脉冲宽度调制(PWM)等,增加了对各种传感器和执行器的支持。

  • 低功耗设计: ESP32在设计上注重了功耗的优化,适用于需要长时间运行的电池供电设备。

2.3. ESP32在物联网中的应用:
  • 智能家居: ESP32广泛用于智能家居设备,如智能灯具、智能插座等,通过Wi-Fi和蓝牙实现互联。

  • 传感器网络: 由于其丰富的外设支持,ESP32适用于构建传感器网络,监测环境变量、收集数据等。

  • 嵌入式系统: ESP32在嵌入式系统中也得到了广泛应用,用于控制和连接各种设备。

2.4. ESP32的开发生态:
  • 开发环境: ESP32可以使用多种集成开发环境(IDE),如Arduino IDE、PlatformIO等,使得开发变得简单而灵活。

3.风扇控制与机身控制

3.1风扇控制基础

       电风扇所采用的型号是L9110风扇模块,该模块集成了L9110驱动器,因此无需额外添加驱动模块。其控制方式仅需要通过两路PWM信号即可控制风扇的旋转速度和方向。

        在arduino中控制方式如下

    analogWrite(5,100);
    analogWrite(6,0);

       在这段代码中,通过引脚5和6来控制风扇的旋转。需要注意,在引脚的选择上,应该选择支持PWM的引脚。两个引脚的PWM范围是0到255,通过改变这两个引脚的PWM值,可以实现对风扇转速和方向的精确控制。

3.2机身控制基础 

        风扇的机身控制依赖于两个SG90舵机,一个用于水平旋转,另一个用于控制风扇的上下运动。

舵机控制关系:

        舵机的运动受20毫秒左右的时基脉冲控制,该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分,可用于确定舵机的角度。对于SG90这款180度角度伺服舵机,我们可以参考以下关系:

  • 0.5毫秒: 对应0度位置
  • 1.0毫秒: 对应45度位置
  • 1.5毫秒: 对应90度位置
  • 2.0毫秒: 对应135度位置
  • 2.5毫秒: 对应180度位置

        通过调整脉冲的高电平部分,我们能够实现舵机在整个180度范围内的准确控制。这两个舵机的联动实现了风扇在水平和垂直方向上的全方位定向送风。

3.3mqtt通信基础

3.3.1. MQTT是什么?

MQTT是一种轻量级、开放标准的消息协议,旨在实现设备之间的高效通信。它适用于低带宽、高延迟或不稳定网络的场景,被广泛应用于物联网(IoT)领域。

3.3.2. MQTT的工作原理:

        MQTT采用发布/订阅模式,其中有三个主要角色:发布者(Publisher)、订阅者(Subscriber)和代理服务器(Broker)。

  • 发布者(Publisher): 向主题发布消息的设备。主题(Topic)是消息的分类,用于标识消息内容。

  • 代理服务器(Broker): 负责接收发布者发送的消息并将其传递给对应订阅者。MQTT通信的中心组件。

  • 订阅者(Subscriber): 订阅与特定主题相关的消息,接收发布者发送的消息。

3.3.3. MQTT主题和负载:
  • 主题(Topic): 是消息的分类标识,它是一个由层级结构组成的字符串,形式类似于文件路径。发布者和订阅者通过主题来匹配和过滤消息。

  • 负载(Payload): 是实际的消息内容。可以是任何二进制数据,根据项目需求而定。

3.3.4. 连接和断开:
  • 连接: 设备通过TCP连接到MQTT代理服务器,并进行身份验证。连接后,设备可以发布和订阅消息。

  • 断开: 设备可以主动断开连接,或者在网络故障等情况下,由代理服务器断开。

3.3.5. QoS级别:

MQTT支持不同的服务质量(QoS)级别,以确保消息的可靠传递。QoS级别包括0、1和2,级别越高,消息传递越可靠,但开销也越大。

3.3.6. 为什么选择MQTT:
  • 轻量级: MQTT是一种轻量级协议,适用于资源受限的设备。

  • 可靠性: 支持不同的QoS级别,确保消息可靠传递。

  • 灵活性: 适用于各种不同的网络和设备类型,提供了灵活的通信机制。

3.3.7. MQTT在ESP32项目中的应用:
  • 实现设备通信: 使用MQTT协议,ESP32可以与其他设备进行实时通信,适用于智能家居、监控系统等项目。

  • 远程控制: 通过MQTT,可以实现对ESP32设备的远程控制,例如控制风扇的开关和风速以及风扇机身旋转及停止。

4.实际项目实现

4.1风扇控制实现

        电风扇的控制比较简单,在MQTT的回调函数中,根据电风扇的控制指令输出两个PWM即可实现控制电风扇的风力。在使用前可以先测试一旋转方向保证这个是出风的。

        参考代码如下:

 if ((char)payload[0] == '1') {
    analogWrite(25,70);
    analogWrite(26,0);
    Serial.print("run 1 ");
  } else if ((char)payload[0] == '2'){
    analogWrite(25,140);
    analogWrite(26,0);
    Serial.print("run 2");
  }else if ((char)payload[0] == '3'){
    analogWrite(25,210);
    analogWrite(26,0);
    Serial.print("run 3");
  }
  else if ((char)payload[0] == '0'){
    analogWrite(25,0);
    analogWrite(26,0);
    Serial.print("stop ");
      }

4.2机身控制实现

        通过观察生活中电风扇的旋转,我们可以知道电风扇的旋转是,先匀速旋转到一个方向的最大值,然后反向匀速旋转到最大值,并不断重复,类似于一个不断的三角波。那么如何在代码中实现这个。

        我们可以设定一个函数,在函数中设定一个最大值当函数超过最大值,数值就开始从最大值反向递减,数值没有超过最大值的话则不变。公式如下

\left\{\begin{matrix}\\pos=count (count=<posmax) \\ pos=2*posmax-cout (count>posmax) \end{matrix}\right.

        代码如下

//数据处理函数
int coutTopos(int count,int max){
  int result;
    if (count<=max)
      result=count;
    else if(count>max)
      result=2*max-count;
    else if(count>=2*max)
      result=0;
    else
      result=0;

  return result;
}

        使用时我们会发现当数据大于两倍的max不就是负的了吗,因此我们需要在计数器递增大于两倍max需要对他复位到0。这样我们就可以得到风扇水平旋转控制的程序了。代码如下

void horizontal_roatation(bool flag) {
  if(flag==true){
    myservo.write(h_pos);    // tell servo to go to position in variable 'pos'
     h_count++;
     if(h_count>2*h_posmax)
      h_count=0;
    h_pos=coutTopos(h_count,h_posmax);
    
   delay(15);      
  }
  else{
    delay(15);
  }
	
}

        其中舵机控制函数 myservo.write(h_pos)为arduino发的舵机控制库 <ESP32Servo.h>,提供控制位置的程序。通过给定角度即可实现位置控制。垂直控制方法类似不在赘述。舵机使用前需要一个进行一个初始化,程序如下:

void servossetup() {
	
	ESP32PWM::allocateTimer(0);
	ESP32PWM::allocateTimer(1);
    ESP32PWM::allocateTimer(2);

	myservo.setPeriodHertz(50);    // standard 50 hz servo
    myservo2.setPeriodHertz(50);    // standard 50 hz servo

	myservo.attach(servoPin, 500, 2500); // attaches the servo on pin 18 to the servo object
 	myservo2.attach(servo2Pin, 500, 2500); // attaches the servo on pin 18 to the servo object

    myservo.write(h_pos);
    myservo2.write(v_pos);
}

        注:servoPin我设置了引脚18,控制水平旋转;servoPin2设置17用于控制垂直旋转。

4.3mqtt通信实现

        mqtt的通信主要是利用<PubSubClient.h>库实现MQTT通信。在mqtt通信使用前需要对mqtt进行一个初始化,实现连接wifi、设置回调函数,订阅控制话题等。其代码如下

void mqttsetup() {

  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);


  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
 
    if (client.connect("EFClient",mqttUser,mqttPassword )) {
 
      Serial.println("connected");  
 
    } else {
 
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
 
    }
  }
  client.publish("outTopic","hello,i'm esp32");
  client.subscribe(topic);

}

        在回调函数里根据接收到的不同的数据位,可以实现风扇的旋转,及舵机的转动。在这里使用三位数,其中第一位数的1-3分别表示分速的级别,0表示停止;第二位及第三位则用于判断水平及垂直机械臂的旋转。具体实现如下

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the electric fan
  if(strncmp(topic,"epp32/cyf",9)==0){

    if ((char)payload[0] == '1') {
    analogWrite(25,70);
    analogWrite(26,0);
    Serial.print("run 1 ");
  } else if ((char)payload[0] == '2'){
    analogWrite(25,140);
    analogWrite(26,0);
    Serial.print("run 2");
  }else if ((char)payload[0] == '3'){
    analogWrite(25,210);
    analogWrite(26,0);
    Serial.print("run 3");
  }
  else if ((char)payload[0] == '0'){
    analogWrite(25,0);
    analogWrite(26,0);
    Serial.print("stop ");
      }

    if((char)payload[1]=='1'){
      h_flag=true;
    }
    else {
      h_flag=false;
    }

    if((char)payload[2]=='1'){
      v_flag=true;
    }
    else {
      v_flag=false;
    }
  }

}

        在使用时可以通过MQTTx软件实现mqtt通信,后面可以通过发送三个数字实现风扇的控制。软件界面如下

        在这里mqtt通信建议采用qos2,完成控制。

5.总结

        无线电风扇控制主要由三个部分组成,第一个是风扇旋转控制(最简单的),第二个是MQTT通信(调库就行),最后一个是双舵机的控制(稍加修改就行)。总之这是个很简单的无线风扇设计。风扇如下,待mqtt接通后,通过三位mqttx界面发送三个就能实现电风扇控制了。

好了,下面到了激动人心的时刻了,直接贴程序。感兴趣的朋友可以试试。

#include <Arduino.h>
#include <ESP32Servo.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <stdio.h>
// Update these with values suitable for your network.

const char* ssid = "";//wifi账号
const char* password = "";//wifi密码
const char* mqtt_server = "";//mqtt服务器
const char* mqttUser = "emqx";//mqtt用户 自己取就行了
const char* mqttPassword = "public" ;//mqtt密码 自己取就行了
const char *topic="esp32/elefan"; //订阅话题对上就行了

WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE	(50)
char msg[MSG_BUFFER_SIZE];
int value = 0;

Servo myservo;  // create servo object to control a servo
Servo myservo2;

int h_pos = 90;    // variable to store the servo position
int v_pos = 30; 
int h_count = 90;    // variable to store the servo position
int v_count= 0; 
int h_posmax=180;
int v_posmax=180;
bool h_flag=false;
bool v_flag=false;
int servoPin = 18;
int servo2Pin = 17;

int coutTopos(int count,int max);

void servossetup() {
	
	ESP32PWM::allocateTimer(0);
	ESP32PWM::allocateTimer(1);
  ESP32PWM::allocateTimer(2);

	myservo.setPeriodHertz(50);    // standard 50 hz servo
  myservo2.setPeriodHertz(50);    // standard 50 hz servo

	myservo.attach(servoPin, 500, 2500); // attaches the servo on pin 18 to the servo object
 	myservo2.attach(servo2Pin, 500, 2500); // attaches the servo on pin 18 to the servo object

  myservo.write(h_pos);
  myservo2.write(v_pos);
}

void horizontal_roatation(bool flag) {
  if(flag==true){
    myservo.write(h_pos);    // tell servo to go to position in variable 'pos'
     h_count++;
     if(h_count>2*h_posmax)
      h_count=0;
    h_pos=coutTopos(h_count,h_posmax);
    
   delay(15);      
  }
  else{
    delay(15);
  }
	
}
void vertical_roatation(bool flag) {
  if(flag==true){
     myservo2.write(v_pos);    // tell servo to go to position in variable 'pos'
      v_count++;
     if(v_count>2*v_posmax)
      v_count=0;
    v_pos=coutTopos(v_count,v_posmax);

   
   delay(15);      
   
  }
  else{
    delay(15);
  }
}


void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password,6);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the electric fan
  if(strncmp(topic,"epp32/cyf",9)==0){

    if ((char)payload[0] == '1') {
    analogWrite(25,70);
    analogWrite(26,0);
    Serial.print("run 1 ");
  } else if ((char)payload[0] == '2'){
    analogWrite(25,140);
    analogWrite(26,0);
    Serial.print("run 2");
  }else if ((char)payload[0] == '3'){
    analogWrite(25,210);
    analogWrite(26,0);
    Serial.print("run 3");
  }
  else if ((char)payload[0] == '0'){
    analogWrite(25,0);
    analogWrite(26,0);
    Serial.print("stop ");
      }

    if((char)payload[1]=='1'){
      h_flag=true;
    }
    else {
      h_flag=false;
    }

    if((char)payload[2]=='1'){
      v_flag=true;
    }
    else {
      v_flag=false;
    }
  }

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "dfsClient-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      // ... and resubscribe
      client.subscribe(topic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void mqttsetup() {

  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);


  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
 
    if (client.connect("EFClient",mqttUser,mqttPassword )) {
 
      Serial.println("connected");  
 
    } else {
 
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
 
    }
  }
  client.publish("outTopic","hello,i'm esp32");
  client.subscribe(topic);

}


void setup(){
  
  servossetup();
  mqttsetup();
  
}
void loop(){
 
  
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
 
  horizontal_roatation(h_flag);
  vertical_roatation(v_flag);
}

//数据处理函数
int coutTopos(int count,int max){
  int result;
    if (count<=max)
      result=count;
    else if(count>max)
      result=2*max-count;
    else if(count>=2*max)
      result=0;
    else
      result=0;

  return result;
}

注:mqtt服务器可以自己通过emqtt或者mosquitto搭载,嫌麻烦也可以直接用公有的库,使用公共mqtt服务器通信失败的话,可以换一个试试。博主之前使用emqtt公共服务器,调试不出来,就用自己搭载发服务器重新测试了一下发现可以,后面又试了一下乐鑫公司的mqtt测试服务器mqtt.eclipseprojects.io(为啥我会先用这个试试,因为我一直是idf开发呀,之前用过,嘻嘻),也可以实现,如果你行可以换其他试试。

随着电子制造业的不断发展,社会对生产率的要求越来越高,各行业都需要精良高效、高可靠性的设备来满足要求。作为一种老式家电,电风扇曾一度被认为是空调产品冲击下的淘汰品;但电风具有价格便宜、摆放方便、体积轻巧等特点。由于大部分家庭消费水平的限制,电风扇作为成熟的家电行业的一员,在中小城市以及乡村将来一段时间内仍然会占有市场的大部分份额,但老式电风扇功能简单,不能满足智能化的要求。为提高电风扇的市场竞争力,使之在技术含量上有所提高,且更加安全可靠,智能风扇随之被提出。   传统电风扇具有以下缺点:风扇不能遥控控制风扇调速,必须手动调速,给人们生活带来极大的不方便。传统电风机械的定时方式常常会伴随着机械运动的声音,特别是夜间影响人们的睡眠,而且定时范围有限,不能满足人们的需求。鉴于这些缺点,我们需要设计一款智能的电风扇控制系统来解决。   本文以STC89C52单片机为核心,通过数字温度传感器对外界环境温度进行数据采集,从而建立一个控制系统,使电风扇随温度的变化而自动调节档位,实现“温度高、风力大、温度低、风力弱”的性能。另外,通过红外发射和接收装置及按键实现各种功能的启动与关闭,并且可对各种功能实现遥控,用户可以在一定范围内设置电风扇的最低工作温度,当温度低于所设置温度时,电风扇将自动关闭,当高于此温度时电风扇又将重新启动。   本设计主要内容如下: (1)风速设为从低到高共2个档位,可由用户通过键盘设定。 (2)每当温度低于下限值时,则电风扇风速关闭。 (3)每当温度在下限和上限之间时,则电风扇转速缓慢。 (4)每当温度高于上限值时,则电风扇风速全速运转。   本设计的整体思路是:利用温度传感器DSI8B20检测环境温度并直接输出数字温度信号给单片机STC89C52进行处理,在LED数码管上显示当前环境温度值以及预设温度值。其中预设温度值只能为整数形式,检测到的当前环境温度可精确到小数点后一位。同时采用PWM脉宽调制方式来改变直流风扇电机的转速。并通过两个按键改变预设温度值,一个提高预设温度,另一个降低预设温度值。系统结构框图,如图所示。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值