Arduino-ESP8266 控制舵机开门

  • Post author:
  • Post category:其他




简介

成本不到30大洋,实现手机自动开门功能!

先上效果视频:

mqtt消息控制舵机开门



准备工作

  1. esp8266 开发板 13¥
  2. Mg995 舵机 13¥
  3. 四个5号电池 2 ¥
  4. 导线若干 0.5¥
  5. 电池盒一个 0.5¥

    成本不到30大洋,可玩性,实用性都很高。



原理图

从右往左看

  1. 手机上 点击 开门 按钮,发送mqtt消息到 服务器;
  2. esp8266开发板订阅了mqtt消息,接收到开门指令;
  3. esp8266发送pwm信号到舵机转动;
  4. 舵机 收到消息转动一定角度,拉动 门阀 ;
  5. 门打开。

    在这里插入图片描述



arduino代码

主要是esp8266控制舵机转动

我这边没有用到Servo 库,原因是 提示我的开发板不在Servo 支持的架构里面。不知道为啥,有没有大佬来解惑?万分感谢!

警告: Servo 库要求运行在 avr, megaavr, sam, samd, nrf52, stm32f4, 
mbed, mbed_nano, mbed_portenta, mbed_rp2040 架构(),
可能与你现在运行在 esp8266 架构上的开发板()不兼容。

废话不多说了,上Arduino 写给 esp8266的代码


//连接多个wifi
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
//网络请求
#include <ESP8266HTTPClient.h>
//mqtt 库
#include <PubSubClient.h>
#include <Ticker.h>


int sp1=12;//定义舵机连接到esp8266上的接口
int pulsewidth;//定义脉宽变量
int val;
int val1;
int myangle1;

int count;    // Ticker计数用变量
Ticker ticker;

ESP8266WiFiMulti wifiMulti; //创建一个ESP8266WiFiMulti对象
//创建 HTTPClient 对象
HTTPClient httpClient;
WiFiClient wifiClient;

//mqtt服务对象
PubSubClient mqttClient(wifiClient);
//mqtt 服务器地址
const char* mqttServer = "mqtt服务器得地址";
//mqtt账号密码
const char* mqttname = "用户名";
const char* mqttpwd = "密码";
int dataIndex=0;

void setup() {
  // put your setup code here, to run once:
  //开启串口监视器
  Serial.begin(9600);
//  servo.attach(12);
  pinMode(sp1,OUTPUT);//设定舵机接口为输出接口

  connWifi();
  //设置mqtt服务器和端口号
  mqttClient.setServer(mqttServer,1883);
  // 设置MQTT订阅回调函数
  mqttClient.setCallback(receiveCallback);
  connectMQTTServer();
  digitalWrite(sp1,LOW);//将舵机接口电平至低
  // Ticker定时对象
  ticker.attach(1, tickerCount);

}

void tickerCount(){
  count++;
}

void connWifi(){
    //设置ESP8266工作模式为无线终端模式
  WiFi.mode(WIFI_STA);
  // 通过 addAP函数存储  wifi 名称,wifi密码
  wifiMulti.addAP("wifi_name", "wifi_pwd");
  wifiMulti.addAP("wifi_name2", "wifi_pwd2");

  Serial.println("Please wait, Connecting");
  int i = 0;
  while (wifiMulti.run() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
    if(i>10) Serial.println("\n");
    //print(i++);
  }
  //连接wifi成功后,输出连接成功信息
  Serial.println("\n");
  Serial.print("connected to ");
  Serial.println(WiFi.SSID());
  Serial.print("IP address: \t");
  Serial.println(WiFi.localIP());
}

void loop() {

  if(mqttClient.connected()){
    mqttClient.loop();//保持客户端心跳   
    // 每隔3秒钟发布一次信息
    if(count >3) {
        pubTopic(String(val1));
        count = 0;
    }
  }else{
    connectMQTTServer();
  }
  
  
}



//连接mqtt服务器
void connectMQTTServer(){
  //生成一个id,一般用mac地址生成,保证唯一性 WiFi.macAddress()
  String clientId = "random_id随机字符串";
  //连接服务器
  if(mqttClient.connect(clientId.c_str(),mqttname,mqttpwd)) {
    Serial.println("MQTT Server Connected.");
    Serial.print("Server Address:");
    Serial.println(mqttServer);
    subTopic();//订阅主题
  }else{
    Serial.println("MQTT Server Connect Failed.");
    Serial.println(mqttClient.state());
    delay(3000);
  }
  
}
// 收到信息后的回调函数
void receiveCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message Received [");
  Serial.print(topic);
  Serial.print("] ");
  String cmd = "";
  for (int i = 0; i < length; i++) {
    cmd += (char)payload[i];
  }
  
  Serial.print("cmd is:");
  Serial.println(cmd.toInt());
  doAction(cmd.toInt());
}

