资讯详情

基于STM32设计的WiFi语音播报日程表

1. 前言

近年来,随着电子产品的发展,数字时间表的应用在人们的工作和生活中发挥着越来越重要的作用。时间对人们来说总是那么宝贵,忙碌和复杂的工作很容易让人们忘记当前的时间,忘记他们想做什么,当事情不是很重要时,这种遗忘是无害的。然而,遇到重要的事情,暂时的延迟可能会导致一场灾难。

因此,从人们的日常生活到公司办公,从台式电脑到便携式智能手机,都需要标准的时间表。人们要求随时随地快速准确地提醒当前事务,并要求时间表更直观、更可靠、更便宜。这一要求催生了新的时间表。此外,由于更多的社会责任,人们要求设计的产品产生尽可能少的垃圾,消耗尽可能少的能量。因此,人们对时间表有体积小、功耗低的要求。

2. 功能需求

2.1 硬件部分

整个项目在正点原子stm32f103mini在开发板环境下进行。开发板的主控制是stm32f103rct6。

(1)2.8寸tft触摸屏负责显示和交互;

(2)pcf8563t用作时钟计时,并在屏幕上显示日期和时间。

(3)24小时显示时间,上位机支持设备端RTC更新日期和时间信息.

(4)DHT11温湿度传感器检测环境信息,并在液晶屏上显示信息;

(5)使用esp8266作WiFi模块与手机app进行通信;

(6)单片机存储接收到的内容w25Q64内,同时可以在屏幕上指定位置将内容显示出来。内容包括具体日程的文字内容以及日程开始、结束的时间;

(7)使用蜂鸣器并通过syn6288语音芯片合成的语音信息通过喇叭播放实现提醒功能。蜂鸣器和喇叭在议程开始和结束前五分钟提前发出语音提示(可以修改此提醒时间)。

(8)显示屏(横屏显示)应包括: 基本的日期、时间、温度和湿度信息显示,屏幕主体部分通过列表表显示app端接收到的时间表内容;在屏幕上设置一个触摸屏按钮,可以跳过当前或未开始的下一个时间表。

因为2.8英寸显示屏的空间有限,因此在同一屏幕范围内只显示一到两个时间表的具体内容。需要显示时间表的文本内容、开始和结束时间。当时间表即将开始和结束时触发语音提示;时间表结束后,从当前显示位置删除w25Q存储在64上的信息。同时显示下一个待开始的时间表。

显示的文本部分需要能够支持显示16和24个大小的所有字符,包括中文字符果不兼容,可以做成24个大小)。从手机上发送的中文文本信息应存储在屏幕上w25q64内。单片机终端应至少支持15个日程内容的存储。单片机应能够识别具体时间表的时间信息,并根据时间排序判断时间表是否过时,并直接删除过时时间表。删除和跳过时间表不需要触发提示。

(9)语音提示内容: 1:如果您需要开始时间表,请注意时间表。(如果可以阅读手机输入的时间表内容(主要是汉字),则此句改为:下一个时间表:XXX请注意时间。(XXX日程内容))

2:当前日程即将结束

3:连接成功(成功连接)app时播报)

4:连接失败

5:日程已设置(单片机接收手机发送的日程内容)。

(10)每句提示播报前蜂鸣器响一声,响完后停顿一秒再播报。

2.2 软件部分

软件部分主要是控制手机app,手机的app包括可以选择添加时间表的按钮,可以输入信息的文本框,可以将文本框中的内容发送到单片机的按钮。能同时在app查看单片机终端输入的时间表内容,并添加可删除输入内容的按钮。文本框分为三部分,一部分输入文本内容(大约两到八个汉字字符的长度)。)输入开始时间的文本框,输入结束时间的文本框。

2.3 功能总结

(1)STM32采用正点原子mini板。正原子2.8寸tft触摸屏

(2)pcf8563t用作时钟计时,并在屏幕上显示日期和时间

(3)DHT11温湿度传感器检测环境信息,并在液晶屏上显示信息

(4)使用esp8266作WiFi模块与手机app进行通信;

(5)w25Q64 烧录字库,存储字库,存储日程提醒信息 实现思路: 将W25Q64安装FATFS文件系统便于数据存储和读取,读写日程信息和字库信息。

(6)syn6288语音芯片合成的语音信息通过喇叭播放提醒功能

(7)开发手机APP输入提醒日程,单片机接收手机发送的日程内容。 单片机存储接收到的内容w25Q在64内,内容可以在屏幕上指定的位置显示。内容包括具体时间表的文本内容和时间表的开始和结束时间;时间表信息以文件的形式存储,修改、阅读和写作为文件操作。

3. 软件运行效果

打开软件后,输入设备端IP功能操作可在连接成功后进行地址和端口。

