前言:本项目为基于m5stack的校园物联网竞赛项目,以工程应用为主,不涉及前沿技术,期与大家共享。
1.产品功能
本产品是基于语音交互的物联家居管理系统。通过存入、处理、确认、保存、上传、反馈、分析(标记为本产品已经实现部分)等七个步骤,先后完成人机交互下,“物品”信息的本地保存、云端上传和大数据处理并反馈给用户。
2.产品特点
- 将语音模块融入物联网,提高了人与数据的交互性,语音交流时间成本低,也使得产品的使用更加可行;
- 通过语音采集,可以有效地实现许多传统传感器无法实现的数据采集,将大大推动数据的联网;
- 实现M5STACK模块和新的外接设备(MCU)进行互联,有助于新模块的开发和应用;
- 实现人-模块-从机-主机的多系统通信系统
3.产品设计
3.1 硬件设计
本产品的核心部分由2个单片机MCU组成,分别为主机M5STACK,从机STM32F103ZET6板子,实现语音信息的沟通和识别,建立起人与物与数据的沟通桥梁。

图 1 本系统宏观设计下的硬件分布图
其中主机M5STACK负责和各类外设进行连接、沟通和数据采集。M5STACK和NB-IoT模块实现USART连接,实现与云平台之间的数据共享。M5STACK和温度、湿度、色彩、GPS等各类传感器实现串行线路连接,根据乐鑫公司提供的模块化设计,可以快速获取各类必要的环境参数。M5STACK和STM32F103ZET6 V3开板实现UART串行连接,根据STM32F103ZET6 V3提供的数据信号,实现语音信号控制下数据的存储、删除和显示功能。
STM32F103ZET6 V3开发板是用于沟通主机和语音识别模块LD3320进行沟通的模块。因为LD3320模块是针对STM32板进行设计的模块,在设计之初可以有较为完整的库函数,而乐鑫采用的ESP32芯片没有对应的库。同时,其与外界沟通需要基于SPI的4端口通信,而M5STACK暂时没有提供合适的端口。因此考虑到短时间内无法实现LD3320直接向M5STACK的移植,我们直接采取使用STM32F103ZET6 V3开发板作为从机,一方面连接主机M5STACK实现数据传递,一方面连接LD3320实现数据采集。

图 2 从机和LD3320之间的通信
STM32F103ZET6 V3开发板是较为简装的开发板,因此其采购成本较低(大约42元左右)。之后时间充分,可以考虑将LD3320代码移植,最终实现和其他传感器一样的集成化,将推动物联网模块设计的语音化进程。

图3 NB-IoT模块实现云端互联
NB-IoT模块是用于主机和云端进行数据互联,其基本操作类似于主机和STM32之间的通讯,区别在于STM32连接有线的语音接收器,NB-IoT连接无线的云端平台ONENET。但是目前只完成云端平台搭建,因为缺乏对应的说明材料,经过更换nano-Sim、参阅文档中心等多次尝试和测试后,最终未能实现在ULFLOW上的联网工作。
同时,因为无法实现数据的直接上传,我们采取了建立一个简易的本地数据列表,对收到的数据进行收集和处理,对未来物联网中本地数据库的建立有帮助作用。
3.2 指令设计
指令的设计,是为了实现不同设备之间能实现数据的交互。

图 5 不同设备之间的数据交互
在人和LD3320的交互中,LD3320需要将人的指令和内部存储的汉语拼音指令进行对应解读。目前,LD3320至多可以存放大约50条指令。
在LD3320和从机STM32F103ZET6开发板的交互中,STM32F103ZET6 V3开发板设置了中断函数,当有语音传入时启动中断。根据从LD3320中收到的汉语拼音的索引,转化成对应的编码如01H,发送给主机。
在从机STM32和主机M5STACK的交互中,主机根据STM32传输线RX中低电平(起始位)后的8bits数据,对应了之前的编码,将其转化为实际含义的数据,如01H为“小凌”唤醒程序,并进行存储、处理、显示等操作。
下面将具体介绍各个指令段的含义:
命令指令(00 000 000~00 000 111)
命令指令是最重要的一段指令,它负责控制整个程序的运转。
01H 小凌用于唤醒程序;
02H 确认用于与用户实现三次握手,保证数据输入有效;
03~06H,分别为数据的增删查显四个必要的数据库操作;
07H 结束用于结束程序。

人名指令(00 001 000~00 001 111)
人名指令用于指示家中的成员,以解决如衣物等私人物品的有效归属问题。其中:
08H用于指示家庭共有财产,如食物、药物等;
0AH~0FH用于指示家庭成员,此处用代称简写。

