1239|1

4

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

物联网智能家居系统 [复制链接]

  本帖最后由 萧风吹衣 于 2022-10-23 08:58 编辑

一、作品简介(100-200字)

本作品名称是物联网智能家居系统,主机、从机使用的是STM32芯片,两者之间通过CAN总线进行通信,保证数据传输的可靠性,从而提高整个系统的安全性。主机采集温湿度信息,上传到阿里云平台,通过Web查看相关数据。此外,K210负责进行人脸识别,并将结果通过串口发送到主机,主机再将数据通过CAN发送给从机,从机控制门禁继电器。

二、系统框图(图文结合)
(系统软硬件实现框图)

硬件:STM32F429I-DISC1(主机)

      STM32F103C8T6(从机)

      K210(人脸识别模块)

      ESP8266(WIFI模块)

      DHT11(温湿度采集模块)

      SN65HVD230(CAN通信模块)

 

开发工具:Keil+STM32CubeMx

软件:软件框架基于FreeRTOS嵌入式实时操作系统,进行任务调度实现项目功能

      任务1:主机闪烁LD3,指示系统运行状态

      任务2:主机将K210人脸识别结果发送到CAN总线,从机接收信息,控制继电器

      任务3:主机通过ESP8266上传温湿度信息到阿里云

      任务4:主机通过DHT11采集温湿度信息

 

三、各部分功能说明(图文结合)

(各部分实现的功能说明及讲解,以图文结合的展示。)

  1. 温湿度采集模块(DHT11)

硬件接线如下图所示,5V电源、GND、DATA接到主机PD4引脚上进行数据传输

 

软件上的主要问题就是DHT11在进行数据传输时需要用到微秒级延时,具体做法就是使用HAL库建立一个微秒级延时函数

用定时器1是给FreeRTOS使用的,这里的用定时器2创建微秒级延时函数给DHT11用,具体配置如下

 生成代码如下:

 在任务4中调用DHT11_Read_Data读取温湿度数据

#include "dht11.h"
#include "tim.h"

unsigned char Data[5];

//复位DHT11
void DHT11_Rst(void)	   
{                 
	DHT11_IO_OUT(); 	//设置为输出
	DHT11_DQ_OUT=0; 	//拉低DQ
	bsp_delay_us(18000);    	//拉低至少18ms
	DHT11_DQ_OUT=1; 	//DQ=1 
	bsp_delay_us(30);     	//主机拉高20~40us        微秒级延时
}

//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();      //设置为输出	 
	while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
	{
		retry++;
		bsp_delay_us(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
	while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
	{
		retry++;
		bsp_delay_us(1);
	};
	if(retry>=100)return 1;	    
	return 0;
}

//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)//等待变为低电平
	{
		retry++;
		bsp_delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
	{
		retry++;
		bsp_delay_us(1);
	}
	bsp_delay_us(40);//等待40us
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}

//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
	u8 i,dat;
	dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
    }						    
    return dat;
}

//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp1,u8 *temp2,u8 *humi1,u8 *humi2)    
{
	u8 buf[5];
	u8 i;
	DHT11_Rst();
	if(DHT11_Check()==0)
	{
		for(i=0;i<5;i++)//读取40位数据
		{
			buf=DHT11_Read_Byte();
		}
		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
		{
			*humi1=buf[0];
			*humi2=buf[1];
			*temp1=buf[2];
			*temp2=buf[3];
		}
	}else return 1;
	return 0;    
}

//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在     	 
u8 DHT11_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_GPIOD_CLK_ENABLE();			//开启GPIOD时钟
	
    GPIO_Initure.Pin=GPIO_PIN_4;           //PD4
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOD,&GPIO_Initure);     //初始化
 
    DHT11_Rst();
	 return DHT11_Check();
}

 

  1. WIFI模块(ESP8266)+阿里云

这部分难点在于通过AT指令连接阿里云,以及上传数据的格式

