STM32毕业设计项目实战:智能门锁系统设计与实现
先看成品:
项目需求分析
作为一个智能门锁,设置解锁密码,输入密码解锁,开锁,屏幕显示都是基本功能,所以一定会需要控制芯片,键盘,屏幕、舵机和蜂鸣器。另外需要独立给硬件供电,所以要锂电池。
而这个项目的创新点在于使用微信小程序控制,所以需要ESP8266-01s使硬件联网。
第二个创新点就是监测屋内的烟雾浓度,这里使用MQ2模块。
代码使用C语言和HAL库编写。
原理图设计
核心部分有:电源电路设计、稳压电路设计、蜂鸣器电路设计、4×4矩阵电路设计。
电源电路设计
因为项目内有舵机、MQ2模块都需要5V电压,所以锂电池选用5V电压,并且舵机旋转所需要的电流比较大,所以用5V 1A 的锂电池。
然后5V电流经过3.3V稳压电路供给核心控制板和项目中其他部分。
而锂电池中的电会用完,所以需要设计一个锂电池充电电路。
用TP4056单节锂电池充电器,参照原理图运用图设计电路。充电口使用比较常见的Type-C,这样就解决了锂电池充电问题。
稳压电路设计
5V-3V稳压芯片选用HT711-1,静态电流小,功耗小。不建议用AMS1117,因为功耗大,锂电池的电不够它耗的。
稳压电路出来的3.3V电流最好经过几个滤波电路,再供给核心控制板。
蜂鸣器电路设计
蜂鸣器可以选用有源蜂鸣器和无源蜂鸣器。如果是有源蜂鸣器,硬件比较简单,代码编写也会比较简单,就是发出的声音比较单一。无源蜂鸣器可以控制声音的频率。
有源蜂鸣器只需要一端接芯片IO口,一端接地,IO口设置推挽输出,高电平蜂鸣器响,低电平不响。
无源蜂鸣器控制电路如下,需要使用PNP三极管作开关管。
4×4矩阵键盘设计
对于矩阵键盘,有三种代码编写方式,一种是循环,一种是中断,还有一种是定时器。
循环比较低效,但思维简单,适合初学。定时器虽然高效,但对于其他系统正在执行的任务不太友好,会打断其他正在执行的任务。
还有一些其他的硬件设计需要注意,比如ESP8266的连接,TX–RX,RX–TX,需要交叉连接。
AT24C02的硬件连接需要注意要接两个上拉电阻,不然会显示busy p…
PCB布局、布线
这里没有高频电路,所以布局布线比较简单。
驱动代码编写
AT24C02驱动代码
AT24C02是使用IIC与单片机进行通信的,所以先要初始化IIC。
初始化代码使用STM32CUBEMX生成就行。将MX_I2C1_Init();添加到main中便可开启。
以下是驱动代码:
// 向 AT24C02 的指定地址写入一个字节
HAL_StatusTypeDef AT24C02_WriteByte(I2C_HandleTypeDef *hi2c, uint16_t MemAddress, uint8_t data) {
uint8_t buffer[2];
buffer[0] = MemAddress; // 存储字节地址
buffer[1] = data; // 存储数据
return HAL_I2C_Master_Transmit(&hi2c1, AT24C02_Write_ADD, buffer, 2, 100); // 发送设备地址、字节地址和数据
}
// 从 AT24C02 的指定地址读取多个字节
HAL_StatusTypeDef AT24C02_ReadByte(I2C_HandleTypeDef *hi2c, uint16_t MemAddress,uint8_t size,uint8_t *data) {
if (HAL_I2C_Master_Transmit(&hi2c1, AT24C02_Write_ADD, (uint8_t*)&MemAddress, 1, 100)!= HAL_OK) { // 发送设备地址和字节地址
return HAL_ERROR;
}
return HAL_I2C_Master_Receive(&hi2c1,AT24C02_Read_ADD, data, size, 100); // 重新发起起始条件,读取数据
}
// 向 AT24C02 的指定地址写入多个字节
HAL_StatusTypeDef AT24C02_WriteBytes(I2C_HandleTypeDef *hi2c, uint16_t MemAddress, const uint8_t *data, uint16_t size) {
uint8_t buffer[8 + 1];
uint16_t i, bytes_to_write;
HAL_StatusTypeDef status;
for (i = 0; i < size; i += bytes_to_write) {
bytes_to_write = (size - i) > 8? 8 : (size - i);
buffer[0] = MemAddress + i; // 当前页的起始地址
memcpy(&buffer[1], &data[i], bytes_to_write); // 复制数据到缓冲区
status = HAL_I2C_Master_Transmit(hi2c, AT24C02_Write_ADD, buffer, bytes_to_write + 1, 100); // 发送设备地址、字节地址和数据
if (status!= HAL_OK) {
return status;
}
HAL_Delay(5); // 等待写入完成,可根据实际情况调整延迟时间
}
return HAL_OK;
}
TFT屏幕驱动代码
TFT屏幕使用SPI协议与单片机进行通讯,同样使用CubeMx添加初始化代码,并在main中添加开启SPI。
然后最基础函数就是写数据,写命令。
void write_com(uint8_t* command,int size){
int i=0;
if(size==0){ //需要我这边统计,约定command以 0x71,0xFF结尾
for(i=0;command[i]!=0x71&&command[i+1]!=0xFF;i++){
}
size = i-1;
}
//0 _命令,1——数据
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1,command,size,600);
}
void write_dat(uint8_t* date,int size){
int i=0;
if(size==0){ //需要我这边统计,约定command以 0x71,0xFF结尾
for(i=0;date[i]!=0x71&&date[i+1]!=0xFF;i++){
}
size = i-1;
}
//0 _命令,1——数据
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
HAL_SPI_Transmit(&hspi1,date,size,600);
}
接着就是看屏幕生产厂商给的数据手册,编写屏幕初始化函数,设置地址函数,以及画点,画线,输出字符串,输出数字,输出中文等。
注意屏幕初始化函数如果是对的,那么屏幕就会由花屏变为你设置的颜色,如果出错,可能是基础的写数据,写命令错了,检查没错就换个初始化命令。因为TFT屏幕的驱动芯片不同,所以初始化也有不同,我的是ST7735S,一般再TFT屏幕背后有写。
void init_LCD(){
write_com((uint8_t []){0x11},1);
HAL_Delay(120);
write_com((uint8_t []){0xB1},1);
write_dat((uint8_t []){0x05,0x3C,0x3C},3);
write_com((uint8_t []){0xC0},1);
write_dat((uint8_t []){0x28,0x08,0x04},3);
write_com((uint8_t []){0x36},1);
write_dat((uint8_t []){0xC0},1);
write_com((uint8_t []){0x3A},1);
write_dat((uint8_t []){0x05},1);
write_com((uint8_t []){0x29},1); //使能显示
}
void lcd_set_address(uint16_t x_start,uint16_t x_end,uint16_t y_start,uint16_t y_end){
write_com((uint8_t []){0x2A},1);
write_dat((uint8_t []){x_start>>8,x_start&0xFF,x_end>>8,x_end&0xFF},4);
write_com((uint8_t []){0x2B},1);
write_dat((uint8_t []){y_start>>8,y_start&0xFF,y_end>>8,y_end&0xFF},4);
write_com((uint8_t []){0x2C},1);
}
void lcd_clear(uint16_t color){
lcd_set_address(0,128,0,160);
for(unsigned char i=0;i<128;i++){
for(unsigned char j = 0;j<160;j++){
write_dat((uint8_t []){color>>8,color&0xFF},2);
}
}
}
void showImage(const unsigned char *p){
int i=0;
unsigned char picH,picL;
lcd_set_address(0,128,0,160);
for(i=0;i<128*126;i++){
picL = *(p+i*2);
picH = *(p+i*2+1);
write_dat((uint8_t []){picH,picL},2);
}
}
void lcd_fill(uint16_t sx,uint16_t ex,uint16_t sy,uint16_t ey,uint16_t color){
unsigned short i,j;
uint8_t xlen = ex-sx+1;
uint8_t ylen = ey-sy+1;
lcd_set_address(sx,ex,sy,ey);
for(i=0;i<xlen;i++){
for(j=0;j<ylen;j++){
write_dat((uint8_t []){color>>8,color&0xFF},2);
}
}
}
void LCD_DrawPoint(uint16_t x,uint16_t y,uint16_t color)
{
lcd_set_address(x,x,y,y);//设置光标位置
write_dat((uint8_t []){color},1);
}
void LCD_ShowChar(uint16_t x,uint16_t y,uint8_t num,uint16_t fc,uint16_t bc,uint8_t sizey,uint8_t mode)
{
uint8_t temp,sizex,t,m=0;
uint16_t i,TypefaceNum;//一个字符所占字节大小
uint16_t x0=x;
sizex=sizey/2;
TypefaceNum=(sizex/8+((sizex%8)?1:0))*sizey;
num=num-' '; //得到偏移后的值
lcd_set_address(x,x+sizex-1,y,y+sizey-1); //设置光标位置
for(i=0;i<TypefaceNum;i++)
{
if(sizey==12)temp=ascii_1206[num][i]; //调用6x12字体
else if(sizey==16)temp=ascii_1608[num][i]; //调用8x16字体
else if(sizey==24)temp=ascii_2412[num][i]; //调用12x24字体
else if(sizey==32)temp=ascii_3216[num][i]; //调用16x32字体
else return;
for(t=0;t<8;t++)
{
if(!mode)//非叠加模式
{
if(temp&(0x01<<t)) write_dat((uint8_t []){fc>>8,fc&0xFF},2);
else write_dat((uint8_t []){bc>>8,bc&0xFF},2);
m++;
if(m%sizex==0)
{
m=0;
break;
}
}
else//叠加模式
{
if(temp&(0x01<<t))LCD_DrawPoint(x,y,fc);//画一个点
x++;
if((x-x0)==sizex)
{
x=x0;
y++;
break;
}
}
}
}
}
void LCD_ShowString(uint16_t x, uint16_t y, const uint8_t *p, uint16_t fc, uint16_t bc, uint8_t sizey, uint8_t mode) {
uint16_t x_start = x; // 保存起始X坐标
uint8_t char_width = sizey / 2; // 字符宽度(假设为等宽字体)
uint16_t screen_width = 128; // 屏幕宽度
uint16_t screen_height = 160; // 屏幕高度
while (*p != '\0') {
// 检查是否需要换行
if (x + char_width > screen_width) {
x = x_start; // X回到起始位置
y += sizey; // Y下移一行
// 检查是否超出屏幕底部
if (y + sizey > screen_height) {
break; // 超出屏幕高度,停止显示
}
}
// 显示当前字符
LCD_ShowChar(x, y, *p, fc, bc, sizey, mode);
// 更新下一个字符的X坐标
x += char_width;
p++;
}
}
uint32_t mypow(uint8_t m,uint8_t n)
{
uint32_t result=1;
while(n--)result*=m;
return result;
}
void LCD_ShowIntNum(uint16_t x,uint16_t y,uint16_t num,uint8_t len,uint16_t fc,uint16_t bc,uint8_t sizey)
{
uint8_t t,temp;
uint8_t enshow=0;
uint8_t sizex=sizey/2;
for(t=0;t<len;t++)
{
temp=(num/mypow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
LCD_ShowChar(x+t*sizex,y,' ',fc,bc,sizey,0);
continue;
}else enshow=1;
}
LCD_ShowChar(x+t*sizex,y,temp+48,fc,bc,sizey,0);
}
}
void LCD_ShowFloatNum1(uint16_t x,uint16_t y,float num,uint8_t len,uint16_t fc,uint16_t bc,uint8_t sizey)
{
uint8_t t,temp,sizex;
uint16_t num1;
sizex=sizey/2;
num1=num*100;
for(t=0;t<len;t++)
{
temp=(num1/mypow(10,len-t-1))%10;
if(t==(len-2))
{
LCD_ShowChar(x+(len-2)*sizex,y,'.',fc,bc,sizey,0);
t++;
len+=1;
}
LCD_ShowChar(x+t*sizex,y,temp+48,fc,bc,sizey,0);
}
}
void TFT_display_char16_16(const uint8_t *address ,uint16_t startX,uint16_t startY,uint16_t color)
{
unsigned int column;
unsigned char tm=0,temp;
lcd_set_address(startX,startX+15,startY,startY+15); //设置光标位置
for(column = 0; column < 32; column++) //column loop
{
temp =* address;
for(tm = 0; tm < 8; tm++)
{
if(temp&0x01)
{
write_dat((uint8_t []){color>>8,color},2);
}
else
{
write_dat((uint8_t []){0xff,0xff},2);
}
temp >>= 1;
}
address++;
}
}
void Chinese_Show_one(uint8_t x,uint8_t y,uint8_t num,uint8_t fc,uint8_t bc,uint8_t size,uint8_t mode)
{
TFT_display_char16_16(chinese_one_16[num],x,y,fc);
}
舵机驱动代码
舵机本质是使用PWM波去控制旋转角度,所以我们可以使用单片机的定时器生成PWM波形。
void MX_TIM3_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim3.Instance = TIM3;
htim3.Init.Prescaler = 799;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1999;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim3);
}
因为舵机的计数周期是20ms,所以这里我们的计数周期就是20ms,我们设置比较值为舵机旋转角度的几个值就可以驱动了。
void spin_45N(){
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,100);
HAL_Delay(1000);
}
void spin_0(){
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,150);
HAL_Delay(1000);
}
ESP8266驱动代码
ESP8266是使用串口进行通讯的,然后用CubeMx进行初始化设置,波特率设置为115200。
串口接收是使用FIFO循环队列接收更好,如果和我一样不想麻烦,就可以把接收数组设置的更大,每次接收后记得清空。
// 串口发送字符串函数
HAL_StatusTypeDef UART_SendString(UART_HandleTypeDef *huart, const char *str,uint16_t timeout) {
return HAL_UART_Transmit(huart, (uint8_t *)str, strlen(str), timeout); // 超时时间设置为 100 毫秒
}
// 重写 HAL 库的接收完成回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
// if (huart == &huart1) {
// 检测到结束符
// HAL_UART_Receive_IT(huart, &rxBuffer[rxIndex], 1);
// rxIndex+=1;
//}
if (huart == &huart1) {
// 存储接收到的数据并更新索引
HAL_UART_Receive_IT(&huart1,&rxBuffer[rxIndex], 1); // 启动首次接收
rxIndex+=1;
if (rxIndex > 800) {
// 缓冲区已满,处理溢出情况
rxIndex = 0;
}
}
}
int waitRes(const char *send_str, const uint8_t *target_str,uint16_t tarlen,uint16_t timeout){
uint8_t flag=1;
int time= 0;
while(flag){
UART_SendString(&huart1,send_str,timeout);
HAL_Delay(1000);
if(findStringInBuffer(rxBuffer,rxIndex,target_str,tarlen) != -1){
return 1;//发送成功
}else{
time++;
if(time == 9){
flag = 0;
}
}
}
return 0;
}
void init_WIFI(){
int index = 0;
uint8_t ip[16] = {0};
LCD_ShowString(0,20,"wait...",0x4321,0xFFFF,16,0);
if(waitRes("AT+RST\r\n","OK",2,500) == 1){
HAL_Delay(500);
rxIndex = 0;
if(waitRes("AT+CWMODE=1\r\n","OK",2,1000) == 1){
HAL_Delay(500);
rxIndex = 0;
if(waitRes("AT+CWJAP=\"Lock_WIFI\",\"76120922\"\r\n","GOT IP",6,1000)==1){
HAL_Delay(500);
rxIndex = 0;
if(waitRes("AT+CIPSNTPCFG=1,8,\"ntp1.aliyun.com\"\r\n","OK",2,1000) == 1){
HAL_Delay(500);
rxIndex = 0;
if(waitRes("AT+MQTTUSERCFG=0,1,\"NULL\",\"clock1&k1dpwES8Rlf\",\"4b80043f83be56dd8ce679bba65b3c878faccc3704db91108913d11a94d95d54\",0,0,\"\"\r\n","OK",2,2000)==1){
HAL_Delay(500);
rxIndex = 0;
if(waitRes("AT+MQTTCLIENTID=0,\"k1dpwES8Rlf.clock1|securemode=2\\,signmethod=hmacsha256\\,timestamp=1744112409660|\"\r\n","OK",2,1000)==1){
HAL_Delay(1000);
rxIndex = 0;
if(waitRes("AT+MQTTCONN=0,\"iot-06z00hp847mbhm8.mqtt.iothub.aliyuncs.com\",1883,1\r\n","OK",2,2000)==1){
HAL_Delay(500);
rxIndex = 0;
//订阅
if(waitRes("AT+MQTTSUB=0,\"/k1dpwES8Rlf/clock1/user/esp8266\",1\r\n","OK",2,1000)){
LCD_ShowString(0,20," ",0x4321,0x0000,16,0);
LCD_ShowString(0,50,"Connect Success!",0x4321,0xFFFF,16,0);
HAL_Delay(1000);
LCD_ShowString(0,50," ",0x4321,0x0000,16,0);
HAL_Delay(1000);
LCD_ShowString(0,50,"Connect Success!",0x4321,0xFFFF,16,0);
HAL_Delay(1000);
LCD_ShowString(0,50," ",0x4321,0x0000,16,0);
}
}
}
}
}
}
}
}
这里通过rxIndex可以判断是否有数据接收完成。然后在主函数中循环判断。
int ESP01S_WaitRecive(void)
{
static int rxIndex_pre=0;
if(rxIndex == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
return 0;
if(rxIndex == rxIndex_pre) //如果上一次的值和这次相同,则说明接收完毕
{
return 1; //返回接收完成标志
}
rxIndex_pre = rxIndex; //置为相同
return 0; //返回接收未完成标志
}
微信小程序端连接
微信小程序端引入mqtt.min.js,然后我在app.js中创建mqtt连接,接入阿里云平台,然后收到消息后使用监听器将消息传到各个页面。
以下代码仅供参考,连接阿里云的三元组需要替换成你们自己的。
// app.js
var mqtt = require('/utils/mqtt.min.js') //根据自己存放的路径修改
const crypto = require('/utils/hex_hmac_sha1.js'); //根据自己存放的路径修改
App({
onLoad: function () {
console.log("连接mqtt123")
if(!this.globalData.options){
this.doConnect()
}
},
onLaunch: function () {
if (!wx.cloud) {
console.error('请使用 2.2.3 或以上的基础库以使用云能力');
} else {
wx.cloud.init({
env: 'cloud1-6gulvjj85a8a7891',
traceUser: true,
});
}
if(!this.globalData.options){
this.doConnect()
}
},
// 注册监听器函数的方法
registerListener: function (listener) {
this.globalData.listeners.push(listener);
},
// 触发监听器函数的方法
triggerListeners: function () {
var listeners = this.globalData.listeners;
for (var i = 0; i < listeners.length; i++) {
listeners[i]();
}
},
doConnect(){
const deviceConfig = {
productKey: "",
deviceName: "",
deviceSecret: "",
regionId: "cn-shanghai",//根据自己的区域替换
reconnectPeriod: 1000 // 禁用自动重连
};
const options = this.initMqttOptions(deviceConfig);
const that = this
this.globalData.options = options
console.log(options)
//替换productKey为你自己的产品的(注意这里是wxs,不是wss,否则你可能会碰到ws不是构造函数的错误)
const client = mqtt.connect('wxs://productKey.iot-as-mqtt.cn-shanghai.aliyuncs.com:443',options)
this.globalData.client = client
client.on('connect', function () {
wx.showToast({
title: 'Connect...'
})
console.log('连接服务器成功')
//注意:订阅主题,替换productKey和deviceName(这里的主题可能会不一样,具体请查看控制台-产品详情-Topic 类列表下的可订阅主题),并且确保改主题的权限设置为可订阅
client.subscribe('/productKey/lockWeChat/user/get', function (err) {
if (!err) {
console.log('订阅成功!');
}
})
})
client.on('disconnect', () => {
console.log('代理断开连接')
console.log('连接断开,原因:', reason);
// 自动触发重连(通过 doConnect 重新初始化参数)
setTimeout(() => that.doConnect(), deviceConfig.reconnectPeriod);
})
// 新增:监听重连事件(关键)
client.on('reconnect', (error) => {
console.log('正在重连 MQTT:', error);
// 可在此处重新生成连接参数(如需)
});
//接收消息监听
client.on('message', function (topic, message) {
// message is Buffer
let msg = message.toString();
console.log('收到消息:'+msg);
const messa = JSON.parse(message);
if(messa.params && messa.params.receive_act == 1){ //收到应答
that.globalData.receive_act = true
}
if(messa.params && messa.params.addCode_act == 1){ //收到确认增加信息
console.log("收到确认增加信息")
that.globalData.addCode_act = true
that.triggerListeners();
}
console.log(messa.lockStatus)
if(messa && messa.lockStatus == 1 || messa.lockStatus == 2){ //收到解锁信息
console.log("收到解锁信息")
that.globalData.openLock_act = true
that.globalData.openLock_info = messa
that.globalData.openLock_readMess[messa.lockUser] = 1
that.triggerListeners();
}
if(messa.params && messa.params.update_act == 1){ //收到更新执行状态信息
console.log("收到更新执行状态信息")
that.globalData.updatecode_act = true
that.globalData.updatacode_status = messa.params.update_act
that.triggerListeners();
}
if(messa.params && messa.params.delete_act == 1){ //收到删除执行状态信息
console.log("收到删除执行状态信息")
console.log(messa.params)
that.globalData.deletecode_act = true
that.globalData.deletecode_status = messa.params.delete_act
that.triggerListeners();
}
if(messa.params && (messa.params.air_warning == 1 || messa.params.air_warning == 2) ){ //收到空气质量消息
console.log("收到空气质量消息")
console.log(messa.params)
that.globalData.air_warning = messa.params.air_warning
if(messa.params.air_warning == 2){ //
wx.showToast({
title: '空气质量异常',
icon: 'error'
})
}
that.triggerListeners();
}
//关闭连接 client.end()
})
client.on('close', () => {
console.log('断开连接')
})
},
//IoT平台mqtt连接参数初始化
initMqttOptions(deviceConfig) {
const params = {
productKey: deviceConfig.productKey,
deviceName: deviceConfig.deviceName,
timestamp: Date.now(),
clientId: Math.random().toString(36).substr(2),
}
//CONNECT参数
const options = {
keepalive: 180, //60s
clean: false, //cleanSession不保持持久会话
protocolVersion: 4, //MQTT v3.1.1
reconnectPeriod: 1000,
connectTimeout: 30*1000
}
//1.生成clientId,username,password
options.password = this.signHmacSha1(params, deviceConfig.deviceSecret);
options.clientId = `${params.clientId}|securemode=2,signmethod=hmacsha1,timestamp=${params.timestamp}|`;
options.username = `${params.deviceName}&${params.productKey}`;
console.log(options.clientId)
console.log("4568")
return options;
},
//发送消息
mqttSend(url,msg) {
console.log(this.globalData.client)
this.globalData.client.publish(url, msg, { qos: 2 }, (err) => {
console.log('send', err);
});
},
signHmacSha1(params, deviceSecret) {
let keys = Object.keys(params).sort();
// 按字典序排序
keys = keys.sort();
const list = [];
keys.map((key) => {
list.push(`${key}${params[key]}`);
});
const contentStr = list.join('');
return crypto.hex_hmac_sha1(deviceSecret, contentStr);
},
onHide(){
}
});
连接注意的事项
在硬件端,电压不稳会造成ESP8266发送不了消息,并且在阿里云监控界面会看见下线,所以最好用5V锂电池供电。
微信小程序端如果使用真机调试,记得把模拟调试窗口关闭,不然两个mqtt连接相撞,就会导致接收不到消息,或者是一直反复重连。
然后微信小程序可以多使用console.log去打印一些参数,也可以看见程序执行到哪,哪里的逻辑有问题。
产品测试图
作者:ABUCC7