物品指令(00 010 000~00 100 111)
用于记忆家中常见的物品,如衣物、药物、食物等。受目前硬件条件限制,暂时只列出上述三种各8样典型物品。实际生活中还可以包括书籍、杂物、被褥、人员进入等记录。



数量指令(00 101 000~00 110 111)
用于记录物品的数量,如深蓝牛仔裤(12H) 一(30H) 条(28H)。
概念较为简单,不在赘述。这里数量只列举了一至十,但实际需要实现,如131能完成 “一”“百”“三”“十”“一”这样较为困难的记录和运算,否则就得将“1 ~ ∞ 数字”数据全部存储在本地,显然是不可行的。但是因为开发时间较短,这部分暂未能完善。


3.3 逻辑设计
整体逻辑设计
在M5STACK处理收到数据的整体逻辑如下:
收到01H 呼唤“小凌”,开启程序,屏幕显示英文“小凌在!请说指令:Little Ling is here! Please say instructions”;
接下来根据收到03~06H,分别进入存放、删除、寻找、显示四个单线循环,直至结束或者收到07H。
之后可以继续呼唤“小凌”,重新进入循环。

图 6 主机整体逻辑设计流程图
①存放,表示加入一条数据,包括存放→谁的→什么物件→数量→单位五个主要对话逻辑部分,其中每个部分都会采取对应的三次握手逻辑,以确保数据添加的可靠性,后面将会具体阐述上述两个逻辑。
存放对应的现实场景为:将洗好的衣物放入柜中;将买来的蔬菜放入冰箱;将得到的书籍放入书架等等。

图 7 存放对应的现实场景
②删除,表示删除一条数据,包括删除→谁的→什么物件→数量→计算库存(不足将false退出)→遍历库存→按照时间顺序从库存中删除对应物件等步骤。
删除对应的现实场景为:将衣物从衣柜中取出;将买来的大米取出1斤烧饭;将过期的药物丢弃等等。

图 8 删除对应的现实场景
③寻找,表示查找某一条数据,包括查找→谁的→什么物件→遍历库存→显示是否有无等步骤。
寻找对应的显示场景为:寻找凌泰炀的黑色外套放在哪个衣柜;寻找家庭的扳手放在哪个柜子等等。
④显示,表示显示某一些系列数据,包括显示单个步骤(可以根据数据索引进行显示),但是因为时间缘故,暂时只能显示全部数据条目。
显示对应的显示场景为:显示家中食物的库存;显示家中存放的书目;显示家中缺少的药物
三次握手逻辑设计
三次握手源于TCP协议中,为了确保双方通信建立的正确采取的三次握手方法,通过一方向另一方发送已知信息的确认,以确保信息的准确性。

图 9 三次握手逻辑设计模拟图
在我们的设计中,也采用了三次握手的方式,因为目前采用的语音识别芯片并非智能芯片,其原理是通过汉语拼音识别,因此有可能会出现识别错误的情况,或者是识别周边噪声中的错误消息。
尽管三次握手会使得单个条目的存取时间翻倍,目前是25秒左右一条,但是三次握手环节可以大大的提高存入数据的准确度,避免数据出错后重新输入的问题。
经过多次测试实践后得出,三次握手环节是必要且有效的。
主要对话逻辑
下面将以图片的方式,展现一条完整的存放流程,展现对话逻辑(因为M5没有中文系统,因此实际屏幕显示的是英文):


图 10 主要对话逻辑设计模拟图
共12步,考虑系统延迟,一般需要25秒左右。以上为逻辑设计部分全部内容,接下来将进行具体的实际测试。
4.后续改进
因为开发者的时间确实十分有限,因此还存在许多前期希望完成但最终没能完成的部分,希望后续有待完善,这里将简单的补充一些内容和设想
联网问题:目前本质上,实现了物联,但是没能实现联网,这是本产品最大的遗憾。之后可以进一步通过wifi模块或者NB-IoT模块,实现云端数据共享。
语音对话:目前本质上是单方面对话(机端采用屏幕显示),但实际上我们设计的LD3320可以根据控制,播放写入的MP3语音,希望将来能进一步完成。
时间问题:因为M5中没有当前时间读取的模块,因此没法记录物品存储时间,希望将来能加上,这样产品意义将会更明显。
界面优化:受内存空间限制,界面优化条件有限,目前如下图所示。

