简介
成本不到30大洋,实现手机自动开门功能! 先上效果视频:
准备工作
- esp8266 开发板 13¥
- Mg995 舵机 13¥
- 四个5号电池 2 ¥
- 导线若干 0.5¥
- 电池盒一个 0.5¥
成本不到30大洋,可玩性,实用性都很高。
原理图
从右往左看
- 手机上 点击 开门 按钮,发送mqtt消息到 服务器;
- esp8266开发板订阅了mqtt消息,接收到开门指令;
- esp8266发送pwm信号到舵机转动;
- 舵机 收到消息转动一定角度,拉动 门阀 ;
- 门打开。
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;
}
效果界面
总结
总体下来没啥难点,舵机转到的角度,与门阀拉动多少距离 需要动手多调一调,找出比较合适的角度。多动手试试吧,感谢您看到最后,谢谢!
|