查看软件上的页面(即主页)、日志页面、新的日程提醒页面。

软件分为windows桌面版本和Android手机版,以下演示截图windows以桌面版为例。

软件采用QT设计,Qt Creator是跨平台的 Qt IDE, Qt Creator 是 Qt 被 Nokia 收购后推出的新型轻量级集成开发环境(IDE)。此 IDE 可跨平台运行,支持系统包括 Linux(32 位及 64 位)、Mac OS X 以及 Windows。

Qt Creator官网下载地址:Download Qt | Embedded System | Real Time Embedded Systems | Qt

QT所有版本下载地址:Index of /archive/qt

QT环境建设、入门开发专栏: https://blog.csdn.net/xiaolong1126626497/category_11400392.html

image-20220222141556523

提醒事件的内容可以填写在此页面上,输入提醒的开始时间和结束时间。提前提醒时,输入后单击新的提醒事件按钮可以将数据发送到设备端,并在主页上添加数据显示。

4. 通信协议

设备端与软件上位机之间的数据交互协议:

(1) 软件上位机对STM32发送: #update 让STM32发送当前存储的所有日程数据  STM32向上位机返回的数据格式: $update,提前提醒开始时间、结束时间、事件内容(0~59分钟) $update,2022/02/22 13:15,2022/02/23 12:17,吃饭,5

(2)
给STM32发送一条日程数据过去
格式:$add,起始时间,结束时间,事件内容,提前提醒时间(0~59分钟)
$add,2022/02/22 13:15,2022/02/23 12:17,吃饭,5

(3)
给STM32发送校准时间  *20220222131338

(4)
删除STM32上存储的日程数据
$del,起始时间,结束时间,事件内容,提前提醒时间(0~59分钟)
$del,2022/02/22 13:15,2022/02/23 12:17,吃饭,5

5. 测试流程总结

设备端采用ESP8266与上位机进行通信,ESP8266上电初始化为AP+TCP服务器模式,设置固定端口号。

采用电脑或者手机运行APP测试之前,先搜索ESP8266创建的WIFI热点连接上,然后打开软件,在软件里输入ESP8266服务器的IP地址和端口号点击连接,连接成功之后就可以与设备端进行交互。

如果没有设备端,也可以采用网络调试助手与上位机之前交互,测试功能。

6. 硬件部分

6.1 硬件实物

板子的串口正常提示:

6.2 外设硬件连线

(1) ESP8266 WIFI
PB10--->ESP8266-RX
PB11--->ESP8266-TX
3.3v--->VCC
GND---->GND


(2) SYN6628
PA2(TX)---SYN6628-RX
PA3(RX)---SYN6628-TX
3.3v---->VCC
GND----->GND


(3) DHT11 温湿度传感器
PA5 ---->DHT11-OUT
3.3v---->VCC
GND----->GND
    
剩下的用的硬件是开发板本身自带--正点原子STM32F1战舰V3开发板,硬件连接详情看原理图接口。

6.3 字库创建

6.4 SD卡上存放的字库文件

7. 设备端核心代码及实现思路

如果需要整个工程直接使用,可以去这里获取: https://download.csdn.net/download/xiaolong1126626497/85892788

7.1 字库读取

目前设备端LCD屏字库存放在SD卡上,通过fatfs文件系统读取字模进行显示,这样做的优点: 更换字库方便,直接把SD卡拔出来放在电脑上拷贝字库即可。

void NT35310_DisplayGBKData(u32 x,u32 y,u32 size,u8 *p,u16 c1,u16 c2)
{ 
        
      FIL fp;
	  UINT br;
	  u8 L,H;
	  u32 Addr;
	  u16 font_size=size/8*size; //字体占用的点阵码字节大小
	  u8 *buff=NULL;
		H=*p;
		L=*(p+1);
		if(L<0x7f)L=L-0x40;
		else L=L-0x41;
		H=H-0x81;
		Addr=(190*H+L)*font_size; //中文在字库里的偏移量
		buff=malloc(font_size);   //使用的堆空间
		if(buff==NULL)return;

		switch(size)
		{ 
        
			case 16:
				if(f_open(&fp,"0:SYSTEM/FONT/GBK16-H.DZK",FA_READ)!=FR_OK)
                { 
        
                      printf("f_open error.\r\n");
                }
				f_lseek(&fp,Addr);
				f_read(&fp,buff,font_size,&br);
				f_close(&fp);
               
				break;
			case 24:
                f_open(&fp,"0:SYSTEM/FONT/GBK24-H.DZK",FA_READ);
				f_lseek(&fp,Addr);
				f_read(&fp,buff,font_size,&br);
				f_close(&fp);
				break;
			case 32:
				
				break;
		}
		//显示中文
		NT35310_DisplayData(x,y,size,size,buff,c1,c2);
		
		//释放空间
		free(buff);
}
void NT35310_DisplayData(u32 x,u32 y,u32 w,u32 h,u8 *p,u16 c1,u16 c2)
{ 
        
   u16 i,j,x0=x;
   u8 data;
    u16 colortemp=POINT_COLOR;    
   for(i=0;i<w/8*h;i++) //取出的模型总字节数
   { 
        
       data=p[i]; //取出数组里一个字节的数据
			
       for(j=0;j<8;j++)
       { 
        
          if(data&0x80)Draw_Point(x0,y,c1); //字体颜色 
          else 
          { 
        
              Draw_Point(x0,y,c2); //背景颜色
          }
           
           
          data<<=1; //继续判断下一位
          x0++; //继续画下一个点
       }
       if(x0-x==w) //判断是否需要换行
       { 
        
          x0=x;//横坐标归位
          y++; //纵坐标自增
       }
   }
   POINT_COLOR=colortemp;	  
}

