语音和家庭自动化自然结合。昨天回家告诉家里该做什么的梦想是今天的现实。
为了尝试家庭自动化,我决定修改我的一个旧项目BLElectric Light 101 ,并使其成为带有Mycroft/Picroft的语音控制物联网设备。
我已经从Chuck Hellyebuck 的 Filament Friday中“回收”了 3D 打印的 Edison 风格的电灯泡。灯泡中的新像素环提供光和颜色效果。在最初的项目中,我使用 Arduino 101 使用其板载 BLE 功能来控制环。
在这个项目中,我用基于 ESP8266 的开发板SparkFun ESP8266 Thing - Dev Board 替换了 Arduino 101 和 BLE,并将其变成了灯泡的 Web 服务器。这允许将灯泡或东西放置在我的家庭 Wifi 网络上。
在我的 MyCroft 技能中使用 python HTTP请求库给了我们:语音物联网!
我还决定将步进电机连接到电路板上。为什么?好吧,因为我可以!但说真的,从我用来启动它的语音命令中,你可能会猜到我将来会在这个项目中走向何方。. .
请注意,这个项目也可以使用带有 ESP8266 WiFi 的 Adafruit Feather HUZZAH来完成。
ESP8266 是一款支持 WiFi 的微控制器,可以通过 Arduino IDE 进行编程。adafruit 和 Sparkfun 基于这种芯片的开发板价格低廉,并且有大量的库、教程和代码示例可供学习。它们的主要优势是能够使用 Arduino IDE 和 WiFi 功能轻松对其进行编程。这些板的小尺寸使其非常容易嵌入到物体中并通过 WiFi 轻松连接。该板的一个相对缺点是 GPIO 和 ADC 引脚数量较少,但正如您从我的项目中看到的那样,Huzzah!用 PubNub 给我上色!,有办法克服这个问题。
总体而言,易用性、大量社区支持和共享以及 WiFi 使其成为 Makers 几乎理想的物联网微控制器。即将推出的ESP32 开发板希望让任何对引脚编号的担忧成为过去!
为了开始使用 Arduino 上的电路板,我将您指向 Sparkfun 和 adafruit 网站。两个指南都很有用,推荐的设置似乎适用于任一板:
两个教程都涵盖了使用开发板和 Arduino IDE 所需的驱动程序和库的安装。
安装 ESP8266 库后,您应该查看 WiFi 示例草图。我使用这些示例提出了我自己的 Web 服务器代码,如下所述。
Web 服务器是一个程序,它在端口上侦听http命令并通过沿指定路由执行代码来响应它们(路由是处理特定http命令的代码 - 这将在稍后显而易见)。ESP8266 提供了多个库和大量代码示例,使您可以非常轻松地设计自己的 Web 服务器。不要害怕,您的 Web 服务器草图将遵循与 Arduino IDE 程序相同的结构。它将具有 setup() 和 loop() 函数,以及一些函数等。
我们的网络服务器将有 4 条路线。一个用于处理根部,一个用于打开和关闭灯泡,一个用于处理灯光颜色,最后一个用于控制我们的步进电机。您可以将这些路由中的每一个视为按需执行的函数或代码块。在未来的重构中,灯泡相关的路由将更改为一个路由,有 2 个参数,但稍后会更多。现在,我将逐行介绍如何在 ESP8266 板上设置 Web 服务器的代码。
就像在 Arduino IDE 中编写的任何程序一样,我们将编写我们的 Web 服务器作为草图上传到我们的板上。为了设置我们的服务器,我们需要导入一些标准库:
#include
#include
#include
#include
为了将我们的 Web 服务器连接到我们的 WiFi 网络,我们必须提供我们的网络 ID 和密码,并创建一个 Web 服务器对象来监听端口 80:
const char* ssid = "********";
const char* password = "**********";
ESP8266WebServer server(80);
如果您遵循代码,您将看到处理我上面提到的路由的函数列表。但是,我们将首先进入草图的setup()函数。当我们正在编写我们的第一个 Web 服务器时,我们需要将消息写入串行端口。这将使我们能够看到我们正在使用的开发板的IP 地址,这对于我们向开发板发送命令至关重要!
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("esp8266")) {
Serial.println("MDNS responder started");
}
在串行输出中看到 IP 地址后,请记下它。它将用于在我们的 MyCroft 技能中与董事会沟通。它将类似于172.16.254.1 ,为了连接到板,您将发出一个 http 命令,例如:
http://172.16.254.1/stepper
IP 是服务器的主机地址,“/stepper”是我们在代码中指定的资源路径或路由。事实上,以下几行指定了我们的每个路径和每个服务器应该执行的响应。要理解这一点,请像 Web 服务器一样阅读它:
从 http 请求 http://172.16.254.1/stepper 接收到路径“/stepper”后,我将执行名为 handleStepper 的函数中的代码
server.on("/", handleRoot);
server.on("/lamp", handleCommand);
server.on("/color", handleColor);
server.on("/stepper",handleStepper);
步进器处理代码很容易理解(我为使用延迟功能道歉),因此不是很有趣。
因此,我将介绍handleCommand()代码,它不仅可以帮助您学习如何创建和处理资源路径,而且还必须将参数和值发送到您的函数!handleCommand的代码在函数void handleCommand()中定义,它必须具有参数中指定的确切名称才能传递给server.on()函数。
void handleCommand()
{
String message = "done";
int commandValue;
...
我们创建一个名为message的 String 变量,以传回发出请求的客户端。您可以在此过程中修改此字符串以将您希望返回的任何消息传递给您的客户。客户端可以是您的网络浏览器、您编写的程序,或者在我们的例子中是 Mycroft/Picroft 系统。
然后我们创建一个名为commandValue的整数变量。我们的客户端将传入一个参数值。在这种情况下,参数值将是 1 或 0,具体取决于客户端是否希望打开或关闭灯。在浏览器窗口中,这看起来像:
http://176.16.254.1/lamp?cmd=1
or
http://176.16.254.1/lamp?cmd=0
还记得我们创建了/lamp资源路径来处理这个吗?当遵循此资源路径时,调用来处理它的函数可以访问使用server.arg()函数发送的参数和值。在我们的例子中,只有一个叫做cmd:
commandValue = (server.arg("cmd")).toInt();
首先,当然,我们检查以确保客户端发送了一个参数,如果没有使用我们的消息变量将错误消息发送回客户端。有更好的方法来处理这个问题,但是由于我们正在创建客户端和它将发送的命令,所以现在这已经足够好了。cmd参数值作为字符串类型接收。为了在非常优雅的switch/case控制结构中有效地使用它,我们使用 String 类的toInt() 函数将其转换为整数类型。然后我们可以执行与打开或关闭灯对应的代码:
switch(commandValue)
{
case 0:
currentColor = 0;
setLampColor();
break;
case 1:
currentColor = 1;
setLampColor();
break;
default:
message = "choose from 0, 1 for lamp off/on";
break;
}
这里有两个重点。首先,如果客户端应该发送一个无意义的cmd参数值,我们将向客户端发送一条错误消息。其次,我们使用一个名为currentColor的状态变量来设置和跟踪当前的灯颜色。在打开或关闭灯的情况下,这将分别是白色或黑色。使用这种方法,我们可以将setLampColor()重新用于开/关和特定颜色设置。变量currentColor不仅充当灯泡的状态变量,而且还是新像素颜色对象数组的索引。
如果您遵循所有这些,您可以设计自己的 Web 服务器以嵌入任何 8266 项目并制作连接的东西!
现在 Web 服务器已经启动并运行,我们可以在 python 中开发客户端。
正如我们在上面看到的,我们可以使用 Web 浏览器作为我们的客户端,甚至可以编写我们自己的自定义 HTML/Javascrip/CSS 页面来与之交互。我们也可以编写自己的 python 脚本来做同样的事情。只要运行它的计算机可以访问您的网络,您就可以开展业务。然后,您可以在 Mycroft 中实施技能之前测试您希望包含在语音技能中的大部分功能。
如果您熟悉使用 Mycroft 技能,则不必编写脚本来测试 Mycroft 之外的客户端功能。Mycroft 技能中的大部分动作发生在技能的__init__.py文件中。您可以在此文件中实现 python 代码写入并从那里进行调试。由你决定!
如果您还没有创建自己的技能,我建议您查看以下资源:
看完这些后,继续学习你自己的“Hello World”技能并开始运行吧!以下不是技能设计和实施的综合教程,只是亮点和一些见解。
一切由此开始:
import requests
如果你能在你的树莓派上用 python 做到这一点,你可以在 Mycroft 下用你的声音来控制它!
使用 python 与我们的 Web 服务器交互的关键是Requests: HTTP for Humans库。没有什么比他们自己的描述更能描述这个库了:
Requests 是一个优雅而简单的 Python HTTP 库,专为人类构建。
它是为人类使用而设计和建造的!我们将使用这个库在语音控制下与我们的物联网设备进行通信。该库将允许我们在我们的 python 意图代码中简单地复制上面讨论的 http 命令。
此 IoT 演示技能将由 3 个意图组成:
def handle_lamp_command_intent(self, message):
def handle_lamp_color_intent(self, message):
def handle_feeder_intent(self,message):
这些意图中的每一个都将对应于我们服务器上的一个路由(根路由除外)。它不必是这样,也许这不是最有效的设计,但它有效,我现在会坚持下去。
在 initialize() 函数中注册意图:
lamp_color_intent = IntentBuilder("LampColorIntent").require("LampColorKeyword").require("ColorName").build()
self.register_intent(lamp_color_intent, self.handle_lamp_color_intent)
请务必注意,此意图将使用LampColorKeyword.voc文件将话语与此特定意图相匹配。该文件如下所示:
change
make
这就是说,“嘿,Mycroft,让颜色变成粉红色。” 这不是很理想,但我在为这个意图创建正确的用语并让它识别这样的短语时遇到了一些困难。当我更多地了解意图及其与 vocab 或 .voc 文件的关系时,在我看来,这些意图是由构成话语的关键词组成的,而不是完整的话语。我认为这可能会引起对重叠关键字冲突的担忧。我发现并继续发现以下资源是我继续理解意图、关键字等的关键:
当我们改变灯的颜色时,我们需要能够响应用户请求的颜色。这是通过使用正则表达式完成的。文件lampactions.rx如下所示:
(turn|switch) (?Pon|off) (?P.*)
(change|make) (?Pcolor|color to) (?P.*)
如果 btoharye 看到这个,他会注意到他的家庭助理工作有一些相似之处!
第二条线是我们的重点。这个正则表达式将允许我们在消息总线上通过变量名称ColorName 传递用户命名的颜色。通过匹配单词change或make来进行匹配。我目前没有使用Action变量。
def handle_lamp_color_intent(self, message):
lamp_color = message.data.get("ColorName")
LOGGER.info("Lamp Color: " + lamp_color)
if self.color_map.has_key(lamp_color):
self.speak_dialog("lamp.color",{"color": lamp_color})
color_index = self.color_map[lamp_color]
r = requests.get('http://ip_here/color?color='+str(color_index))
else:
self.speak_dialog("lamp.color.error",{"color": lamp_color})
我们可以创建函数处理程序并使用参数消息从用户的话语中获取命名的颜色:
def handle_lamp_color_intent(self, message):
lamp_color = message.data.get("ColorName")
LOGGER 对象将这些语句打印到 /var/log/mycroft-skills.log。另一种调试方法是让 Mycroft 使用 self.speak_dialog() 函数说话。在此之前,我正在单独编写 python 技能功能,并将该代码放入__init__.py文件中。但是,熟悉“在”Mycroft 本身内进行调试会为您节省一些时间,我建议您转向这种方法。
LOGGER.info("Lamp Color: " + lamp_color)
在这之前,我们创建了一个名为color_map 的 python 地图:
def __init__(self):
. . .
self.color_map = {'black': 0, 'white': 1, 'blue': 2, 'green': 3, 'orange': 4, 'red': 5, 'purple': 8, 'yellow': 9, 'pink': 10}
这种方法允许我们获取 ColorName 话语并将其用作访问相应颜色值以发送到 ESP8266 Dev Thing 的键。从草图中的这段代码可以看出,颜色键值和颜色索引匹配。
// From the sketch file running on the ESP8266 board
void setStandardColors()
{
black = neoRing.Color(0,0,0); //used for 'off' state
white = neoRing.Color(255,255,255);
blue = neoRing.Color(0,0,255);
green = neoRing.Color(255,0,0);
orange = neoRing.Color(140,255,0);
red = neoRing.Color(0,255,0);
red_orange = neoRing.Color(69,255,0);
sky_blue = neoRing.Color(206,135,235);
purple = neoRing.Color(0,255,255);
yellow = neoRing.Color(255,255,0);
pink = neoRing.Color(0,255,180);
standardColors[0] = black;
standardColors[1] = white;
standardColors[2] = blue;
standardColors[3] = green;
standardColors[4] = orange;
standardColors[5] = red;
standardColors[6] = red_orange;
standardColors[7] = sky_blue;
standardColors[8] = purple;
standardColors[9] = yellow;
standardColors[10] = pink;
}
这使得发送颜色消息变得容易,尤其是当我们使用请求库时!
if self.color_map.has_key(lamp_color):
self.speak_dialog("lamp.color",{"color": lamp_color})
color_index = self.color_map[lamp_color]
r = requests.get('http://ip_here/color?color='+str(color_index))
无论您在哪里看到 ip_here ,都将其替换为您的板 IP 地址。未来版本将在 settings.json文件中包含此内容。
在没有与可用颜色匹配的键的情况下,我们使用lamp.color.error.dialog文件中的错误对话框报告此情况。我们可以通过重复请求的颜色来帮助用户,并提供一些有效的选项(有关更多信息,请参见下文)。
sorry {{color}} not available maybe try red green or blue
sorry {{color}} not available maybe try orange yellow or purple
{{color}} is not an option you can try red green or blue instead
{{color}} is not an option you can try purple orange or yellow
sorry could you try purple blue or orange instead of {{color}}
sorry could you try red yellow or green instead of {{color}}
有这么多颜色!您怎么知道要尝试哪个,以及我们如何在提供有用建议的同时避免认知超负荷。. .这就是语音接口的挑战。
当 Mycroft 无法识别所请求的颜色时,例如“chartreuse”,它会回复一个包含 3 个备选颜色的列表。这三种选择是一种很好的方式,不仅可以让用户知道一些有效的选项,还可以引导用户使用基本的颜色请求。所有可用颜色选项的综合列表将无法处理并且占用太多时间。
sorry {{color}} not available maybe try red green or blue
sorry {{color}} not available maybe try orange yellow or purple
...
当我反思良好的声音设计时,我不喜欢这里的短语:“..maybe try”?设备不应该知道它知道什么颜色吗?跛脚,我知道,我会摆脱这个。. .
此外,这些替代方案在.dialog 文件中进行了“硬”编码。未来的重构可能会受益于从可用的颜色中证明 3 种随机颜色。另一个选项,可以提供更大的智能和响应能力,是让 Mycroft 提供 2 或 3 种颜色选项,在色调上与请求的选项最接近。
目前 Mycroft 不直接支持多轮对话:
Mycroft 是否支持多回合/会话技能?
这是一个进展中的工作。在当前的主线中,有能力告诉 Mycroft 在说完任何话后立即听。在技能中,您可以使用...调用它self.speak("utterance", expect_response=True)
...请参阅https://github.com/MycroftAI/mycroft-core/blob/dev/mycroft/skills/core.py#L3262)
这只允许用户与 Mycroft 对话,而不必说“Hey Mycroft”。
对于真正的对话交互,有一些正在进行的工作来实现 converse() 方法。这是即将到来的拉取请求:https ://github.com/MycroftAI/mycroft-core/pull/9253
这将允许技能在调用 Adapt 意图解析器之前预览话语。只有最近使用的技能会收到 converse() 通知,并按使用顺序传递给他们。因此,您可以编写执行以下操作的技能:
@intent_handler(IntentBuilder().require('alarm').require('cancel').build()) def handle_cancel_alarm(): self.speak("are you sure you want to cancel?", expect_response=True) def converse(self, utterances,): if utterances == "yes": # do whatever self.speak("Alarm canceled")
这显然是头脑简单,但使用此代码,以下交互将起作用:
用户:嘿,Mycroft,请取消闹钟 Mycroft:你确定要取消吗? 用户:是 Mycroft:警报取消
未来将会有更多的工具让建立和管理对话变得更容易,但这是基础。
在这里,当我们请求未知颜色时,从用户体验的角度来看,多轮方法将是理想的。
您可以在我的文章 BLElectric Light 101中了解 3D 打印以及如何将组件组合在一起。我想在此重申 Lulzbot mini 能够在没有支撑的情况下将灯泡打印为单个单元。试试吧,它可以工作,让您的印后整理变得更加轻松!
希望你喜欢这个项目。. .更多在路上!
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !