Arduino应用开发——通过小爱同学控制灯光
目录
前言
我之前发布了一篇关于如果用手机APP控制LED灯的文章,是基于Blink APP和它的服务器实现的,这一讲我们在之前的基础上做一些扩展,通过手机的小爱同学,使用语音控制灯光。没有看过上一篇博客的同学可以先看一下。
1 工作原理
Blinker APP控灯原理:
Blinker厂家有自己的一个服务器,我们的设备(esp8266、esp32、手机等)都可以通过网络接入到这个服务器,手机app我们看不到源码不确定是以什么样的方式接入的,我主要讲一下MCU(esp8266、esp32等)这边,MCU是通过WIFI连接到互联网的,然后通过MQTT协议接入到Blinker的服务器,MCU作为客户端,服务器作为服务端。具体MQTT的实现原理这里不多说了,光是MQTT这一个话题都可以写好多文章了,想深入了解的同学可以自行查阅资料。
当MQTT连成功之后,MCU会一直侦听,等待服务器下发数据。这个时候我们可以通过手机app操作,比如点一下开灯,服务器收到这个开灯命令后会通过MQTT发送命令到MCU,MCU接收到数据并解析命令后执行点灯的操作。服务器在这里相当于一个中转站,把手机的操作转发到设备端,实现了手机和设备之间的互动。不过有一点要说明的是,设备和服务器是通过MQTT连接的,而手机app和服务器则不一定是用MQTT,个人觉得用http的可能性更大,但是看不到源码,所以实际上是怎么通讯的就不清楚了,这个也不是很重要,并不影响实际的使用。
小爱同学控灯原理:
说明:这里的小爱同学可以是小米手机里面的小爱,也可以是小米的智能音箱。
前面我们已经把我们的设备(esp8266、esp32等)连接到了Blinker的服务器,实现了数据的上传下发。而Blinker本身是有对接小爱同学的,并且设定好了一些固定的语音指令。我们只通过米家APP关联Blinker的账号就能实现小爱同学和Blinker服务器的联动,再结合之前Blinker服务器和我们的硬件设备的连接,就可以间接的实现小爱同学和硬件设备的交互。
简单来说就是我们的语音指令通过小爱同学发送到Blinker的服务器,再由Blinker的服务器转发到我们的硬件设备。Blinker服务器在这里起到一个数据解析、中转的作用。
2 硬件准备
我这里以ESP8266和ESP32为例讲解,其他MCU方法和原理都是一样的,所以根据自己的MCU选择其中一种即可。
硬件配置如下:
模块 | 型号 | 说明 |
---|---|---|
ESP8266 | ESP-12F | 这是安信可的一款模组,内部主要是用乐鑫的ESP8266EX再加上一个片外FLASH组成,开发板型号是NodeMCU-12F(CH340版本) |
ESP32 | ESP-WROOM-32 | MCU是乐鑫的一款芯片,开发板型号ESP32 DEVKITV1 |
具体的硬件参数和电路原理图这里就不发出来了,不同厂家做的开发板引脚可能会有点差别。
3 软件准备
版本说明:
本文测试时各软件使用的版本如下:
软件 | 版本 | 备注 |
---|---|---|
Arduino IDE | 1.8.16 | Blinker需要配合1.8.x及以上版本的Arduino IDE |
ESP8266 package | 3.0.2 | Blinker需配合使用3.0.0或以上release版本的ESP8266 package |
ESP32 package | 3.0.2 | Blinker需配合使用2.0.0或以上版本的ESP32 package |
Blinker arduino package | 0.3.9 | 当前最新版本,以后可能会有更新 |
Blinker APP | 2.5.2(安卓版) | 当前最新版本,以后可能会有更新 |
米家APP | 7.5.705.5319(安卓版) | 当前最新版本,以后可能会有更新 |
3.1 Arduino IDE环境搭建
这个在我的博客里面讲过很多次了,这里就不再说了,不懂的同学先按上一讲把环境搭好(根据自己的MCU选择对应的教程)。
Arduino应用开发——手机APP控制LED
esp8266开发入门教程(基于Arduino)——环境安装
ESP32 Arduino开发环境搭建
3.2 Blinker APP账号注册和使用
这个内容比较多,这里不再赘述了,不懂的同学先看下上一讲的第4点。
Arduino应用开发——手机APP控制LED
4 编写程序
上一讲的教程讲了怎么用Blinker APP创建设备并实现远程控制。
Arduino应用开发——手机APP控制LED
这一讲在这基础上增加小爱同学的语音控制。我们保留上一讲的功能,然后增加电源控制和设备查询这两个回调函数,这两个函数的接口在Blinker的库源码中已经定义好了,我们直接使用即可。我们通过电源控制回调函数实现LED灯的开关控制。
示例代码:
提示:代码中的WIFI的账号和密码根据实际情况修改。
#define BLINKER_WIFI
#define BLINKER_MIOT_LIGHT // 灯设备
// #define BLINKER_MIOT_OUTLET // 插座设备
// #define BLINKER_MIOT_MULTI_OUTLET // 多个插座设备
// #define BLINKER_MIOT_SENSOR // 传感器设备
// #define BLINKER_MIOT_FAN // 风扇设备
// #define BLINKER_MIOT_AIR_CONDITION // 空调设备
#include <Blinker.h>
char auth[] = "e6fce38e525a"; // 设备密钥
char ssid[] = "test"; // WIFI账号
char pswd[] = "123456789"; // WIFI密码
#define LED_PIN 2 // LED引脚
// 新建组件对象
BlinkerButton Button1("btn-abc");
BlinkerNumber Number1("num-abc");
int counter = 0;
bool oState = false;
// 电源控制回调函数
void miotPowerState(const String & state)
{
BLINKER_LOG("need set power state: ", state);
if (state == BLINKER_CMD_ON)
{// 打开开关
digitalWrite(LED_PIN, HIGH); // 开灯
BlinkerMIOT.powerState("on");
BlinkerMIOT.print();
oState = true;
}
else if (state == BLINKER_CMD_OFF)
{// 关闭开关
digitalWrite(LED_PIN, LOW); // 关灯
BlinkerMIOT.powerState("off");
BlinkerMIOT.print();
oState = false;
}
}
// 查询设备状态回调函数
void miotQuery(int32_t queryCode)
{
BLINKER_LOG("MIOT Query codes: ", queryCode);
switch (queryCode)
{
case BLINKER_CMD_QUERY_ALL_NUMBER : // 查询所有设备
BLINKER_LOG("MIOT Query All");
BlinkerMIOT.powerState(oState ? "on" : "off");
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_POWERSTATE_NUMBER : // 查询电源类设备
BLINKER_LOG("MIOT Query Power State");
BlinkerMIOT.powerState(oState ? "on" : "off");
BlinkerMIOT.print();
break;
default : // 查询其他设备
BlinkerMIOT.powerState(oState ? "on" : "off");
BlinkerMIOT.print();
break;
}
}
// 按下按键即会执行该函数
void button1_callback(const String & state)
{
BLINKER_LOG("get button state: ", state);
digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // 翻转LED灯状态
}
// 如果未绑定的组件被触发,则会执行其中内容
void dataRead(const String & data)
{
BLINKER_LOG("Blinker readString: ", data);
counter++;
Number1.print(counter);
}
void setup()
{
// 初始化串口
Serial.begin(115200);
BLINKER_DEBUG.stream(Serial);
BLINKER_DEBUG.debugAll();
// 初始化LED的IO
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
// 初始化blinker
Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead); // 未绑定设备回调
Button1.attach(button1_callback); // 按键回调
BlinkerMIOT.attachPowerState(miotPowerState); // 电源控制回调
BlinkerMIOT.attachQuery(miotQuery); // 查询设备状态回调
}
void loop() {
Blinker.run();
}
5 关联米家APP和Blinker设备
打开 米家APP
,点击我的
,点击其他平台设备
,点击添加
,在列表中找到点灯科技
,点击绑定账号
,登录你的Blinker账号,最后点击同步设备
即可。设备同步成功之后就可以看到你在Blinker APP上创建的所有设备。
提示1:Blinker账号的注册以及APP的使用请跳转到3.2查看。我在上一篇博客有详细的教程。
提示2:同步设备可能会出现失败的情况,我一开始同步的时候一直失败,过了一段时间之后再尝试又没出现了,不知道是不是APP的bug。
6 语音控制测试
特别说明:我没有小爱同学的智能音箱,我这里是直接用小米的手机来测试的。
注:其他品牌的手机可以下载米家APP,但是没法使用小爱同学。
把烧录好代码的设备通上电,通过串口打印的一些信息,我们可以看到设备正常连上网络之后会连接到Blinker的服务器,连接成功之后可以在Blinker的APP上看到设备的状态由 ‘离线’ 变成 ‘在线’。
小爱同学支持的开关类控制语音如下:
打开/关闭{门口}的{灯}
提示1:{}里面的内容是通配多种关键词的,你可以在Blinker APP修改设备的名称,然后语音对应的命令也要跟着改变,保持一致才能正确的控制设备。
对着小米手机呼叫小爱同学
,收到回应之后再说打开灯
,就能看到设备的LED灯被打开了。同理,关闭灯的指令也是一样的操作。
通过串口打印的信息也可以看到开灯和关灯相关的指令。
至此,从小爱同学到硬件设备的整个流程就走通了。流程走通了之后我们就可以在这个基础上增加更多的功能,比如控制灯的亮度,设备工作模式等等,也可以修改设备的名称,或者控制更多其他类型的设备。
7 进阶用法
通过Blinker关联小爱同学控制设备其实有很大的局限性,因为小爱同学所有的语音指令都是Blinker定义好的,不支持做过多的修改,而且目前支持的语音指令实在是有点少,当我们需要自定义一些指令时并不能通过这套代码实现。不过如果只是玩一下或者学习的话,还是可以去研究一下的。
关于Blinker以及小爱同学所支持的设备和语音指令在点灯科技的官网上其实有比较详细的介绍。
点灯科技小米小爱技术文档:https://diandeng.tech/doc/xiaoai
我们这里也基于这些给定的语音指令做一些进阶的操作。
前面的示例我们通过小爱同学控制了灯的亮灭,然后现在我们增加LED灯亮度、色温以及模式的控制。
特别说明:要实际控制灯的亮度和色温的话我们需要用到RGB灯,而且有些比较简单的GRB灯只能调节三色灯的亮灭,最多也只能调出8种颜色,如果需要调节出多种色温,还需要调节三色灯里面每一个灯的亮度。每个灯的亮度阶梯越多,能跳出来的色彩就越多。如果是单色LED的话最多只能调亮度,是没办法改变颜色的,亮度调节可以通过PWM实现。
我这里因为现在手头上也没有合适的灯,这里就不做具体的演示了,我这里只讲一下怎么把小爱语音和LED灯的控制代码关联起来,至于实际的一些操作,感兴趣的同学可以自己再补充。
7.1 亮度控制
小爱同学支持的亮度控制语音如下:
把{卧室}的{灯}的{亮度调高一点}
亮度范围为1-100
注:括号里面的的关键字是可以用户自己修改定义的。比如:灯可以换成其他关键字,如氛围灯、大灯等,这个是跟你在Blinker APP里面修改的设备名称保持一致的。
示例代码:
提示:我这里只把新增的函数列出来,等后面把亮度、色温以及模式的控制全部讲完之后再列一个完整的代码。
// 灯光亮度控制回调函数
void miotBright(const String & bright)
{
BLINKER_LOG("need set brightness: ", bright); // 打印需要设置的亮度
uint8_t colorW = bright.toInt();
BLINKER_LOG("now set brightness: ", colorW);
// 在此处我们可以根据实际情况添加一些控制的代码,比如:通过修改PWM占空比的值,从而达到调节灯亮度的目的
BlinkerMIOT.brightness(colorW);
BlinkerMIOT.print();
}
// 灯光亮度控制初始化
BlinkerMIOT.attachBrightness(miotBright);
测试结果:
特别说明:因为我的硬件并没有接入能调光的灯,所以这里我只把接收到的语音指令打印了出来,硬件上并没有做实际的操作。
对着小爱同学说:把灯的亮度调到60
。通过串口打印的信息我们可以看到,设备已经收到了这个命令,并且把要调节的亮度值60解析出来了,只不过我实际上并没有做控灯的操作,因此,LED灯是没有变化的。
7.2 色温控制
小爱同学支持的色温控制语音如下:
把{卧室}的{灯}调为{红色}
颜色范围为0-16777215(0xFFFFFF)
示例代码:
提示:我这里只把新增的函数列出来,等后面把亮度、色温以及模式的控制全部讲完之后再列一个完整的代码。
// 灯光色温控制回调函数
void miotColor(int32_t color)
{
BLINKER_LOG("need set color: ", color);
// 从接收到的数据中解析得到需要配置的R G B三色光的亮度值
uint8_t colorR = color >> 16 & 0xFF;
uint8_t colorG = color >> 8 & 0xFF;
uint8_t colorB = color & 0xFF;
BLINKER_LOG("colorR: ", colorR, ", colorG: ", colorG, ", colorB: ", colorB);
// 在此处我们可以根据实际情况添加一些控制的代码,比如:通过修改R G B三个灯的亮度从而达到修改灯颜色的目的
BlinkerMIOT.color(color);
BlinkerMIOT.print();
}
// 灯光色温控制初始化
BlinkerMIOT.attachColor(miotColor);
测试结果:
特别说明:因为我的硬件并没有接入能调光的灯,所以这里我只把接收到的语音指令打印了出来,硬件上并没有做实际的操作。
对着小爱同学说:把灯调为紫色
。通过串口打印的信息我们可以看到,设备已经收到了改命令,只不过我实际上并没有做控灯的操作,因此,LED灯是没有变化的。
提示:这里RGB三元光的亮度范围是0-255,把三种亮度不同的光混合在一起就能产生多种多样的颜色。
7.3 模式控制
小爱同学支持的色温控制语音如下:
把{卧室}的{灯}设置为{xx模式}
示例代码:
提示:我这里只把新增的函数列出来,后面有完整的代码。
// 模式控制回调函数
void miotMode(uint8_t mode)
{
BLINKER_LOG("need set mode: ", mode); // 打印接收到需要设置的模式,数值是(0-6),对应7种模式
if (mode == BLINKER_CMD_MIOT_DAY) {
// 日光模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_NIGHT) {
// 夜光模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_COLOR) {
// 彩光模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_WARMTH) {
// 温馨模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_TV) {
// 电视模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_READING) {
// 阅读模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_COMPUTER) {
// 电脑模式,在这里面可以添加自己实际需要控制的代码
}
BlinkerMIOT.mode(mode);
BlinkerMIOT.print();
}
// 模式控制初始化
BlinkerMIOT.attachMode(miotMode);
测试结果:
特别说明:因为我的硬件并没有接入能调光的灯,所以这里我只把接收到的语音指令打印了出来,硬件上并没有做实际的操作。
对着小爱同学说:把灯调为夜光模式
。通过串口打印的信息我们可以看到,设备已经收到了改命令,只不过我实际上并没有做控灯的操作,因此,LED灯是没有变化的。
前面我也讲过,Blinker这套方案有比较大的局限性,就比如这里的模式,虽然支持多种模式,但是每一个模式都只能通过固定的关键字(日光、夜光、彩光等)来触发,不能自定义,如果是控灯还好,很多都可以用上,但是如果控制的是其他设备,这些模式的关键字就牛头不对马嘴了。
我也没有找到改指令关键字的接口,应该是Blinker没有开放这个接口或者不支持这个功能。这一点是不太友好的,不过Blinker毕竟是一个公司,目的还是盈利,而且维护服务器也是需要成本的,白嫖党也不能要求太多。
完整的进阶示例代码:
#define BLINKER_WIFI
#define BLINKER_MIOT_LIGHT // 灯设备
// #define BLINKER_MIOT_OUTLET // 插座设备
// #define BLINKER_MIOT_MULTI_OUTLET // 多个插座设备
// #define BLINKER_MIOT_SENSOR // 传感器设备
// #define BLINKER_MIOT_FAN // 风扇设备
// #define BLINKER_MIOT_AIR_CONDITION // 空调设备
#include <Blinker.h>
char auth[] = "e6fce38e525a"; // 设备密钥
char ssid[] = "tst"; // WIFI账号
char pswd[] = "123456789"; // WIFI密码
#define LED_PIN 2 // LED引脚
// 新建组件对象
BlinkerButton Button1("btn-abc");
BlinkerNumber Number1("num-abc");
int counter = 0;
bool oState = false;
// 电源控制回调函数
void miotPowerState(const String & state)
{
BLINKER_LOG("need set power state: ", state);
if (state == BLINKER_CMD_ON)
{// 打开开关
digitalWrite(LED_PIN, HIGH); // 开灯
BlinkerMIOT.powerState("on");
BlinkerMIOT.print();
oState = true;
}
else if (state == BLINKER_CMD_OFF)
{// 关闭开关
digitalWrite(LED_PIN, LOW); // 关灯
BlinkerMIOT.powerState("off");
BlinkerMIOT.print();
oState = false;
}
}
// 查询设备状态回调函数
void miotQuery(int32_t queryCode)
{
BLINKER_LOG("MIOT Query codes: ", queryCode);
switch (queryCode)
{
case BLINKER_CMD_QUERY_ALL_NUMBER : // 查询所有设备
BLINKER_LOG("MIOT Query All");
BlinkerMIOT.powerState(oState ? "on" : "off");
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_POWERSTATE_NUMBER : // 查询电源类设备
BLINKER_LOG("MIOT Query Power State");
BlinkerMIOT.powerState(oState ? "on" : "off");
BlinkerMIOT.print();
break;
default : // 查询其他设备
BlinkerMIOT.powerState(oState ? "on" : "off");
BlinkerMIOT.print();
break;
}
}
// 灯光亮度控制回调函数
void miotBright(const String & bright)
{
BLINKER_LOG("need set brightness: ", bright);
uint8_t colorW = bright.toInt();
BLINKER_LOG("now set brightness: ", colorW);
// 在此处我们可以根据实际情况添加一些控制的代码,比如:通过修改PWM占空比的值从而达到调节灯亮度的目的
BlinkerMIOT.brightness(colorW);
BlinkerMIOT.print();
}
// 灯光色温控制回调函数
void miotColor(int32_t color)
{
BLINKER_LOG("need set color: ", color);
// 从接收到的数据中解析得到需要配置的R G B三色光的亮度值
uint8_t colorR = color >> 16 & 0xFF;
uint8_t colorG = color >> 8 & 0xFF;
uint8_t colorB = color & 0xFF;
BLINKER_LOG("colorR: ", colorR, ", colorG: ", colorG, ", colorB: ", colorB);
// 在此处我们可以根据实际情况添加一些控制的代码,比如:通过修改R G B三个灯的亮度从而达到修改灯颜色的目的
BlinkerMIOT.color(color);
BlinkerMIOT.print();
}
// 模式控制回调函数
void miotMode(uint8_t mode)
{
BLINKER_LOG("need set mode: ", mode); // 打印接收到需要设置的模式,数值是(0-6),对应7种模式
if (mode == BLINKER_CMD_MIOT_DAY) {
// 日光模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_NIGHT) {
// 夜光模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_COLOR) {
// 彩光模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_WARMTH) {
// 温馨模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_TV) {
// 电视模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_READING) {
// 阅读模式,在这里面可以添加自己实际需要控制的代码
}
else if (mode == BLINKER_CMD_MIOT_COMPUTER) {
// 电脑模式,在这里面可以添加自己实际需要控制的代码
}
BlinkerMIOT.mode(mode);
BlinkerMIOT.print();
}
// 按下按键即会执行该函数
void button1_callback(const String & state)
{
BLINKER_LOG("get button state: ", state);
digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // 翻转LED灯状态
}
// 如果未绑定的组件被触发,则会执行其中内容
void dataRead(const String & data)
{
BLINKER_LOG("Blinker readString: ", data);
counter++;
Number1.print(counter);
}
void setup()
{
// 初始化串口
Serial.begin(115200);
BLINKER_DEBUG.stream(Serial);
BLINKER_DEBUG.debugAll();
// 初始化LED的IO
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
// 初始化blinker
Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead); // 未绑定设备回调
Button1.attach(button1_callback); // 按键回调
BlinkerMIOT.attachPowerState(miotPowerState); // 电源控制回调
BlinkerMIOT.attachQuery(miotQuery); // 查询设备状态回调
BlinkerMIOT.attachBrightness(miotBright); // 灯光亮度控制回调
BlinkerMIOT.attachColor(miotColor); // 灯光色温控制回调
BlinkerMIOT.attachMode(miotMode); // 模式控制回调
}
void loop() {
Blinker.run();
}
结束语
好了,关于如何在Arduino上通过小爱同学控制灯光就到这里。Blinker的这套框架局限性比较大,只适用于某些设备,也只能使用比较固定的命令,不能做过多的修改,这一点是不太好的,但好处是比较省事,不需要我们做语音这部分的开发,有利有弊吧,对于学生党或者刚入门的初学者来说是可以去玩一下的,而且Blinker维护服务器本身也是需要成本的,能免费提供这个平台给大家用就算不错了,白嫖党要求也不能太过分是吧。如果有条件的话就自己搭服务器,这样可操作性会大大的增加。
想了解更多Arduino的内容,可以关注一下博主,后续我还会继续分享更多的经验给大家。
esp8266基于Arduino的开发教程汇总:
https://blog.csdn.net/ShenZhen_zixian/article/details/121659482
还有什么问题的话,欢迎在评论区留言。
如果这篇文章能够帮到你,就…你懂得。