7.2 解析ESP8266数据

主函数里通过轮询方式检测,ESP8266是否收到上位机的命令,收到之后进行解析处理

//ESP8266 WIFI 返回的数据
if(USART3_RX_FLAG)
{ 
        
	USART3_RX_BUFFER[USART3_RX_CNT]='\0';
	printf("%s",USART3_RX_BUFFER);

	//解析WIFI返回的数据
	
	//如果是校准RTC时间 +IPD,0,15:*20220304220552
	if(strstr((char*)USART3_RX_BUFFER,":*"))
	{ 
        
		 printf("校准时间.\r\n");
		 rtc_time_update((char*)USART3_RX_BUFFER);
	}
	//如果是请求更新提醒
	else if(strstr((char*)USART3_RX_BUFFER,"#update"))
	{ 
        
		printf("请求更新事件.\r\n");
		update_enev();
	}
	//如果是新增提醒
	//+IPD,0,49:$add,2022/03/04 21:56,2022/03/04 21:56,
	else if(strstr((char*)USART3_RX_BUFFER,"$add"))
	{ 
        
		 printf("新增提醒事件.\r\n");
		 add_enev((char*)USART3_RX_BUFFER);
	}
	//如果是删除某个提醒
	//+IPD,0,49:$del,2022/03/04 21:56,2022/03/04 21:56,水水水水,0
	else if(strstr((char*)USART3_RX_BUFFER,"$del"))
	{ 
        
		 printf("删除某个提醒.\r\n");
		del_enev((char*)USART3_RX_BUFFER);
	}

	USART3_RX_CNT=0;
	USART3_RX_FLAG=0;
}

7.3 向SD卡存放事件信息

事件提醒都是存放在SD卡上,以文件的形式存放,上面封装的几个函数里,主要是就是读写文件。

/* 函数功能:从buf里面得到第cnt个逗号所在的位置 返 回 值:0~254,代表逗号所在位置的偏移. 255,代表不存在第cnt个逗号 */
u8 GetCommaOffset(char *buf,u8 cnt)
{ 
        
	char *p=buf;
	while(cnt)
	{ 
        
		if(*buf==',')cnt--;
		buf++;
	}
	return buf-p; //计算偏移量
}


/* 写文件 */
void FATFS_Write(const TCHAR *FileName,const char *WriteBuff)
{ 
        
    FIL fp;
    FRESULT res;
    UINT cnt; //存放写入成功的数量
  
    /*1. 创建文件*/  
    res=f_open(&fp,FileName, FA_WRITE | FA_CREATE_ALWAYS);
    if(res!=0)
    { 
        
        printf("%s文件创建失败!\n",FileName);
        return;
    }
    
    /*2. 写入数据*/
    res=f_write(&fp,WriteBuff,strlen(WriteBuff),&cnt);
    if(res!=0)
    { 
        
        printf("%s文件写入失败!\n",FileName);
        return;
    }
    
    /*3. 关闭文件*/
    f_close(&fp);
    
    printf("%s文件创建成功,成功写入:%d\n",FileName,cnt);
}

//提取指定逗号位置的数据
void GetCommaOffsetBuff(char *buf_in,char *buf_out,u8 cnt)
{ 
        
	while (cnt)
	{ 
        
		if (*buf_in == ',')cnt--;
		if (*buf_in == '\0')break;
		buf_in++;
	}
	
	while (*buf_in != ',' && *buf_in != '\0')
	{ 
        
		*buf_out=*buf_in;
		buf_in++;
		buf_out++;
	}
	*buf_out = '\0';
}

//字符串替换
//sub1替换前 sub2替换后字符
void StringSubstitution(char *p,char sub1,char sub2)
{ 
        
	while (*p!='\0')
	{ 
        
		if (*p == sub1)
		{ 
        
			*p = sub2;
		}
		p++;
	}
}


