智能家居的话,如果只能手动控制会觉得不太智能,如果能用说的或者自动的话体验可能会更好一些。
智能家居其实就是为了给生活添加一些便利,懒人推送科技发展是没错的。
对于个人而言接入智能家居无非有以下几个原因:
1、动动手动动嘴不动腿就是懒恨不能意念控制
2、开关对小朋友不友好,个头不够摸不到开关
3、全屋联动回家模式离家模式等等
基于以上几个原因
通过尝试。实现了语音控制 HA 设备的基本功能。
主要思路如图:
![流程.png](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZrKOibRdpw3x6oa4GvmwJRadt7eZCvF0t9NBM8M04okc3Yv77BtuTAqA/640?wx_fmt=png&from=appmsg)
开始想着增加机械开关手动去操作,后来想了以下都语音控制了还要物理开关干嘛呢。
小朋友和老人,直接语音控制开关设备。就很舒服呢。
对于红外设备,还没研究透彻怎么去获取遥控器的红外编码,然后发射出来。这样就可以实现更多的功能。
可以语音控制空调、电视等红外设备。
[智能家居]MQTT 控制 HomeAssistant 设备
https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=44644&fromuid=15918
(出处: 物联网开发者社区-安信可论坛)
这里主要还是用到了 HomeAssistant 的自动化功能,这种设置不需要对 HomeAssistant 有很深的了解,简单配置即可。
对于小白用户体验更好一些,上手就用。对于大佬的话,可以延伸出更多的功能。
下面介绍一下主要功能模块
一、离线语音模组系列
VC 系列模组是我司开发的一款 AI 离线语音识别的产品,主芯片是云知声推出的离线语音识别芯片锋鸟 M(US516P6),具有高可靠性,通用性强的特点。在语音识别技术上实现了高可靠的唤醒识别率、更远距离的唤醒、更低误唤醒率、更强的抗噪音能力、更快的响应识别时间,免联网的纯离线识别。
VC 系列模组采用了 32bit RISC 架构内核,并加入了专门针对信号处理和语音识别所需要的 DSP 指令集,支持浮点运算的 FPU 运算单元,以及 FFT 加速器。支持最高 150 条本地指令离线识别,支持 RTOS 轻量级系统,以及简单友好的客制化工具。
VC 系列模组具有丰富的外围接口,包括 UART/I2C/PWM/SPI,以及简单友好的二次开发工具,方便客户实现单模组的语音控制应用场景方案。
VC-02 模组
![捕获.PNG](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZ8LPiaAibAdVoYZWtOtt3vYcYb72T6iangL1OnChkgZicDZicebuk1UajNkQ/640?wx_fmt=png&from=appmsg)
扬声器功率尽量不要太大,如果用 M61-32S 供电扬声器功率过高会导致掉电。
扬声器:
SPK-8Ω2W
SPK+8Ω2W
官方资料
VC-02 模组规格书:中文 EN
VC-02 原理图:点我下载
VC-02 封装: 点击下载
串口烧录工具
定制语音
安信可语音开放平台:http://voice.ai-thinker.com/
在安信可语音开放平台创建产品,选择 VC 系列模组。
![](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZSI2ETpdYOxM9PvpgBZNyX6AfdaWGI3sFnO5CpJqMic6f3CmBG50HSww/640?wx_fmt=png&from=appmsg)
然后修改唤醒词
![w.PNG](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZW7Uc2cpnlEiaAiaZw7vQNQCgIBKEdElxBsHaJbHkPDrBVDrPlQSyJrTw/640?wx_fmt=png&from=appmsg)
设置 Pin 引脚功能
![P.PNG](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZOOPuhy4C7H7knPF10sEwqOCre4dyoFm7ZNRIDghCel0ze2z2Tbre3g/640?wx_fmt=png&from=appmsg)
波特率设置 115200,引脚 B6 设置为 UART1_RX 引脚 B7 设置为 UART1_TX,用来发送接收数据。注意不要配置多个 UART1,否则不生效。
添加语音指令
![cmd.PNG](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZofadM9r0UDUT6CyTIcFcKkkkOH7N4tE8agRVDs7muicqaPKgOXjAhOQ/640?wx_fmt=png&from=appmsg)
然后添加控制详情
![cmd1.PNG](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZOZaV24XjBwvKYePEVWVZfTr51bRIt4ibOPMMJy6driawnT0gGSJeodzw/640?wx_fmt=png&from=appmsg)
这里主要用到的就是串口数据,这里发送指令 第一位是指令开头默认 A1,第二位定义的是设备位置,第三位定义的是设备类型,第四位定义的是开关状态,第五位结束位默认 FF。根据自己的需要设置解析即可。
还有免唤醒命令,不用唤醒词直接就可以用,比如开灯、关灯等等。
![mcmd.PNG](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZicsP6N9ENeePYCvrFGVKHpz3ASTZB6icZ8ZdPwEvZv4wgcGM276EXSzg/640?wx_fmt=png&from=appmsg)
免唤醒命令词最多 10 个选择自己需要的添加,添多了也是默认取前 10 条。
二、Ai-M61-32S Wi-Fi 模组
Ai-M61 系列模组(下称模组)是由深圳市安信可科技有限公司开发的 Wi-Fi6 & 蓝牙双模模组,搭载 BL618 芯片作为处理器,支持 Wi-Fi 802.11b/g/n/ax 协议和 BLE 5.3 协议。BL618 芯片内置低功耗的 32 位 RISC-V CPU,最高主频可达 320M. 丰富的外围接口,包括 DVP、MJPEG、Dispaly、Audio Codec、USB2.0、SDU、以太网(EMAC)、SD/MMC(SDH)、SPI、UART、I2C、I2S、PWM、GPDAC、GPADC、ACOMP 和 GPIO 等。可广泛应用于音视频多媒体、物联网(IoT)、移动设备、可穿戴电子设备、智能家居等领域。
M61-32S 模组
![](http://mmbiz.qpic.cn/sz_mmbiz_png/wDN2fZTUMRVUoXpmMUtcu3raleAvSsqZSVg8RMdeSRNLTW124241rtbicTzSwaJnibFcueGahGHXIonUnKSzIiaog/640?wx_fmt=png&from=appmsg)
官方资料
Ai-M61-32S-Kit 开发板规格书:中文 EN
Ai-M61-32S 开发板原理图:中文
Ai-M61-32S 模组规格书:中文 EN
Ai-M61-32S 推荐封装:点击下载
Ai-M61 系列出厂固件
固件下载工具:点击下载
固件烧录指南:M61/M62 系列烧录指导(包含模组&开发板)
三、代码部分
主要用 wifi、mqtt、uart、flash
uart 代码主要参考
【完全开源】智能桌面助手——AiPi-DSL_Dashboard
https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=42026&fromuid=15918
主程序代码
#include
#include
#include
#include
#include
#include
#include "log.h"
#include "board.h"
#include "mem.h"
#include "easyflash.h"
#include "bflb_mtd.h"
#include "bl_fw_api.h"
#include "wifi_mgmr_ext.h"
#include "wifi_mgmr.h"
#include "ha_task.h"
#include "wifi_event.h"
#include "voice_uart.h"
int main(void)
{
board_init();
wifi_start_firmware_task();
bflb_mtd_init();
easyflash_init();
xTaskCreate(voice_uart_task, "uart task", 1024, NULL, 10, NULL);
xTaskCreate(queue_receive_task, "queue_receive_task", 1024*3, NULL, 3, NULL);
vTaskStartScheduler();
}
VC02 模组交互代码
5#include
#include
#include
#include
#include
#include
#include
#include "ha_task.h"
#include "bflb_uart.h"
#include "bflb_gpio.h"
#include "voice_uart.h"
#include "user_mqtt.h"
#include "log.h"
#define GBD_TAG "UART"
static void uart_init(void);
extern xQueueHandle queue;
uart_rx_cmd_t uart_cmd;
static struct bflb_device_s* uart1;
static char uart_buff[5] = { 0 };
static const uart_data_t g_uart_buf[] = {
{{0xA1, 0x00, 0x01, 0x00, 0xFF
}, 5},
{{0xA1, 0x00, 0x01, 0x01, 0xFF}, 5},
{{0xA1, 0x00, 0x01, 0x00, 0xFF}, 5},
};
void voice_uart_task(void* arg)
{
uart_init();
while (1) {
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
static int voice_cmd_check(char* buff, int len)
{
int data_cnt = 0, j = 0;
while (1) {
for (size_t i = 0; i < 5; i++)
{
if (buff[i]==g_uart_buf[j].data[i]) data_cnt++;
else break;
}
if (data_cnt==g_uart_buf->len) break;
data_cnt = 0;
j++;
if (j>23) return -1;
}
LOG_I("check uart cmd=%d", j);
return j;
}
static void uart_isr(int irq, void* arg)
{
uint32_t intstatus = bflb_uart_get_intstatus(uart1);
uint32_t rx_data_len = 0;
char data_type[1] = { 0 };
if (intstatus & UART_INTSTS_RX_FIFO) {
int index = 0;
memset(uart_buff, 0, 5);
LOG_I("rx fifo\r\n");
while (bflb_uart_rxavailable(uart1)) {
uart_buff[index++] = bflb_uart_getchar(uart1);
}
LOG_I("uart recv:%02X %02X %02X %02X %02X", uart_buff[0], uart_buff[1], uart_buff[2], uart_buff[3], uart_buff[4]);
}
}
static void uart_init(void)
{
struct bflb_device_s* gpio;
struct bflb_uart_config_s cfg = {
.baudrate = 115200,
.data_bits = UART_DATA_BITS_8,
.stop_bits = UART_STOP_BITS_1,
.parity = UART_PARITY_NONE,
.flow_ctrl = 0,
.tx_fifo_threshold = 4,
.rx_fifo_threshold = 4,
};
gpio = bflb_device_get_by_name("gpio");
uart1 = bflb_device_get_by_name("uart1");
bflb_gpio_uart_init(gpio, GPIO_PIN_25, GPIO_UART_FUNC_UART1_TX);
bflb_gpio_uart_init(gpio, GPIO_PIN_26, GPIO_UART_FUNC_UART1_RX);
bflb_uart_init(uart1, &cfg);
bflb_uart_txint_mask(uart1, false);
bflb_uart_rxint_mask(uart1, false);
bflb_irq_attach(uart1->irq_num, uart_isr, NULL);
bflb_irq_enable(uart1->irq_num);
}
MQTT 与 WIFI
wifi
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include
#include
#include
#include "bl_fw_api.h"
#include "wifi_mgmr_ext.h"
#include "wifi_mgmr.h"
#include "bflb_irq.h"
#include "bflb_uart.h"
#include "bflb_l1c.h"
#include "bflb_mtimer.h"
#include "bl616_glb.h"
#include "rfparam_adapter.h"
#include "board.h"
#include "log.h"
#include "ha_task.h"
#include "config.h"
#define DBG_TAG "WIFI EVENT"
#define WIFI_STACK_SIZE (1024*4)
#define TASK_PRIORITY_FW (16)
static wifi_conf_t conf =
{
.country_code = "CN",
};
static TaskHandle_t wifi_fw_task;
static uint32_t sta_ConnectStatus = 0;
extern TaskHandle_t https_Handle;
int
wifi_start_firmware_task(void)
{
LOG_I("Starting wifi ...");
GLB_PER_Clock_UnGate(GLB_AHB_CLOCK_IP_WIFI_PHY | GLB_AHB_CLOCK_IP_WIFI_MAC_PHY | GLB_AHB_CLOCK_IP_WIFI_PLATFORM);
GLB_AHB_MCU_Software_Reset(GLB_AHB_MCU_SW_WIFI);
GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB);
if (0 != rfparam_init(0, NULL, 0)) {
LOG_I("PHY RF init failed!");
return 0;
}
LOG_I("PHY RF init success!");
extern void interrupt0_handler(void);
bflb_irq_attach(WIFI_IRQn, (irq_callback)interrupt0_handler, NULL);
bflb_irq_enable(WIFI_IRQn);
xTaskCreate(wifi_main, (char*)"fw", WIFI_STACK_SIZE, NULL, TASK_PRIORITY_FW, &wifi_fw_task);
return 0;
}
void wifi_event_handler(uint32_t code)
{
sta_ConnectStatus = code;
switch (code) {
case CODE_WIFI_ON_INIT_DONE:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE", __func__);
wifi_mgmr_init(&conf);
}
break;
case CODE_WIFI_ON_MGMR_DONE:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE", __func__);
}
break;
case CODE_WIFI_ON_SCAN_DONE:
{
char* scan_msg = pvPortMalloc(128);
wifi_mgmr_sta_scanlist();
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE SSID numbles:%d", __func__, wifi_mgmr_sta_scanlist_nums_get());
sprintf(scan_msg, "{\"wifi_scan\":{\"status\":0}}");
vPortFree(scan_msg);
}
break;
case CODE_WIFI_ON_CONNECTED:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED", __func__);
void mm_sec_keydump();
mm_sec_keydump();
}
break;
case CODE_WIFI_ON_GOT_IP:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP", __func__);
mqtt_client_init(MQTT_HOST, MQTT_PORT);
mqtt_client_register_event();
vTaskDelay(500/portTICK_PERIOD_MS);
mqtt_start_connect(MQTT_HOST, MQTT_PORT, MQTT_USERNAME, MQTT_PASSWORD);
mqtt_app_subscribe("sub", 0);
}
break;
case CODE_WIFI_ON_DISCONNECT:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT", __func__);
}
break;
case CODE_WIFI_ON_AP_STARTED:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED", __func__);
}
break;
case CODE_WIFI_ON_AP_STOPPED:
{
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED", __func__);
}
break;
case CODE_WIFI_ON_AP_STA_ADD:
{
LOG_I("[APP] [EVT] [AP] [ADD] %lld", xTaskGetTickCount());
}
break;
case CODE_WIFI_ON_AP_STA_DEL:
{
LOG_I("[APP] [EVT] [AP] [DEL] %lld", xTaskGetTickCount());
}
break;
default:
{
LOG_I("[APP] [EVT] Unknown code %u ", code);
}
}
}
uint8_t wifi_connect(char* ssid, char* passwd)
{
int ret = 255;
uint32_t ipv4_addr = 0;
char* queue_buff = pvPortMalloc(128);
memset(queue_buff, 0, 128);
if (NULL==ssid || 0==strlen(ssid)) {
return 1;
}
if (wifi_mgmr_sta_state_get() == 1) {
wifi_sta_disconnect();
}
LOG_I("WiFi STA connect .....");
if (wifi_sta_connect(ssid, passwd, NULL, NULL, 0, 0, 0, 1)<0) {
vPortFree(queue_buff);
return 4;
}
LOG_I("Wating wifi connet");
sta_ConnectStatus = 0;
for (int i = 0;i<10*30;i++) {
vTaskDelay(100/portTICK_PERIOD_MS);
switch (sta_ConnectStatus) {
case CODE_WIFI_ON_MGMR_DONE:
vPortFree(queue_buff);
return 3;
case CODE_WIFI_ON_SCAN_DONE:
vPortFree(queue_buff);
return 2;
case CODE_WIFI_ON_DISCONNECT:
return 4;
case CODE_WIFI_ON_CONNECTED:
break;
case CODE_WIFI_ON_GOT_IP:
wifi_sta_ip4_addr_get(&ipv4_addr, NULL, NULL, NULL);
LOG_I("wifi connened %s,IP:%s", ssid, inet_ntoa(ipv4_addr));
sprintf(queue_buff, "{\"ip\":{\"IP\":\"%s\"}}", inet_ntoa(ipv4_addr));
flash_erase_set(SSID_KEY, ssid);
flash_erase_set(PASS_KEY, passwd);
LOG_I("Wating wifi connet OK and get ip OK");
vPortFree(queue_buff);
return 0;
default:
break;
}
}
vPortFree(queue_buff);
return 14;
}
mqtt
#include
#include
#include
#include "FreeRTOS.h"
#include "log.h"
#include
#include "user_mqtt.h"
#include "ha_task.h"
#include
#include "bflb_uart.h"
#include
#include
#include
#include
#include "bflb_uart.h"
#include "voice_uart.h"
#include "utils_getopt.h"
#include "config.h"
#define DBG_TAG "MQTT"
int test_sockfd;
struct mqtt_client client;
static mqtt_event_t event;
uint8_t sendbuf[2048];
uint8_t recvbuf[1024];
static TaskHandle_t client_daemon;
static int open_socket(const char* host, const char* port);
static int open_socket(const char* host, const char* port)
{
struct addrinfo hints = { 0 };
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
int sockfd = -1;
int rv;
struct addrinfo* p, * servinfo;
LOG_I("host:%s, port=%s", host, port);
rv = getaddrinfo(host, port, &hints, &servinfo);
if (rv != 0) {
LOG_E("Failed to open socket (getaddrinfo): %d", rv);
return -1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd == -1) continue;
rv = connect(sockfd, p->ai_addr, p->ai_addrlen);
if (rv == -1) {
close(sockfd);
sockfd = -1;
continue;
}
break;
}
freeaddrinfo(servinfo);
if (sockfd != -1) {
int iMode = 1;
ioctlsocket(sockfd, FIONBIO, &iMode);
}
return sockfd;
}
static void test_close(void)
{
if (test_sockfd)
{
close(test_sockfd);
}
vTaskDelete(client_daemon);
}
static void publish_callback_1(void** unused, struct mqtt_response_publish* published)
{
char* topic_name = (char*)malloc(published->topic_name_size + 1);
memcpy(topic_name, published->topic_name, published->topic_name_size);
topic_name[published->topic_name_size] = '\0';
char* topic_msg = (char*)malloc(published->application_message_size + 1);
memcpy(topic_msg, published->application_message, published->application_message_size);
topic_msg[published->application_message_size] = '\0';
printf("%s:[%s]\r\n", topic_name, topic_msg);
char* queue_buff = pvPortMalloc(512);
memset(queue_buff, 0, 512);
sprintf(queue_buff, "{\"mqtt_msg\":{\"topic\":\"%s\",\"data\":%.*s}}", topic_name, published->application_message_size, topic_msg);
vPortFree(queue_buff);
free(topic_name);
free(topic_msg);
}
enum MQTTErrors _inspector_callback(struct mqtt_client* client_arg)
{
if (client_arg->mq.queue_tail->state!=MQTT_QUEUED_UNSENT) return MQTT_OK;
event = client_arg->mq.queue_tail->control_type;
static struct bflb_device_s* uartx;
uartx = bflb_device_get_by_name("uart1");
switch (event) {
case MQTT_EVENT_CONNECT:
{
LOG_I("MQTT_EVENT_CONNECT");
}
break;
case MQTT_EVENT_CONNACK:
{
LOG_I("MQTT_EVENT_CONNACK");
}
break;
case MQTT_EVENT_PUBLISH:
{
LOG_I("MQTT_EVENT_PUBLISH");
}
break;
case MQTT_EVENT_PUBACK:
{
LOG_I("MQTT_EVENT_PUBACK");
}
break;
case MQTT_EVENT_PUBREC:
{
LOG_I("MQTT_EVENT_PUBREC");
}
break;
case MQTT_EVENT_PUBREL:
{
LOG_I("MQTT_EVENT_PUBREL");
}
break;
case MQTT_EVENT_PUBCOMP:
{
LOG_I("MQTT_EVENT_PUBCOMP");
}
break;
case MQTT_EVENT_SUBSCRIBE:
{
LOG_I("MQTT_EVENT_SUBSCRIBE");
}
break;
case MQTT_EVENT_SUBACK:
{
LOG_I("MQTT_EVENT_SUBACK");
}
break;
case MQTT_EVENT_UNSUBSCRIBE:
{
LOG_I("MQTT_EVENT_UNSUBSCRIBE");
}
break;
case MQTT_EVENT_UNSUBACK:
{
LOG_I("MQTT_EVENT_UNSUBACK");
}
break;
case MQTT_EVENT_PINGREQ:
{
LOG_I("MQTT_EVENT_PINGREQ");
}
break;
case MQTT_EVENT_PINGRESP:
{
LOG_I("MQTT_EVENT_PINGRESP");
}
break;
case MQTT_EVENT_DISCONNECT:
{
LOG_I("MQTT_EVENT_DISCONNECT");
}
break