图 13 产品界面设计
语音扩充:受LD3320板只能存储大约50条语音指令的限制,因此我们的指令只有48条,可采用更优质的芯片。
多传感器:目前受设计时间和本地存储空间限制,每条数据只存储了与人沟通获得的数据,但是也可以存储其他传感器得到的及其推算出的,如存食物时记录温度和湿度、保质期等等。
5.实践代码
m5代码
UIFLOW 模块
from m5stack import *
from m5ui import *
from uiflow import *
setScreenColor(0xffffff)
rectangle1 = M5Rect(12, 76, 300, 150, 0xFFFFFF, 0x04f700)
label2 = M5TextBox(32, 79, "...", lcd.FONT_Default,0x1fe13f, rotate=0)
rectangle0 = M5Rect(12, 7, 300, 60, 0xFFFFFF, 0xd71313)
label1 = M5TextBox(25, 3, "...", lcd.FONT_DejaVu24,0xff0000, rotate=0)
numbers = None
kucun = None
in_order = None
people = None
temp_str = None
i = None
tempread = None
things = None
library_things = None
uart = None
tempvalue = None
orders = None
temp_list = None
delete_size = None
k = None
delete_whose = None
find_whose = None
delete_what = None
in_whose = None
in_what = None
in_unit = None
in_number = None
find_what = None
def orderprocess():
global numbers, kucun, in_order, people, temp_str, i, tempread, things, library_things, uart, tempvalue, orders, temp_list, delete_size, k, delete_whose, find_whose, delete_what, in_whose, in_what, in_unit, in_number, find_what
wait(1)
while True:
tempread = uart.read(1)
tempvalue = orders[str(tempread)]
label1.setText(str(str(tempvalue)))
if 'None' == str(tempread):
continue
else:
if "b'\\x03'" == str(tempread):
storeConfirm()
break
else:
if "b'\\x04'" == str(tempread):
deleteConfirm()
break
else:
if "b'\\x05'" == str(tempread):
findConfirm()
break
else:
if "b'\\x06'" == str(tempread):
showConfirm()
break
else:
if "b'\\x07'" == str(tempread):
endConfirm()
break
def showConfirm():
global numbers, kucun, in_order, people, temp_str, i, tempread, things, library_things, uart, tempvalue, orders, temp_list, delete_size, k, delete_whose, find_whose, delete_what, in_whose, in_what, in_unit, in_number, find_what
label1.setText('Really Show?')
in_order = orders[str(tempread)]
while True:
tempread = uart.read(1)
if "b'\\x02'" == str(tempread):
show()
break
else:
if "b'\\x03'" == str(tempread):
storeConfirm()
break
else:
if "b'\\x04'" == str(tempread):
deleteConfirm()
break
else:
if "b'\\x05'" == str(tempread):
findConfirm()
break
else:
if "b'\\x06'" == str(tempread):
showConfirm()
break
else:
if "b'\\x07'" == str(tempread):
endConfirm()
break
else:
continue
def findConfirm():
global numbers, kucun, in_order, people, temp_str, i, tempread, things, library_things, uart, tempvalue, orders, temp_list, delete_size, k, delete_whose, find_whose, delete_what, in_whose, in_what, in_unit, in_number, find_what
label1.setText('Really Find?')
in_order = orders[str(tempread)]
while True:
tempread = uart.read(1)
if "b'\\x02'" == str(tempread):
findWhose()
break
else:
if "b'\\x03'" == str(tempread):
storeConfirm()
break
else:
if "b'\\x04'" == str(tempread):
deleteConfirm()
break
else:
if "b'\\x05'" == str(tempread):
findConfirm()
break
else:
if "b'\\x06'" == str(tempread):
showConfirm()
break
else:
if "b'\\x07'" == str(tempread):
endConfirm()
break
else:
continue
def deleteConfirm():
global numbers, kucun, in_order, people, temp_str, i, tempread, things, library_things, uart, tempvalue, orders, temp_list, delete_size, k, delete_whose, find_whose, delete_what, in_whose, in_what, in_unit, in_number, find_what
label1.setText('Really Delete?')
in_order = orders[str(tempread)]
while True:
tempread = uart.read(1)
if "b'\\x02'" == str(tempread):
deleteWhose()
break
else:
if "b'\\x03'" == str(tempread):
storeConfirm()
break
else:
if "b'\\x04'" == str(tempread):
deleteConfirm()
break
else:
if "b'\\x05'" == str(tempread):
findConfirm()
break
else:
if "b'\\x06'" == str(tempread):
showConfirm()
break
else:
if "b'\\x07'" == str(tempread):
endConfirm()
break
else:
continue
def endConfirm():
global numbers, kucun, in_order, people, temp_str, i, tempread, things, library_things, uart, tempvalue, orders, temp_list, delete_size, k, delete_whose, find_whose, delete_what, in_whose, in_what, in_unit, in_number, find_what
label1.setText('Really End?')
in_order = orders[str(tempread)]
while True:
tempread = uart.read(1)
if "b'\\x02'" == str(tempread):
end()
break
else:
if "b'\\x03'" == str(tempread):
storeConfirm()
break
else:
if "b'\\x04'" == str(tempread):
deleteConfirm()
break
else:
if "b'\\x05'" == str(tempread):
findConfirm()
break
else:
if "b'\\x06'" == str(tempread):
showConfirm()
break
else:
if "b'\\x07'" == str(tempread):
endConfirm()
break
else:
continue
def inital():
global numbers, kucun, in_order, people, temp_str, i, tempread, things, library_things, uart, tempvalue, orders, temp_list, delete_size, k, delete_whose, find_whose, delete_what, in_whose, in_what, in_unit, in_number, find_what
numbers = {
"b'\\x30'":'1',"b'\\x31'":'2',"b'\\x32'":'3',"b'\\x33'":'4',"b'\\x34'":'5',"b'\\x35'":'6',"b'\\x36'":'7',"b'\\x37'":'8',"b'\\x38'":'9',"b'\\x39'":'10',"b'0'":'1',"b'1'":'2',"b'2'":'3',"b'3'":'4',"b'4'":'5',"b'5'":'6',"b'6'":'7',"b'7'":'8',"b'8'":'9',"b'9'":'10'}
people = {
"b'\\x08'":'Family',"b'\\x09'":'Lingtaiyang',"b'\\x0A'":'Little brother',"b'\\x0B'":'Elder brother',"b'\\x0C'":'Mother',"b'\\x0D'":'Father',"b'\\x0E'":'Grandpa',"b'\\x0F'":'Grandma'}
things = {
"b'\\x10'":'Black coat',"b'\\x11'":'White coat',"b'\\x12'":'Dark blue jeans',"b'\\x13'":'Light blue jeans',"b'\\x14'":'Grey trench coat',"b'\\x15'":'White shirt',"b'\\x16'":'Gloves',"b'\\x17'":'Blindfold',"b'\\x18'":'aspirin',"b'\\x19'":'Berberine',"b'\\x1A'":'Yunnan Baiyao',"b'\\x1B'":'Iodine',"b'\\x1C'":'band Aid',"b'\\x1D'":'Morpholine',"b'\\x1E'":'Cold particles',"b'\\x1F'":'Fengyoujing',"b'\\x20'":'rice',"b'\\x21'":'potato',"b'\\x22'":'vegetables',"b'\\x23'":'cabbage',"b'\\x24'":'tomato',"b'\\x25'":'pork',"b'\\x26'":'beef',"b'\\x27'":'Lamb',"b'\\x28'":'pair',"b'\\x29'":'item',"b'\\x2A'":'box',"b'\\x2B'":'bottle',"b'\\x2C'":'ge',"b'\\x2D'":'jin',"b'\\x2E'":'kilogram',"b'\\x2F'":'',"b'!'":'potato','b\'"\'':'vegetables',"b'#'":'cabbage',"b'$'":'tomato',"b'%'":'pork',"b'&'":'beef',"b'''":'Lamb',"b'('":'pair',"b')'":'item',"b'*'":'box',"b'+'":'bottle',"b','":'ge',"b'-'":'jin',"b'.'":'kilogram',"b'/'":''}
orders = {
'None':'No Instruction',"b'\\x00'":'NULL',"b'\\x01'":'Little Ling',"b'\\x02'":'Confirm',"b'\\x03'":'Store',"b'\\x04'":'Delete',"b'\\x05'":'Find',"b'\\x06'":'Show',"b'\\x07'":'End'}
library_things = []
def storeConfirm():
global numbers, kucun, in_order, people, temp_str, i, tempread, things, library_things, uart, tempvalue, orders, temp_list, delete_size, k, delete_whose, find_whose, delete_what, in_whose, in_what, in_unit, in_number, find_what
label1.setText('Really Store?')
in_order = orders[str(tempread)]
while True:
tempread = uart.read(1)
if "b'\\x02'" == str(tempread):
storeWhose()
break
else:
if "b'\\x03'" == str(tempread):
storeConfirm()
break
else:
if "b'\\x04'" == str(tempread):
deleteConfirm()
break
else:
if "b'\\x05'" == str(tempread):
findConfirm()
break
else:
if "b'\\x06'" == str(tempread):
showConfirm()
break
else:
if "b'\\x07'" == str(tempread):
endConfirm()
break
else:
continue
def storeWhose():
global numbers, kucun, in_order, people, temp_str, i, tempread, things, library_things, uart, tempvalue, orders, temp_list, delete_size, k, delete_whose, find_whose, delete_what, in_whose, in_what, in_unit, in_number, find_what
label1.setText('Store: Whose?')
while True:
tempread = uart.read(1)
if str(tempread) in people.keys():

2381

被折叠的 条评论
为什么被折叠?