//新增提醒
 //+IPD,0,49:$add,2022/03/04 21:56,2022/03/04 21:56,
void add_enev(char *p)
{ 
        
   char buf_out[20];
	u8 offset = 0;
	char *p2;
	offset = GetCommaOffset(p, 3);
	p2 = p + offset;
	printf("%d,%s\r\n", offset, p2);

	//提取提醒的起始时间 例如:2022/03/04 21:56
	GetCommaOffsetBuff(p,buf_out,3);
	printf("buf_out1=%s\r\n",buf_out);

	StringSubstitution(buf_out,'/','-');
	StringSubstitution(buf_out, ' ','-');
	StringSubstitution(buf_out, ':','-');
	printf("buf_out2=%s\r\n", buf_out);

	strcat(buf_out,".ev");
	printf("buf_out3=%s\r\n", buf_out);

	FATFS_Write(buf_out, p2);
    
    //更新事件列表
    update_event_list();
}


//删除某个提醒
 //+IPD,0,49:$del,2022/03/04 21:56,2022/03/04 21:56,水水水水,0
void del_enev(char *p)
{ 
        
    char buf_out[20];
    char dir_file_path[50];
	//提取提醒的起始时间 例如:2022/03/04 21:56
	GetCommaOffsetBuff(p, buf_out, 3);
	printf("buf_out1=%s\n", buf_out);

	StringSubstitution(buf_out, '/', '-');
	StringSubstitution(buf_out, ' ', '-');
	StringSubstitution(buf_out, ':', '-');
	printf("buf_out2=%s\n", buf_out);

	strcat(buf_out, ".ev");
	printf("buf_out3=%s\n", buf_out);
    
    //拼接目录名称
    sprintf(dir_file_path,"0:/%s",buf_out);
    
    //删除文件
    if(f_unlink(dir_file_path)==FR_OK)
    { 
        
        printf("%s\r\n删除成功.",dir_file_path);
    }
    else
    { 
        
        printf("%s\r\n删除失败.",dir_file_path);
    }    
    
    //更新事件列表
    update_event_list();
}


//向APP终端更新提醒
//+IPD,0,7:#update
/* STM32向上位机返回的数据格式: $update,起始时间,结束时间,事件内容,提前提醒时间(0~59分钟) $update,2022/02/22 13:15,2022/02/23 12:17,吃饭,5 */
u8 update_enev(void)
{ 
        
// u8 buff[]="$update,2022/02/22 13:15,2022/02/23 12:17,吃饭,5";
// ESP8266_ServerSendData(0,buff,strlen((char*)buff));
    
    char *path="0:/"; //目录位置
    DIR dir;
    FIL file; //文件指针
    FRESULT res; 
    FILINFO fno; //存放读取的文件信息
    char *abs_path=NULL;  
    char cmd[]="$update,"; //请求的命令头
    char out_buff[100];
    UINT cnt;
    
    strcpy(out_buff,cmd);
    
    /*1. 打开目录*/    
    res=f_opendir(&dir,path);
    if(res!=FR_OK)return res;
    
    /*2. 循环读取目录*/
     while(1)
     { 
        
        res=f_readdir(&dir,&fno);
        if(fno.fname[0] == 0 || res!=0)break;
        //printf("文件名称: %s,文件大小: %ld 字节\r\n",fno.fname,fno.fsize);

        /*过滤目录*/
        if(strstr(fno.fname,".ev"))
        { 
        
            //申请存放文件名称的长度
            abs_path=malloc(strlen(path)+strlen(fno.fname)+1);
            if(abs_path==NULL)break;
             
            strcpy(abs_path,path);
            strcat(abs_path,"/");
            strcat(abs_path,fno.fname);
            //读取文件数据
            f_open(&file,abs_path,FA_READ);
            f_read(&file,out_buff+strlen(cmd),100,&cnt);
            free(abs_path);      
            printf("abs_path=%s,读取:%d\r\n",abs_path,cnt);
            out_buff[cnt+strlen(cmd)]='\0';
            
            //发送给上位机
            ESP8266_ServerSendData(0,(u8*)out_buff,strlen((char*)out_buff));
            printf("发送:%s\r\n",out_buff);
            delay_ms(100);
        }
    }
    
    /*3. 关闭目录*/
    f_closedir(&dir);
    return 0;
}


//提取数据,存放到全局事件结构体里
//参数: i 索引值 buf_out 源数据内容
void ExtractData(int i,char *buf_out)
{ 
        
	//std_to_sec(u16 syear, u8 smon, u8 sday, u8 hour, u8 min)

	char buf_num[50];
    
    //格式化处理
	
        标签: 传感器归位300pa2pa传感器nt1传感器w25传感器ld11传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台