ESP8266连接阿里云的顺序大致可以分为以下几个步骤

配置模块为STA模式连接热点连接TCP配置传输模式为透传模式验证用户名与密钥订阅主题发送心跳包接收/发送数据

(1)通过三元组数据连接到阿里云,测试过程如下


      AT+CWMODE=1

     AT+CIPSNTPCFG=1,8,"ntp1.aliyun.com"

     AT+CWJAP="ChinaNet-B47A","42q7unu7"

 

 


AT+MQTTUSERCFG=0,1,”NULL”,”TestDevice01&hhpbfI5ahHV”,”6e01e2488d9facabdc11800ee750b2bcbfd99449”,0,0,””


AT+MQTTCLIENTID=0,”ESP8266|securemode=3\,signmethod=hmacsha1|”

 


AT+MQTTCONN=0,”hhpbfI5ahHV.iot-as-mqtt.cn-shanghai.aliyuncs.com”,1883,1

 通过上述过程,就将设备连接到阿里云平台

(2)主机订阅,云端发布数据


AT+MQTTSUB=0,"/sys/hhpbfI5ahHV/TestDevice01/thing/service/property/set",1

 

 

 

(3)主机发布数据,云端订阅


AT+MQTTPUB=0,"/sys/hhpbfI5ahHV/TestDevice01/thing/event/property/post",“test”,1,0

 

 

 


AT+MQTTPUB=0,

"/sys/hhpbfI5ahHV/TestDevice01/thing/event/property/post",

"{

\"method\":\"thing.service.property.set\"\,

\"id\":\"2012934115\"\,

\"params\":{\"temperature\":36.5}\,

\"version\":\"1.0.0\"

}",

1,0

 

需要点击获取

 

 

需要点击获取

 

自动更新数据

 

通过以上介绍,可以实现数据上传到阿里云

  1. 人脸识别模块K210 

这部分难点在于人脸识别的精度以及K210与主机通信

人脸识别原理及代码如下


import sensor
import image
import lcd
import KPU as kpu
import time
from Maix import FPIOA, GPIO
import gc
from fpioa_manager import fm
#from board import board_info
import utime
#import 相关库
 
task_fd = kpu.load(0x300000) #从flash 0 0x300000 加载人脸检测模型
task_ld = kpu.load(0x400000) #从flash 0 0x400000 加载人脸五点关键点检测模型
task_fe = kpu.load(0x500000) #从flash 0 0x500000 加载人脸196维特征值模型
 
clock = time.clock()  #初始化系统时钟,计算帧率
 
fm.register(16, fm.fpioa.GPIOHS0) #设置 boot按键 的io
key_gpio = GPIO(GPIO.GPIOHS0, GPIO.IN)
start_processing = False
 
BOUNCE_PROTECTION = 50
 
 
def set_key_state(*_):  #按键中断
    global start_processing
    start_processing = True
    utime.sleep_ms(BOUNCE_PROTECTION)
 
 
key_gpio.irq(set_key_state, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT)
 
lcd.init() #初始化 lcd
sensor.reset()#初始化sensor摄像头
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_hmirror(1) #设置摄像头镜像
sensor.set_vflip(1) #设置摄像头翻转
sensor.run(1) #试能摄像头
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437,
          6.92275, 6.718375, 9.01025)  # 人脸检测锚
dst_point = [(44, 59), (84, 59), (64, 82), (47, 105),
             (81, 105)]  # 标准人脸关键点位置  标准正脸的5关键点坐标 分别为 左眼 右眼 鼻子 左嘴角 右嘴角
