[i=s] 本帖最后由 云烟 于 2026-1-14 17:07 编辑 [/i]
基于Ai-WB2的HomeAssistant实现RGB彩灯控制功能
一、准备工作
1.1 硬件清单
| 硬件名称 |
数量 |
备注 |
| Ai-WB2-12F |
1 |
核心控制MCU |
| typec数据线 |
1 |
烧录程序调试 |
1.2 软件清单
| 软件/资源 |
获取地址 |
| HomeAssistant Ai-WB2 智能家居应用仓库 |
Gitee |
| HomeAssistant_MQTT-C |
Gitee |
前置要求
- 熟悉PWM;
- 对Linux、Git指令有一定的了解;
- 对回调函数、JSON、MOTT协议有一定了解。
二、克隆完整的HaDevice仓库
- 建议直接通过 Git 克隆源码仓库,也可点击链接跳转直接下载
ZIP包:
#Gitee
git clone --recursive https://gitee.com/seahi007/HaDevice.git
提示:该仓库包含子模块内容较大,需要耐心等待克隆完成
三、RGB工程创建与配置
- 打开vscode连接WSL,打开刚下载好的仓库,在
HaDevice➡project下找到 USB_Ligth_ctn 文件,将其复制粘贴一份,并更名为 RGB ,删除一些我们不需要的文件,保留以下文件,如图:
- 在
components文件夹下创建 rgb文件夹,在此文件中创建我们的 rgb.c和 rgb.h驱动文件,并在 bouffalo.mk文件中添加末尾添加 rgb用于编译时连接,如图所示:
- 在
rgb文件中添加我们的RGB底层驱动代码,代码如下:
//RGB.h
#ifndef __RGB_H__
#define __RGB_H__
#include <bl602.h>
#include <bl602_gpio.h>
#include <bl_pwm.h>
//配置PWM参数
BL_Err_Type PWM_Smart_Configure2(PWM_CH_ID_Type ch, uint16_t clkDiv, uint16_t period,uint16_t threshold2);
//初始化PWM
void RGB_init(void);
//设置3个RGB LED
void RGB_set(uint8_t red,uint8_t green,uint8_t blue);
#endif
//RGB.c
#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include <bl602.h>
#include <bl602_gpio.h>
#include <bl602_glb.h>
#include <bl_pwm.h>
#include "rgb.h"
/* -------------------------------------------------------------------------
宏定义
------------------------------------------------------------------------- */
/* 根据通道号计算对应寄存器块起始地址
* BL602 每通道占用 0x20 字节,基地址 + 偏移 + ch*0x20 */
#define PWM_Get_Channel_Reg(ch) (PWM_BASE + PWM_CHANNEL_OFFSET + (ch) * 0x20)
/* 等待 PWM 停止完成时的超时次数;主频 160 MHz 下约 1 ms */
#define PWM_STOP_TIMEOUT_COUNT (160 * 1000)
/* -------------------------------------------------------------------------
GPIO 配置表
* 数组索引 0/1/2 分别对应 R/G/B,顺序与 RGB_set() 保持一致
* drive=0 : 驱动能力最低(减小 EMI)
* smtCtrl=1 : 使能施密特(抗干扰)
* pullType : 下拉,防止未初始化时 LED 微亮
* gpioFun=8 : 功能 8 即为 PWM
------------------------------------------------------------------------- */
GLB_GPIO_Cfg_Type cfg[3] = {
{ .drive = 0, .smtCtrl = 1, .gpioMode = GPIO_MODE_OUTPUT,
.pullType = GPIO_PULL_DOWN, .gpioPin = 14, .gpioFun = 8 }, /* Red */
{ .drive = 0, .smtCtrl = 1, .gpioMode = GPIO_MODE_OUTPUT,
.pullType = GPIO_PULL_DOWN, .gpioPin = 17, .gpioFun = 8 }, /* Green */
{ .drive = 0, .smtCtrl = 1, .gpioMode = GPIO_MODE_OUTPUT,
.pullType = GPIO_PULL_DOWN, .gpioPin = 3, .gpioFun = 8 }, /* Blue */
};
/* =========================================================================
本地函数
========================================================================= */
/**
* @brief 自定义 PWM 配置函数(基于官方 PWM_Smart_Configure 修改)
* @note 与官方版本差异:
* 1) 时钟源固定为 BUS Clock(PLL 输出),而非外部 32 k/40 M 晶振;
* 2) 占空比只使用 threshold2(THRE2),THRE1 固定写 0;
* 3) 输出极性为正相(PWM_POL_NORMAL),优雅停模式。
*
* @param ch : PWM 通道号(0~5)
* @param clkDiv : 时钟分频,实际 PWM 频率 = BUS_CLK / (clkDiv+1) / (period+1)
* @param period : PWM 周期(计数器最大值),推荐 255 方便 8 bit 占空比
* @param threshold2: 高电平结束点(占空比 = (threshold2+1) / (period+1) )
* @return : SUCCESS 或 TIMEOUT(停止等待超时)
*/
BL_Err_Type PWM_Smart_Configure2(PWM_CH_ID_Type ch,
uint16_t clkDiv,
uint16_t period,
uint16_t threshold2)
{
uint32_t tmpVal;
uint32_t timeoutCnt = PWM_STOP_TIMEOUT_COUNT;
/* 1. 拿到通道寄存器基地址 */
uint32_t PWMx = PWM_Get_Channel_Reg(ch);
/* 2. 如果当前时钟源不是 BUS Clock,则先切换 */
tmpVal = BL_RD_REG(PWMx, PWM_CONFIG);
if (BL_GET_REG_BITS_VAL(tmpVal, PWM_REG_CLK_SEL) != PWM_CLK_BCLK) {
/* 2.1 置位 STOP_EN,请求停止 */
BL_WR_REG(PWMx, PWM_CONFIG, BL_SET_REG_BIT(tmpVal, PWM_STOP_EN));
/* 2.2 等待硬件完成停止(STS_TOP 置位) */
while (!BL_IS_REG_BIT_SET(BL_RD_REG(PWMx, PWM_CONFIG), PWM_STS_TOP)) {
if (--timeoutCnt == 0) {
return TIMEOUT; /* 超时退出,防止死等 */
}
}
/* 2.3 切换时钟源为 BUS Clock */
tmpVal = BL_SET_REG_BITS_VAL(tmpVal, PWM_REG_CLK_SEL, PWM_CLK_BCLK);
}
/* 3. 配置输出极性与停止模式 */
tmpVal = BL_SET_REG_BITS_VAL(tmpVal, PWM_OUT_INV, PWM_POL_NORMAL);
tmpVal = BL_SET_REG_BITS_VAL(tmpVal, PWM_STOP_MODE, PWM_STOP_GRACEFUL);
BL_WR_REG(PWMx, PWM_CONFIG, tmpVal);
/* 4. 写分频、周期、占空比 */
BL_WR_REG(PWMx, PWM_CLKDIV, clkDiv); /* 分频 */
BL_WR_REG(PWMx, PWM_PERIOD, period); /* 周期 */
BL_WR_REG(PWMx, PWM_THRE1, 0); /* 通道 1 比较点(未用)*/
BL_WR_REG(PWMx, PWM_THRE2, threshold2); /* 通道 2 比较点(决定占空比)*/
return SUCCESS;
}
/* =========================================================================
用户 API
========================================================================= */
/**
* @brief 初始化 RGB 三路 GPIO 与 PWM 通道
* @note 1. 只初始化 GPIO,不立即开灯;
* 2. 先禁用 PWM 通道,防止旧配置产生毛刺。
*/
void RGB_init(void)
{
for (int i = 0; i < 3; i++) {
/* 初始化 GPIO 复用、上下拉、驱动能力 */
GLB_GPIO_Init(cfg + i);
/* 计算对应 PWM 通道号(硬件允许 0~5)*/
PWM_CH_ID_Type ch = cfg[i].gpioPin % PWM_CH_MAX;
/* 关闭该通道,确保输出低电平 */
PWM_Channel_Disable(ch);
}
}
/**
* @brief 同时设置 R/G/B 三路占空比
* @param red : 0~255
* @param green : 0~255
* @param blue : 0~255
* @note 固定使用 PWM 通道 2/3/4,与 cfg[] 数组顺序无关;
* 频率 = 1 MHz / 256 ≈ 3.9 kHz,肉眼无频闪。
*/
void RGB_set(uint8_t red, uint8_t green, uint8_t blue)
{
/* Green -> 通道 2 */
PWM_Smart_Configure2(2, 80, 255, green);
PWM_Channel_Enable(2);
/* Blue -> 通道 3 */
PWM_Smart_Configure2(3, 80, 255, blue);
PWM_Channel_Enable(3);
/* Red -> 通道 4 */
PWM_Smart_Configure2(4, 80, 255, red);
PWM_Channel_Enable(4);
}
四、实体参数配置
- 开启CONFIG_ENTITY_ENABLE_LIGHT资源,需要通过以下方式开启Bianry sensor资源:
- 打开
HomeAssistant-C➡homeAssistantDevConfig.h
- 设置
CONFIG_ENTITY_ENABLE_LIGHT为 1:
- 打开
HomeAssistant/HomeAssistant-C/homeAssistantPort.h 注释掉 #define CONFIG_Ai_M6x 并使用 #define CONFIG_Ai_WB2如图:
- 创建实体信息,需要把light 的信息填写完整结构体:
ha_lh_entity_t,在 homeAssistant➡dev_ha.c中第37行中添加以下内容:
static ha_lh_entity_t light = {
.name = "云烟的RGB彩灯",/* 实体显示名称,在HomeAssistant界面中显示 */
.unique_id = "light_1",/* 实体唯一标识符,用于在MQTT主题中区分设备 */
/* RGB灯光相关配置 */
.rgb = {
.rgb_command_topic = "light_1/rgb/set",/* MQTT命令主题:用于接收来自HomeAssistant的颜色设置命令 */
.rgb_state_topic = "light_1/rgb/state", /* MQTT状态主题:用于向HomeAssistant发送当前灯光状态 */
.red = 0,/* RGB颜色初始值:红色分量 (0-255) */
.green = 0, /* RGB颜色初始值:绿色分量 (0-255) */
.blue = 0, /* RGB颜色初始值:蓝色分量 (0-255) */
}
};
homeAssistant_device_add_entity(CONFIG_HA_ENTITY_LIGHT, &light);//向HomeAssistant注册灯光实体
homeAssistant_device_send_entity_state(CONFIG_HA_ENTITY_SWITCH, &sw1, 0);//发送开关实体状态到HomeAssistant,当前为关
- 配置接受控制指令;Light实体可以接收开关指令,颜色指令、亮度指令等。当设备收到指令后,会触发
HA_EVENT_MQTT_COMMAND_LIGHT_SWITCH和 HA_EVENT_MQTT_COMMAND_LIGHT_RGB_UPDATE,事件相关描述请参考 HomeAssistant MQTT 控制事件 ,添加以下代码:
/**
* @brief RGB灯光开关事件处理
* 处理来自HomeAssistant的RGB灯光开关命令
* 根据开关状态控制RGB灯的实际亮灭,并处理颜色逻辑
*/
case HA_EVENT_MQTT_COMMAND_LIGHT_SWITCH:
/**
* @brief 记录开关状态变化日志
* 通过HA_LOG_I宏串口输出记录开关状态变化,便于调试和监控
* 使用三元运算符将布尔状态转换为"ON"/"OFF"字符串
*/
HA_LOG_I("<<<<<<<<<< HA_EVENT_MQTT_COMMAND_LIGHT_SWITCH=%s\r\n",
dev->entity_light->command_light->light_state ? "ON" : "OFF");
/**
* @brief 向HomeAssistant发送状态确认
* 将当前开关状态发送回HomeAssistant,确保状态同步
* CONFIG_HA_ENTITY_LIGHT: 实体类型为灯光
* &light: 指向灯光实体结构体的指针
* dev->entity_light->command_light->light_state: 当前开关状态
*/
homeAssistant_device_send_entity_state(CONFIG_HA_ENTITY_LIGHT, &light,
dev->entity_light->command_light->light_state);
/**
* @brief 根据开关状态控制RGB灯
* 处理开关状态变化,控制RGB灯的实际亮灭
*/
if (!(dev->entity_light->command_light->light_state)) {
/**
* @brief 关闭状态:熄灭RGB灯
* 当开关状态为关闭时,将所有RGB通道设置为0
* RGB_set(0, 0, 0): 红色=0, 绿色=0, 蓝色=0 (完全熄灭)
*/
RGB_set(0, 0, 0);
} else {
/**
* @brief 开启状态:智能颜色处理
* 当开关状态为开启时,检查当前颜色设置
* 如果所有颜色通道都为0,提供默认亮度
* 否则使用当前设置的颜色值
*/
if (dev->entity_light->command_light->rgb.red == 0 &&
dev->entity_light->command_light->rgb.green == 0 &&
dev->entity_light->command_light->rgb.blue == 0) {
RGB_set(90, 90, 90);
} else {
/**
* @brief 使用当前颜色设置
* 如果已有颜色设置,直接使用当前RGB值
* 保持用户之前设置的颜色偏好
*/
RGB_set(dev->entity_light->command_light->rgb.red,
dev->entity_light->command_light->rgb.green,
dev->entity_light->command_light->rgb.blue);
}
}
break;
/**
* @brief RGB颜色更新事件处理
* 处理来自HomeAssistant的RGB颜色变化命令
* 直接应用新的颜色值到RGB灯
*/
//RGB颜色事件
case HA_EVENT_MQTT_COMMAND_LIGHT_RGB_UPDATE:
/**
* @brief 记录颜色变化日志
*
* 通过HA_LOG_I宏记录RGB颜色值变化
* 格式:R,G,B 分别显示红、绿、蓝三个通道的数值
* 便于调试和追踪颜色变化历史
*/
HA_LOG_I("<<<<<<<<<< HA_EVENT_MQTT_COMMAND_LIGHT_RGB_UPDATE=%d,%d,%d\r\n",
dev->entity_light->command_light->rgb.red,
dev->entity_light->command_light->rgb.green,
dev->entity_light->command_light->rgb.blue);
/**
* @brief 直接应用新的RGB颜色
*
* 将HomeAssistant发送的RGB值直接应用到RGB灯
* 不检查开关状态,假设灯光已开启
* RGB_set(): 同时设置红、绿、蓝三个通道的PWM占空比
*/
RGB_set(dev->entity_light->command_light->rgb.red,
dev->entity_light->command_light->rgb.green,
dev->entity_light->command_light->rgb.blue);
/**
* @brief 向HomeAssistant确认状态更新
* 将更新后的状态发送回HomeAssistant
* 确保HomeAssistant界面显示的状态与实际灯光状态一致
*/
homeAssistant_device_send_entity_state(CONFIG_HA_ENTITY_LIGHT, &light,
dev->entity_light->command_light->light_state);
5.找到 mian添加如下的初始化代码:
// 初始化EasyFlash闪存系统
// easyflash_init();
// 初始化RGB 控制
blog_info("[RGB] Initializing RGB LED control...");
RGB_init();
RGB_set(255, 255, 255);
vTaskDelay(pdMS_TO_TICKS(1000));
RGB_set(0, 0, 255);
vTaskDelay(pdMS_TO_TICKS(1000));
RGB_set(0, 255, 0);
vTaskDelay(pdMS_TO_TICKS(1000));
RGB_set(255, 0, 0);
vTaskDelay(pdMS_TO_TICKS(1000));
RGB_set(0, 0, 0);
// 初始化设备状态管理
// device_state_init(NULL);
- 编译烧录,输入以下指令
//编译
make
//烧录
make flash -j32
//或指定USB口
make flash p=/dev/ttyUSB0 -j32
五、配网
- 重复按复位键5次(每次间隔1S左右)
- GPIO4的配置灯闪烁就是配网状态
- 打开微信小程序搜索 <安信可IOT>
- 进入Blufi配网 界面点击 扫描设备
- 找到一个名称:"USB-light-xxxx"的设备
- 点击连接之后,输入WiFi名称密码
- 点击更多信息,并选择MQTT服务器(只需要配置一次)
- 输入 MQTT服务器地址和端口号(只需要配置一次)
- 输入完成点击 配置WiFi
- 等待GPIO4的配置灯熄灭即可配网成功
六、使用
- 打开HomeAssistant 的MQTT 集成,就能看到USB 小夜灯控制器设备。