目录
[youbanshan]二、唤醒他吧
三、个性化定制
我是谁,谁又是我
Hi, 大家好,我是刘波,人在江湖行走的ID是:IAMLIUBO!受到电子芯吧客平台用户活跃度的感召,为了防止世界被破坏,为了保护世界的和平,于是我决定也要来电子芯吧客平台混个脸熟了(我不会承认我是为了稿费才来的!绝对不会!滑稽.gif)。
一、如何打造属于你的私人语音助手之环境搭建
发布时间: 2019-12-11
第二篇文章的演示视频如下:
【带资料】打造树莓派私人语音助手,年轻人的第一个贾维斯!_哔哩哔哩_bilibili
前言
我们在上篇文章中已经将基本环境搭建好了,本片文章让我们开始下一步吧!
准备工作
硬件:
软件安装
在上篇文章的最后,我们没有全部完成,所以本节课还需要做一些收尾工作,我们还需要编译我们的语音唤醒方案的一个动态库,还有安装一下麦克风阵列的驱动,因为这两部分相对来说还是比较麻烦的,所以留到了本篇文章来写。
编译snowboy
我们先安装swig,本节操作命令有点多,请仔细执行每一条命令:
#安装依赖库sudo apt-get -y updatesudo apt-get install -y libpcre3 libpcre3-dev#下载并编译安装swig,此步是必须的,不可省略wget http://hahack-1253537070.file.myqcloud.com/misc/swig-3.0.10.tar.gztar xvf swig-3.0.10.tar.gzcd swig-3.0.10./configure --prefix=/usr --without-clisp --without-maximum-compile-warningsmakesudo make installsudo install -v -m755 -d /usr/share/doc/swig-3.0.10sudo cp -v -R Doc/* /usr/share/doc/swig-3.0.10#安装依赖库sudo apt-get install -y libatlas-base-dev
上面有条命令由markdown转码html貌似不成功,请参考下面的:
sudo cp -v -R Doc/@@* /usr/share/doc/swig-3.0.10
如果你觉得一条条的执行比较麻烦,可以新建一个swig_install.sh文件,然后复制上面所有的命令到文件:
sudo nano swig_install.sh
./swig_install
然后我们开始编译snowboy的动态库文件,同样的我们执行下面的命令:
wget http://hahack-1253537070.file.myqcloud.com/misc/snowboy.tar.bz2tar -xvjf snowboy.tar.bz2cd snowboy/swig/Python3make
出现跟下图一样的字样,标识编译成功。
我们还需要将snowboydetect.so文件复制到wukong-robot目录下的snowboy文件夹。
cp _snowboydetect.so ~/wukong-robot/snowboy/
OK,到这里snowboy动态库我们就编译完成了,不过先不要着急,我们还需要安装麦克风阵列驱动,Let’s go!
安装麦克风阵列驱动
这里我们使用的是两个麦克风的麦克风阵列,虽然不如多麦克风阵列的收音效果好,但是相对来说比较便宜,还是比较不错的,废话少说!
git clone https://github.com/respeaker/seeed-voicecard.git #下载声卡驱动cd seeed-voicecard sudo ./install.sh #安装声卡驱动
我们先从github拉取源码,拉取完成后直接执行安装脚本就可以了,相对是比较简单的,安装完成后我们需要重启,然后断电顺便插上我们的麦克风阵列和小音箱。
出现以上界面,我们就可以关机了,然后断掉电源,安装麦克风阵列与音箱,音箱3.5mm插头插座我们先插麦克风阵列上就可以,测试完麦克风阵列后我们再插到树莓派上。
sudo poweroff #关机
安装好后,我们重新开机测试麦克风阵列是否工作,首先我们查看声卡列表是否有我们的麦克风阵列:
aplay -l
然后你可以录音测试一下:
arecord -f cd -Dhw:1 | aplay -Dhw:1 #声音会越来越大,建议保护耳朵,ctrl+c退出
如果能听到自己声音,就没问题了,然后你可以将音箱再插回树莓派上!
运行wukong
接下来让我们正式开始运行起来吧,这里默认唤醒词是孙悟空,不过效果不太好,因为是作者自己训练的,当然你可以可以更换成你想要的唤醒词,这里我们先运行起来:
python3 wukong.py
第一次运行会询问我们是否创建config.yml配置文件,我们输入y就可以了,后面我们很多设置都是从这里面更改,目录是/home/pi/.wukong/config.yml,第一次运行可能会唤醒不了,这里不要着急,我们还需要修改一下配置,我们需要先按ctrl+4停止运行,然后输入以下命令:
sudo nano ~/.wukong/config.yml
找到以下内容:
# snowboy 离线唤醒# https://snowboy.kitt.ai/dashboard# 建议到 https://snowboy.kitt.ai/hotword/32768# 使用相同环境录入你的语音,以提升唤醒成功率和准确率hotword: 'wukong_pi.pmdl' # 唤醒词模型,如要自定义请放到 $HOME/.wukong 目录中sensitivity: 0.4 # 灵敏度silent_threshold: 15 # 判断为静音的阈值。环境比较吵杂的地方可以适当调大recording_timeout: 5 # 录制的语音最大长度(秒)snowboy_token: your_token # 你的token,用于 train 命令训练语音
将其中hotword的wukong.pmdl修改为wukong_pi.pmdl,然后保存退出就可以运行并唤醒了,唤醒效果不会像我们买的智能音箱一样,提前给大家打个预防针,主要原因还是因为训练样本不够,会面会教大家如何训练和修改自己的唤醒词。
如果修改配置文件及运行,可以参考下面动态图:
运行效果演示视频可以到DIY视频版块查看(管理员还在审核中…)。
接下来我们安装一下第三方技能插件库,第三方技能插件库是除作者之外的开发者贡献的技能,我们也是可以直接使用的,后面我们也会将如何开发属于自己的技能,这里我们先安装一下别人写好的技能。
cd ~/.wukonggit clone http://github.com/wzpan/wukong-contrib.git contribpip3 install -r contrib/requirements.txt
请一定要切换到~/.wukong这个目录下安装,因为技能默认都会从这里面加载,那么都有哪些用户技能呢,大家可以从这个链接查看:
用户贡献技能
后台管理界面
我们还可以通过web后台跟语音助手互动,我们可以通过电脑浏览器输入树莓派的IP地址加端口号进行访问:
然后我们可以像打字聊天一样进行交互:
尽情去体验吧~
OK,本篇文章就到这里,下一篇文章教大家如何训练自己的唤醒词以及个性化修改等等,后面还会教大家如何开发自己的技能!
三、如何打造属于你的私人语音助手之个性化定制
前言
我们在上一篇文中已经成功运行起来了我们的私人语音助手——孙悟空,等等?你是认真的?叫孙悟空?是的,默认唤醒词就是我们童年最美好的记忆,但是我们老孙悟空,孙悟空的喊,有没有觉得自己有点像唐僧呢?哈哈,那么本节课就带大家做一些个性化定制,让它真正属于你的“私人”助手。
准备
硬件:
安装好wukong的树莓派
貌似没其它了
软件:
无
开始
换掉唤醒词,拒绝做唐僧!
第一步我们先从换掉唤醒词开始,毕竟作为一个合格的程序员,拒绝做唐僧是我们最后的倔强(暗示不脱发!拒绝联想!)。我们在前面的文章中讲过,这里使用的唤醒方案是snowboy,所以我们需要到snowboy官方网站进行唤醒词训练!
戳链接直达:snowboy
这里你需要登录,可以直接使用Github登录,简单方便又快捷!
如果你是第一次登录还未训练任何模型,下载前都需要参与训练,如何训练也很简单,每一步也都有指引,训练过的模型可以直接点击下载按钮下载即可,这里我以小白这个唤醒词给大家演示一下!
下载好后,我们上传到树莓派下面这个目录即可:
接下来我们修改一下配置文件的这一部分,将其中的hotword部分修改为我们刚下载的那个模型,如下:
# snowboy 离线唤醒# https://snowboy.kitt.ai/dashboard# 建议到 https://snowboy.kitt.ai/hotword/32768# 使用相同环境录入你的语音,以提升唤醒成功率和准确率hotword: 'xiaobai.pmdl' # 唤醒词模型,如要自定义请放到 $HOME/.wukong 目录中sensitivity: 0.4 # 灵敏度silent_threshold: 15 # 判断为静音的阈值。环境比较吵杂的地方可以适当调大recording_timeout: 5 # 录制的语音最大长度(秒)snowboy_token: your_token # 你的token,用于 train 命令训练语音
配置文件目录HOME/.wukong/config.yml。
如果唤醒效果不太理想,可以将灵敏度调大一点,比如0.5,不要太大!则误唤醒率会非常高,修改完成后可以运行测试一下:
主人信息修改
细心的同学可能发现了,每次开机都是喊你师父,然后上面我们询问天气的时候,可能位置信息也不是你所处的位置,不过这些都是可以修改的,可能很多同学已经发现了,也是在我们的那个配置文件中:
robot_name_cn: '小白first_name: '大哥last_name: ''timezone: HKTlocation: '济南
大家可以根据自己的喜好进行修改,修改完成后再次运行就会使用新的配置了。
男声不好听?
默认发音人为男生,可能很多”宅男”,不太喜欢听,想要改成女声,那么能改吗?当然可以更改,同样的配置文件中如下部分:
# 百度语音服务# http://yuyin.baidu.com/baidu_yuyin: appid: '9670645' # 建议使用自己的百度语音账户 APPID api_key: 'qg4haN8b2bGvFtCbBGqhrmZy' secret_key: '585d4eccb50d306c401d7df138bb02e7' dev_pid: 1936 # 1936: 普通话远场,1536:普通话(支持简单的英文识别),80001:ASR极速版(请使用自己的百度语音账户再使用) per: 1 # 发音人选择 0:女生;1:男生;3:度逍遥;4:度丫丫 lan: 'zh'
我们只需要将per: 1 中的数字改成0就可以了,当然还提供了其它两种发音人,大家可以自行尝试。
当然上面的修改是基于你选择的是百度文字转语音服务,如果选用的其它的,需要针对性再修改,我们可以从配置文件中发现,一共提供了下面几家的语音服务,默认的语音识别和文字转语音都是使用的百度的API。
# 语音合成服务配置# 可选值:# han-tts - HanTTS# baidu-tts - 百度语音合成(推荐)# xunfei-tts - 讯飞语音合成# ali-tts - 阿里语音合成(推荐)# tencent-tts - 腾讯云语音合成(推荐)tts_engine: baidu-tts
大家如果想换其它家的,只需要将tts_engine后面的值修改成对应的即可。
邮件提醒
没错,还可以使用wukong查询你的邮箱是否有邮件:
# 邮箱# 如果使用网易邮箱,还需设置允许第三方客户端收发邮件email: enable: true address: '你的邮箱地址' password: '你的邮箱密码' # 如果是网易邮箱,须填写应用授权密码而不是登录密码! smtp_server: 'smtp.163.com' smtp_port: '25' # 这里填写非SSL协议端口号 imap_server: 'imap.163.com' imap_port: '143' # 这里填写非SSL协议端口号 read_email_title: true # 当有邮件时,是否朗读邮件标题
QQ邮箱也建议你使用授权码登录!然后QQ邮箱的收件服务器是:imap.qq.com,发件服务器是:smtp.qq.com。
管理后台密码修改
大家还记得上篇文章中的管理后台吗?当时是默认密码,这里教大家修改一下密码:
# 后台管理端server: enable: true host: '0.0.0.0' # ip 地址 port: '5000' # 端口号 username: 'admin' # 用户名 # cookie 的 secret ,用于对 cookie 的内容进行加密及防止篡改 # 建议使用 os.urandom(24) 生成一串随机字符串 # 强烈建议修改!!! cookie_secret: '456134b90bf73510cc5eb2ab8b7948e7400f8c6781d3fb86' # 密码的 md5,可以用 python3 wukong.py md5 "密码" 获得 # 初始密码为 wukong@2019 # 强烈建议修改!!! # 我这里修改成了 XXXXXXXX validate: '1c6e488449e3741a999388f7bd7d07ae'
用户名是明文,大家可以自由修改,建议大家不要修改成汉字哦!
如何修改可以看下面的动态图:
贴一下python代码给大家:
import osimport binasciiprint(binascii.hexlify(os.urandom(24)))
登录测试,可以看到我使用的是admin这个用户名登录的,然后密码是使用的 XXXXXXXX,能够登录成功就说明我们已经修改成功了!
OK,本篇文章就到这里,下篇文章教大家如何开发技能,以及技能是如何开发的,纳尼?你是认真的?简而言之就是开发一个你自己的技能!
四、如何打造属于你的私人语音助手之植物识别
前言
距离上一篇文章,已经过去一段时间了,不知道大家有没有成功跑起来呢?最近比较忙,也没能持续更新,不过今晚忙里偷闲,给家更新一篇!本篇文章教大家如何开发一个技能——植物识别!没错就是通过摄像头识别植物!不过这里我们是调用的百度接口,没错百度AI接口,使用了很长时间了,感觉准确率还是可以的,貌似百度在国内的几家大厂中,图像识别做的还是非常不错的。
准备
硬件
摄像头(个人推荐使用CSI接口的)
软件
无
百度AI应用创建
相信注册账号,大家肯定都比较熟练了,这里也就不给大家一步步的演示了,大家打开下面的官网链接,相信一看就知道去哪里注册了:
百度AI开发平台
这里我给大家演示一下如何注册一个图像识别的应用,其实也非常简单,一共四大步,我们就可以完成一个应用的创建:
最后回到应用列表,可以看到有AppID、API Key和Secret Key,一会我们需要用到这几个参数,大家知道在这里找就可以:
到这里我们的百度AI图像应用就创建好了,其实就是为了得到上面那几个密钥。
依赖库安装
所谓的依赖库其实就是百度给我们写好的SDK,这里我们选择python SDK来进行开发,因为我们的语音助手的代码也是用python来写的,安装方法也很简单,大家只需要执行下面的一条命令即可:
sudo pip install baidu-aip
相信大家多多少少对python应该都有一部分了解,我们安装好依赖库后,基本就都准备好了,baidu-aip怎么用,可以去看一下百度的文档:文档链接,当然你还需要将你的硬件连接起来,就是需要把你的摄像头使用FPC软排线跟树莓派连接起来,相信聪明伶俐的你肯定会的!
如何开发一个技能?
其实我们自己去开发技能还是非常简单的,因为很多麻烦的地方作者都已经帮我们处理好了,当然大家也可以看一下文档:技能开发,不过这里还是简单给大家介绍一下。
准备工作
创建一个文件夹,用来存放我们的技能代码:
sudo mkdir -p ~/.wukong/custom
没错就是在在.wukong这个目录下创建一个custom文件夹,名称是固定的,这里的路径相信你的跟我的应该也一样,这是官方文档中固定的,大家按照这个格式去写好了。
不知道大家还记得那个配置文件吗?就是在个性化定制的那篇文章中我们修改了很多参数的那个配置文件,这里我们增加一些内容到里面:
nano ~/.wukong/config.yml
在文件的最后增加以下内容:
#植物识别#使用百度AI接口#作者: IAMLIUBOplantDetect: enable: true appid: 'XXXXXXXXXXX' api_key: 'XXXXXXXXXXX' secret_key: 'XXXXXXXXXXX' type: 1 # 摄像头类型 0:usb_camera,1:树莓派 5MP 摄像头,2$ dest_path: "/home/pi/Pictures/" # 保存目录 quality: 5 # 成像质量(0~100),不支持 imagesnap vertical_flip: true # 竖直翻转,不支持 imagesnap horizontal_flip: true # 水平翻转,不支持 imagesnap count_down: 3 # 倒计时(秒),仅当开启倒计时时有效
不过这里你需要注意了,请将上面的 XXXXXXXXXXX 替换成我们在上面创建百度AI应用得到的appid、api_key和secret_key,不然后面运行会出错的!一定要记得修改!
到这里准备工作基本就完成了,如果你是第一次使用摄像头的话,记得先运行:
sudo raspi-config
然后在Interfacing Option里面找到Camera并使能,不然后面还是会报错的!
颤抖吧!少年!
哈哈,开玩笑,准备工作都准备好了,那我们就开始运行代码吧,不过我们需要先写好程序:
nano ~/.wukong/custom/CameraPlantDetect.py
然后复制下面的代码并保存:
# -*- coding: utf-8-*-# Camera plant detect# author: IAMLIUBO# github: https://github.com/imliubo# website: https://blogs.oopswow.comimport osimport subprocessimport timefrom robot import config, constants, loggingfrom robot.sdk.AbstractPlugin import AbstractPluginfrom aip import AipImageClassifylogger = logging.getLogger(__name__)options = {}options["baike_num"] = 1def get_file_content(filePath): with open(filePath, 'rb') as fp: return fp.read()class Plugin(AbstractPlugin): SLUG = "plantDetect" def handle(self, text, parsed): APP_ID = config.get('/plantDetect/appid') API_KEY = config.get('/plantDetect/api_key') SECRET_KEY = config.get('/plantDetect/secret_key') client = AipImageClassify(APP_ID, API_KEY, SECRET_KEY) quality = config.get('/plantDetect/quality', 100) count_down = config.get('/plantDetect/count_down', 3) dest_path = config.get('/plantDetect/dest_path', os.path.expanduser('~/Pictures')) device = config.get('/plantDetect/device', '/dev/video0') vertical_flip = config.get('/plantDetect/verical_flip', False) horizontal_flip = config.get('/plantDetect/horizontal_flip', False) camera_type = config.get('/plantDetect/type', 1) if config.has('/plantDetect/usb_camera') and config.get('/plantDetect/usb_camera'): camera_type = 0 try: if not os.path.exists(dest_path): os.makedirs(dest_path) except Exception: self.say(u"抱歉,照片目录创建失败", cache=True) return dest_file = os.path.join(dest_path, "%s.jpg" % time.time()) if camera_type == 0: # usb camera logger.info('usb camera') command = ['fswebcam', '--no-banner', '-r', '1024x765', '-q', '-d', device] if vertical_flip: command.extend(['-s', 'v']) if horizontal_flip: command.extend(['-s', 'h']) command.append(dest_file) elif camera_type == 1: # Raspberry Pi 5MP logger.info('Raspberry Pi 5MP camera') command = ['raspistill', '-o', dest_file, '-q', str(quality)] if count_down > 0 : command.extend(['-t', str(count_down*1000)]) if vertical_flip: command.append('-vf') if horizontal_flip: command.append('-hf') else: # notebook camera logger.info('notebook camera') command = ['imagesnap', dest_file] if count_down > 0 : command.extend(['-w', str(count_down)]) if count_down > 0: self.say(u"收到,%d秒后启动拍照" % (count_down), cache=True) if camera_type == 0: time.sleep(count_down) try: subprocess.run(command, shell=False, check=True) self.play(constants.getData('camera.wav')) image = get_file_content(dest_file) result = client.plantDetect(image, options) plant_name = result['result'][0]['name'] try: baike_info = result['result'][0]['baike_info']['description'] buffer = u"主人,识别到的植物是%s,百度百科描述是%s"%(plant_name, baike_info) self.say(buffer, cache=False) except KeyError: buffer = u"主人,识别到的植物是%s,暂无百度百科描述"%(plant_name) self.say(buffer, cache=False) except subprocess.CalledProcessError as e: logger.error(e) self.say(u"拍照失败,请检查相机是否连接正确", cache=True) def isValid(self, text, parsed): return any(word in text for word in [u"植物识别",u"植物"])
大家先不用了解代码是怎么写的,当然我这里写的可能也不够好,因为我平常主要做嵌入式开发,python只是业余爱好,保存好后我们就可以直接运行wukong了。
为了方便大家测试,这里给大家贴张图片,大家可以将摄像头对准下面的图片,然后唤醒悟空,并对它说 “植物识别” 或者 “植物” 都可以:
不出意外的话,应该可以准确识别出牡丹花,并且播放牡丹花的百度百科!
OK,本篇文章就到这里,大家可以先去测试,有问题可以在评论里提出,后面我会录制一个测试视频给大家!
五、如何打造属于你的私人语音助手之MQTT开发
前言
大家还记得我们前面的树莓派私人语音助手的几篇文章吗?之前年底比较忙,没能连续更新,然后又在家度过了一个漫长的假期,最近准备抽时间重新开始更新一下,本篇文章是我们将我们的私人语音助手打造成真正小助手的第一步,我们将会在树莓派上安装一个MQTT server,用来承载我们在局域网内设备间的交互,后面我们就可以通过语音利用MQTT来控制我们的其它设备了,我想你应该知道我在讲什么~
准备
在本篇文章中,你需要准备以下内容:
硬件
树莓派(已经搭建好wukong的pi)
ESP8266(建议NodeMCU)
软件MQTT.fx(测试用,建议先下载安装,并学一下怎么用)
这就是学习本篇文章,你需要准备的所有的东西了,当然一个可以正常上网的条件也是必不可少的。
安装MQTT server
可能有同学对MQTT不是很熟悉,建议大家可以先去google一下,或者可以看一下我写的这两篇篇文章,以便对MQTT有大致的认识,由于内容比较多,这里就不全贴过来了,大家可以点击下面链接去查看:
物联网标配MQTT初相识
物联网标配MQTT服务端软件
Mosquitto简单了解
这是一款Eclipse 团队开发的超轻量级的软件,安装大小仅有几百KB,当然如此轻量级功能上是没有EMQX那么强大了,仅支持MQTT V3协议,不过做测试还是比较好用的,不用在自己的机器上起一个像EMQX那么大的服务,官网也没做太多的介绍,只有一个简短的介绍:
Eclipse Mosquitto is an open source (EPL/EDL licensed) message broker that implements the MQTT protocol versions 5.0, 3.1.1 and 3.1. Mosquitto is lightweight and is suitable for use on all devices from low power single board computers to full servers.
The MQTT protocol provides a lightweight method of carrying out messaging using a publish/subscribe model. This makes it suitable for Internet of Things messaging such as with low power sensors or mobile devices such as phones, embedded computers or microcontrollers.
The Mosquitto project also provides a C library for implementing MQTT clients, and the very popular mosquitto_pub and mosquitto_sub command line MQTT clients.
当然这不是唯一的选择,我们这里选择Mosquitto主要原因还是因为Mosquitto足够小巧,毕竟我们是在树莓派上跑着这个server的,所以像EMQTT这种比较专业的就有点吃力了。
安装Mosquitto
安装也相对简单,大家依次执行以下命令就可以了。
wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.keysudo apt-key add mosquitto-repo.gpg.keysudo wget http://repo.mosquitto.org/debian/mosquitto-buster.listsudo apt-get updatesudo apt-get install mosquitto
安装完成后,我们先来检查一下,是否正常运行,输入以下命令,并看到跟下面的图片一样,说明你已经成功安装并运行起来了:
service mosquitto status
安装mosquitto-clients
上面我们已经安装好了服务端,这里我们再顺便安装一下客户端,安装好后我们就可以测试一下能不能正常使用了,安装也很简单,直接输入下面命令就可以:
sudo apt-get install mosquitto-clients
测试
这里我们通过下面三种方式去测试,以帮助大家有更好的认识:
mosquitto-clients
MQTT.fx
ESP8266
第一种方式,我们是在树莓派上测试的,可以验证是否可以正常工作,然后第二种方式我们在Windows上通过MQTT软件进行测试,这里可以验证局域网通信是否正常,然后第三种方式是通过ESP8266去测试,可以验证我们后面就可以愉快的跟硬件交互了。
mosquitto-clients
这个测试相对简单,不过大家需要使用putty类似的软件再打开一个窗口,然后我们在窗口1和窗口2分别输入以下命令:
窗口1
mosquitto_sub -t "/icxbk/IAMLIUBO" -v
窗口2
mosquitto_pub -t "/icxbk/IAMLIUBO" -m "Hello,icxbk!"
如果你看到跟下图一样的效果,就说明没问题了!
这里给大家简单说一下这几个参数的含义:
-t 这表示主题,后面跟着的参数就是我们发布消息的主题,如果你不太明白什么是主题,可以看上面我贴出的两篇文章链接的第一篇。
-v 表示收到的消息内容是可见的,也就是通过控制台打印出来
-m 表示实际发送的消息内容,这里的消息主题要与我们通过mosquitto_sub订阅的主题保持一致
MQTT.fx
我们再在Windows上通过MQTT.fx软件测试一下,首先你需要配置一下MQTT broken,依次点击Extras->Edit Connection Profiles->右下角加号,然后配置一下IP,如下图:
然后我们使用Pulish发送一条消息,如下图,但是不要忘记先点击Connect哦~
如果你设置没问题的画,就可以在putty窗口1中看到我们发的消息了,大家如果有不会使用MQTT.fx软件的,建议大家可以先去学习一下。
ESP8266
使用ESP8266可能对部分没有接触过的同学显得稍微有点麻烦,这里我们使用Arduino来开发ESP8266,大家如果还没搭建好开发环境的话,可以参考我的下一篇文章,因为后面我们还会多次用到ESP8266,所以我会教大家如何用Arduino IDE来开发ESP8266,建议大家可以先去看一下文章,再回来这里继续。
下面直接贴上代码,代码也很简单,我们这里使用的是EspMQTTClient这个库,大家可以通过库管理器来进行安装。
#include "EspMQTTClient.h"void onConnectionEstablished();EspMQTTClient client( "XXXXXXXXXX", // Wifi 名称 "XXXXXXXXXX", // Wifi 密码 onConnectionEstablished, // MQTT connection established callback "XXX.XXX.XXX.XXX" // 树莓派IP);void setup(){ Serial.begin(115200);}void onConnectionEstablished(){ // Subscribe to "mytopic/test" and display received message to Serial client.subscribe("/icxbk/IAMLIUBO", [](const String & payload) { Serial.println(payload); }); // Publish a message to "mytopic/test" client.publish("/icxbk/IAMLIUBO", "This is a message form ESP8266!");}void loop(){ client.loop();}
大家需要将代码中这一部分的内容替换为你自己的:
EspMQTTClient client( "XXXXXXXXXX", // Wifi 名称 "XXXXXXXXXX", // Wifi 密码 onConnectionEstablished, // MQTT connection established callback "XXX.XXX.XXX.XXX" // 树莓派IP);
编辑好后,就可以编译下载了,下载成功后你会在putty窗口1中看到我们发的消息了,如下图:
后记
本篇文章是我们后面继续开发我们的私人语音助手的基础,请大家一定要仔细做,可能会有部分代码由markdown格式渲染成html会出现乱码,如果有不能正常执行的代码可以在评论区留言。
六、如何打造属于你的私人语音助手之控制LED灯(NodeMCU)
前言
我们在上篇文章中,已经在树莓派上搭建好了MQTT server,既然都搭建好了,岂有不用的道理?那么本篇文章就教大家语音控制LED灯!带你解锁更多技能!
准备
在本篇文章中,你需要准备以下内容:
硬件
树莓派(已经搭建好wukong的pi,初学者请从前面几篇文章开始)
ESP8266(建议NodeMCU,芯吧客商城有售)
LED灯(直插的就可以)
软件
Arduino IDE
这就是本篇文章需要准备的硬件跟软件,相信大多数Geek应该都有吧,那么废话说少,接下来开始!本次文章代码分为树莓派和NodeMCU两部分,我们先来看树莓派上的设置。
树莓派
config.yml配置修改
本篇文章我们使用的是chenzhuo贡献的ControMqtt技能,相关介绍,大家可以从这个网址进行参考:
wukong-ControMqtt-chenzhuo
由于这个技能是之前dingdang,也就是wukong的前身的一个项目,有两处需要简单修改一下,这里我们先修改一下config.yml配置文件,增加关于MQTT的几个配置。
我们在config.yml文件的最后增加以下内容:
# 使用mqtt与其他设备连接,作为PublishermqttPub: host: 'XXX.XXX.XXX.XXX' #替换为你树莓派IP,或者其它MQTT server的IP port: '1883' #MQTT Server默认的端口号 topic_s: '/wukong/mqtt' #接收设备反馈信息的主题
大家增加上面的内容就可以了。
ControMqtt.py 代码修改
大家可以直接copy下面的代码,然后覆盖掉原来的就可以了,下面的代码我已经PR到wukong-contrib仓库,正在等在wzpan大佬的审核,我已经测试过,没有问题。
# -*- coding: utf-8 #author: chenzhuo#Raspberry Pi or other platform can connect to the mqtt client,publisher and subscriber can access to bidirectional communication by switching their identities.#Example:you can get temperature of the enviroment collected by Arduino using Raspberry Pi when Raspberry Pi and Arduino communicate with each other.#The actions' file must be /home/pi/.wukong/action.json#Fix: Hcreak 2019.10#Fix: imliubo 2020.04 NodeMCU code reference: https://wukong.hahack.com/#/contrib?id=controlmqttimport paho.mqtt.client as mqttimport paho.mqtt.publish as publishimport timeimport jsonimport osfrom robot import config, loggingfrom robot.sdk.AbstractPlugin import AbstractPluginlogger = logging.getLogger(__name__)class Plugin(AbstractPlugin): SLUG = "mqttPub" def search_word(self, text): home_dir = os.path.expandvars('$HOME') location = home_dir + '/.wukong/action.json' if os.path.exists(location): f = open(location).read() try: fjson = json.loads(f) for key in fjson.keys(): value = fjson[key] if isinstance(value,list): # 向上兼容 for word in value: if word in text: return key,word if isinstance(value,dict): for word in value.keys(): if word in text: return key,value[word] except Exception as e: logger.error(e) self.say("抱歉出了问题", cache=True) return else: return def handle(self, text, parsed): profile = config.get() #get config if ( self.SLUG not in profile ) or ( 'host' not in profile[self.SLUG] ) or ( 'topic_s' not in profile[self.SLUG] ): self.say("主人,配置有误", cache=True) return host = profile[self.SLUG]['host'] port = 1883 if ( 'port' in profile[self.SLUG] ): port = int(profile[self.SLUG]['port']) topic_s = profile[self.SLUG]['topic_s'] # text = text.split(",")[0] #百度语音识别返回的数据中有个中文, topic_p,payload = self.search_word(text) try: # self.say("已经接收到指令", cache=True) mqtt_contro(host,port,topic_s,topic_p,payload,self.con) except Exception as e: logger.error(e) self.say("抱歉出了问题", cache=True) return def isValid(self, text, parsed): if self.search_word(text) == None: return False else: return Trueclass mqtt_contro(object): def __init__(self,host,port,topic_s,topic_p,message,mic): self._logger = logging.getLogger(__name__) self.host = host self.port = port self.topic_s = topic_s self.topic_p = topic_p self.message = message self.mic = mic self.mqttc = mqtt.Client() self.mqttc.on_message = self.on_message self.mqttc.on_connect = self.on_connect #mqttc.on_publish = on_publish #mqttc.on_subscribe = on_subscribe #mqttc.on_log = on_log if self.host and self.topic_p: publish.single(self.topic_p, payload=self.message, hostname=self.host,port=self.port) if self.port and self.topic_s and self.host: self.mqttc.connect(self.host, self.port, 5) self.mqttc.subscribe(topic_s, 0) #while True: # self.mqttc.loop(timeout=5) self.mqttc.loop_start() def on_connect(self,mqttc, obj, flags, rc): if rc == 0: pass else: self._logger.critical("error connect") def on_message(self,mqttc, obj, msg): if msg.payload: self.mqttc.loop_stop() self.mqttc.disconnect() self.mic.say(str(msg.payload.decode("utf-8"))) else: time.sleep(5) self.mqttc.loop_stop() self.mqttc.disconnect() self.mic.say("连接超时", cache=True) def on_publish(self,mqttc, obj, mid): self._logger.debug("mid: " + str(mid)) def on_subscribe(self,mqttc, obj, mid, granted_qos): self._logger.debug("Subscribed: " + str(mid) + " " + str(granted_qos)) def on_log(self,mqttc, obj, level, string): self._logger.debug(string)
增加 action.json 文件
这个文件是非常重要的,因为这里面的配置是用来命中技能和跟我们的下位机进行交互的,不过非常简单,我这里只加了一设备的配置,你如果还有别的设备,可以多加一点。
{ "开发板一": ["开灯","关灯"]}
这里来给大家稍微解释一下,方便大家任意的添加其它设备。
其中开发板一这个名字可以随便修改,其实这是一个MQTT主题,也就是我们下位机(NodeMCU)订阅的主题,你可以修改为其它的,然后开灯,关灯,是下发的命令,也是我们命中技能的关键词,当你唤醒wukong时,如果说出了开灯或者关灯,就会命中当前技能,然后向开发板一这个主题发送命令,我们的下位机收到后,通过比对命令,就可以触发响应的操作了。
这就是我们需要在树莓派上需要进行的所有操作,下面我们再来看看NodeMCU的代码。
NodeMCU
代码
大家还记得我们在如何打造属于你的私人语音助手之MQTT开发这篇文章中的代码吗?没错,本节的代码就是在那篇文章的基础上简单修改了下,直接上代码:
/@@* wukong-robot control the NodeMCU by MQTT Compile with Arduino IDE Author: IAMLIUBO Github: github.com/imliubo*/#include "EspMQTTClient.h"#define LED_PIN D0#define BLINK_PIN D4void onConnectionEstablished();EspMQTTClient client( "xxxxxxxxx", // Wifi ssid "xxxxxxxxx", // Wifi password onConnectionEstablished, // MQTT connection established callback "192.168.1.57", // MQTT broker ip 1883, // MQTT broker port "", // MQTT username "", // MQTT password "nodeMCU" // Client name );long lastTime = 0;uint8_t pin_status = 0;void LED_Control_Callback(const String & payload) { const char* p = payload.c_str(); // Serial.println(p); if (strstr(p, "开灯")) { //命令会在MQTT消息中传过来,我们通过利用strtr()函数,就可以执行相应的操作了。 digitalWrite(LED_PIN, LOW); client.publish("/wukong/mqtt", "主人,灯已打开!");// "/wukong/mqtt" 是在config.yml中定义的 topic_s 字段,用来回复wukong_robot的自定义消息,也就是执行完命令后的语音, } if (strstr(p, "关灯")) { digitalWrite(LED_PIN, HIGH); client.publish("/wukong/mqtt", "主人,灯已关闭!"); }}void onConnectionEstablished(){ client.subscribe("开发板一", LED_Control_Callback);// "开发板一" 是在action.json文件中定义的,用来接收wukong-robot下发的命令。}void setup(){ Serial.begin(115200); pinMode(BLINK_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, HIGH);}void loop(){ client.loop(); long now = millis(); if (now - lastTime > 1000) { lastTime = now; digitalWrite(BLINK_PIN, !pin_status); pin_status = !pin_status; }}
大家如果没有使用过Arduino开发NodeMCU(ESP8266),可以参考我的这篇文章使用Arduino来开发ESP8266先搭建好环境,大家直接将上面的代码下载到NodeMCU就可以了。
还没有评论,来说两句吧...