a = kpu.init_yolo2(task_fd, 0.5, 0.3, 5, anchor) #初始化人脸检测模型
img_lcd = image.Image() #设置显示buf
img_face = image.Image(size=(128, 128))#设置 128*128 人脸图片buf
a = img_face.pix_to_ai() #将图片转换为kpu接受的格式
record_ftr = []  #空列表 用于存储当前196维特征值
record_ftrs = []  #空列表 用于存储按键记录下人脸特征,可以将特征以txt等文件形式保存到sd卡后,读取到此列表,即可实现人脸断电存储。
names = ['Mr.1', 'Mr.2', 'Mr.3', 'Mr.4', 'Mr.5',
         'Mr.6', 'Mr.7', 'Mr.8', 'Mr.9', 'Mr.10']     # # 人名标签,与上面列表特征值一一对应。
ACCURACY = 85   # 设定分数标准 人脸阈值
 
while (1):
    img = sensor.snapshot()
    clock.tick()
    code = kpu.run_yolo2(task_fd, img)  # 运行人脸检测模型,获取人脸坐标位置
    if code:  #如果检测到人脸
        for i in code: #迭代坐标框
            # Cut face and resize to 128x128
            a = img.draw_rectangle(i.rect()) #在屏幕显示人脸方框
            face_cut = img.cut(i.x(), i.y(), i.w(), i.h())  #裁剪人脸部分照片到 face_cut
            face_cut_128 = face_cut.resize(128, 128)  #将裁剪出的人脸照片缩放到 128*128 像素
            a = face_cut_128.pix_to_ai()   #将裁剪出的照片转换为kpu接受的格式
            # a = img.draw_image(face_cut_128, (0,0))
            
            # Landmark for face 5 points
            fmap = kpu.forward(task_ld, face_cut_128) #运行人脸5点关键点模型
            plist = fmap[:]  #获取关键点预测结果
            le = (i.x() + int(plist[0] * i.w() - 10), i.y() + int(plist[1] * i.h()))  #计算左眼位置,这里在w方向-10 用来补偿模型转换带来的精度损失
            re = (i.x() + int(plist[2] * i.w()), i.y() + int(plist[3] * i.h()))      #计算右眼位置
            nose = (i.x() + int(plist[4] * i.w()), i.y() + int(plist[5] * i.h()))    #计算鼻子位置
            lm = (i.x() + int(plist[6] * i.w()), i.y() + int(plist[7] * i.h()))      #计算左嘴角位置
            rm = (i.x() + int(plist[8] * i.w()), i.y() + int(plist[9] * i.h()))      #计算右嘴角位置
            #在相应位置处画小圈圈
            a = img.draw_circle(le[0], le[1], 4)
            a = img.draw_circle(re[0], re[1], 4)
            a = img.draw_circle(nose[0], nose[1], 4)
            a = img.draw_circle(lm[0], lm[1], 4)
            a = img.draw_circle(rm[0], rm[1], 4) 
            #在相应位置处画小圈圈
            
            # align face to standard position 将面对齐到标准位置
            src_point = [le, re, nose, lm, rm]  #图片中五点坐标的位置
            T = image.get_affine_transform(src_point, dst_point)  #根据获得的5点坐标和标准正脸坐标获取仿射变换矩阵
            a = image.warp_affine_ai(img, img_face, T)  #对原始图片人脸图片进行仿射变换,变换为正脸图像
            a = img_face.ai_to_pix() #将图片转换为kpu接受的格式
            # a = img.draw_image(img_face, (128,0))
            del (face_cut_128)  #释放裁剪人脸部分照片
            
            # calculate face feature vector  计算人脸特征向量
            fmap = kpu.forward(task_fe, img_face)  #计算正脸图片的196维特征值
            feature = kpu.face_encode(fmap[:]) #获得计算结果
            reg_flag = False
            scores = [] #存储特征比对分数
            for j in range(len(record_ftrs)):  #迭代已存特征值
                score = kpu.face_compare(record_ftrs[j], feature)  #计算当前人脸特征值与已存特征值的分数
                scores.append(score) #添加分数总表
            max_score = 0
            index = 0
            for k in range(len(scores)):  #迭代所有比对分数,找到最大分数和索引值
                if max_score < scores[k]:
                    max_score = scores[k]
                    index = k
            if max_score > ACCURACY:  #如果最大分数大于85, 可以被认定为同一个人
                a = img.draw_string(i.x(), i.y(), ("%s :%2.1f" % (
                    names[index], max_score)), color=(0, 255, 0), scale=2)     # 显示人名 与 分数
            else:
                a = img.draw_string(i.x(), i.y(), ("X :%2.1f" % (
                    max_score)), color=(255, 0, 0), scale=2)    #显示未知 与 分数
            if start_processing:
                record_ftr = feature
                record_ftrs.append(record_ftr)
                start_processing = False
 
            break
    fps = clock.fps()  #计算帧率
    print("%2.1f fps" % fps) #打印帧率
    a = lcd.display(img) #刷屏显示
    gc.collect()
    # kpu.memtest()
 
# a = kpu.deinit(task_fe)
# a = kpu.deinit(task_ld)
# a = kpu.deinit(task_fd)

识别前:

识别后:

 主机通信代码

if(huart->Instance == USART3)	// 判断是由哪个串口触发的中断  USART3   K210
	{
		printf("传输人脸识别结果!\r\n");
		
		//将接收到的数据放入接收usart3接收数组
		usart3_rxbuf[usart3_rxcounter] = usart3_rxone[0];
		usart3_rxcounter++;	//接收数量+1
		
		printf("%d \r\n",usart3_rxone[0]);
		
				
		if(usart3_rxone[0]==49)
		{	
			printf("人脸识别成功!\r\n");
			//通过CAN总线发送信息,打开继电器
			send_data[0]=1;
		}
		else if(usart3_rxone[0]==50)
		{
			printf("人脸识别失败!\r\n");
			send_data[0]=0;
		}
		
		//重新使能串口5接收中断
		HAL_UART_Receive_IT(&huart3,usart3_rxone,1);		
	}

 

  1. CAN总线数据传输

在CubeMX设置PB12   CAN2_RX

在CubeMX设置PB13   CAN2_TX

HAL库关于CAN的所有函数定义和结构体定义分别在 Drivers/STM32F4xx_HAL_Driver 文件夹下 stm32f4xx_hal_can.c 和 stm32f4xx_hal_spi.h 中

常用函数

HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan)**

开启CAN,一般在主函数中调用

HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig)

配置CAN过滤器

HAL_StatusTypeDef HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs)

使能中断

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)

接收回调函数,CAN回调后进行的操作。一般在此接收数据

 

HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox)**

增加一个消息到第一个空闲的 Tx 邮箱,并且激活对应的传输请求

HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[])

从Rx FIFO收取一个 CAN 帧

HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig)

设置接收过滤器

CAN收发思路

定义接收过滤器类型定义结构体(CAN_FilterTypeDef)变量

配置CAN接收过滤器HAL_CAN_ConfigFilter

开启CAN,HAL_CAN_Start

发送,HAL_CAN_AddTxMessage

接收,需要先开启中断HAL_CAN_ActivateNotification,第二个参数设置为CAN_IT_RX_FIFO0_MSG_PENDING

接收中断回调函数HAL_CAN_RxFifo0MsgPendingCallback,里面定义接收报文定义结构体(CAN_RxHeaderTypeDef)结构体变量。用接收函数HAL_CAN_GetRxMessage接收

 

根据上述介绍,主机发送数据到CAN总线上,从机接收数据,控制继电器的开合。

四、作品源码

 

五、作品功能演示视频

 

 

 

 

 

 

 

 

作品源码.rar

38.88 MB, 下载次数: 2

最新回复

真是不错,值得赞,关于硬件的搭配连接有没有小视频解说下呢   详情 回复 发表于 2022-10-23 12:05
点赞 关注(1)
 
 

回复
举报

4817

帖子

4

TA的资源

版主

沙发
 

真是不错,值得赞,关于硬件的搭配连接有没有小视频解说下呢

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表