// 发布信息
void pubTopic(String msg){
  
  String topicString = "主题名称";// 主题名称不能太长,我实际发现太长了好像就发不了有效的消息
  char publishTopic[topicString.length() + 1];  
  strcpy(publishTopic, topicString.c_str());
 
  // 建立发布信息。。 
  char publishMsg[msg.length() + 1];   
  strcpy(publishMsg, msg.c_str());
  
  // 实现ESP8266向主题发布信息
  if(mqttClient.publish(publishTopic, publishMsg)){
    Serial.println("Publish Topic:");Serial.println(publishTopic);
    Serial.println("Publish message:");Serial.println(publishMsg);    
  } else {
    Serial.println("Message Publish Failed."); 
  }
}


//订阅主题消息
void subTopic(){
  String topString = "主题名称,要跟手机上发送的一致,才能收到";
  char subTop[topString.length()+1];
  strcpy(subTop, topString.c_str());
  if(mqttClient.subscribe(subTop)){
    Serial.println("Subscrib Topic");
    Serial.println(subTop);
  }else{
    Serial.println("Subscrib Fail...");
  }
  
}


void servopulse(int sp1,int val1)//定义一个脉冲函数
{
  myangle1=map(val1,0,180,500,2480);
  digitalWrite(sp1,HIGH);//将舵机接口电平至高
  delayMicroseconds(myangle1);//延时脉宽值的微秒数
  digitalWrite(sp1,LOW);//将舵机接口电平至低
  delay(20-val1/1000);
}


void doAction(int val){
  if(val>0 && val<=9)
  {
    val1=val;//将特征量转化为数值变量
    val1=map(val1,0,9,0,180);//将角度转化为500-2480的脉宽值
    Serial.print("moving servo to ");
    Serial.print(val1,DEC);
    Serial.println();
    for(int i=0;i<=50;i++)//给予舵机足够的时间让它转到指定角度
    {
      servopulse(sp1,val1);//引用脉冲函数
    }
  }else{
    Serial.println("can not identy");
  }
}



 



前端代码

前端主要是连接mqtt服务器费点功夫,界面就一个按钮,一个点击事件还是很简单的。

我用的前端框架是angular比较小众简单贴一下代码

component部分

import { Component, OnInit,OnDestroy } from '@angular/core';

import {IMqttMessage, MqttService} from 'ngx-mqtt';
import { Observable, Subscription } from 'rxjs';
import { DoorBean } from 'src/app/bean/door-bean';


@Component({
  selector: 'app-door',
  templateUrl: './door.component.html',
  styleUrls: ['./door.component.scss']
})
export class DoorComponent implements OnInit,OnDestroy {

  carStatus: boolean = false;
  timeo: any ;
  item:DoorBean = new DoorBean(1,"","开门",'container');
  obs:  Subscription;

  constructor(public ms: MqttService){
    this.obs = this.ms.observe("门的状态主题").subscribe(res=>{
      let status = res.payload.toString();
      if( status.toString() === "1"){
        this.carStatus = true;
        if(this.timeo){
          window.clearTimeout(this.timeo)
        }
      }else{
        this.carStatus = false;
      }
      this.timeo = setTimeout(() => {
        this.carStatus = false;
      }, 4000);
      
    })
  }

  ngOnInit(): void {
  }

  //点击图标
  onItemClick(){
    this.sendCmd("4")
    this.item.className = "press"
    this.item.value = "请稍等"
    setTimeout(() => {
      this.sendCmd("1")
      this.item.className = "container";
      this.item.value = "开门"
    }, 3000);
  }

  sendCmd(cmd: string){
    //发送方向指令给舵机
    this.ms.unsafePublish("主题名称,开发板订阅的名称跟这个保持一致", cmd, {qos: 1, retain: true});
  }

  ngOnDestroy(): void {
    //Called once, before the instance is destroyed.
    //Add 'implements OnDestroy' to the class.
    this.obs.unsubscribe();
  }

}

html

<div [class]="item.className" (click)="onItemClick()">
    {{item.value}}
</div>

css

.container{
    width: 5rem;
    height: 5rem;
    background-color: #1c6eaf;
    margin: 0 auto;
    margin-top: 3rem;
    border-radius: 50%;
    color: white;
    text-align: center;
    line-height: 5rem;
}

.press{
    @extend .container;
    background-color: #6a7074;
}

效果界面

在这里插入图片描述



总结

总体下来没啥难点,舵机转到的角度,与门阀拉动多少距离 需要动手多调一调,找出比较合适的角度。多动手试试吧,感谢您看到最后,谢谢!



版权声明:本文为qwe1314225原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。