一、开发前准备
本次软件开发主要是基于涂鸦CBU模组,需要先授权再接入涂鸦IoT云平台,刚好涂鸦的最近的活动比较多,可以参加涂鸦的活动获(bai)取(piao)模组。
涂鸦的SDK编译一般是在Linux下进行编译开发的,建议先安装虚拟机,windows也可以的,但是编译速度有点慢。
下面是开发中会使用到的平台和资料库:
涂鸦IoT云平台
CBU开发SDK环境
本项目代码GitHub地址
CBU模组规格书
二、功能规划
序号 | 功能 |
---|---|
1 | 三种模式风(正常风,自然风:忽大忽小间隔15秒,睡眠风:每隔一小时自动降档,最后降到最低档) |
2 | 编码器旋转控制风扇,顺时针旋转风速+,逆时针旋转风速- 。 |
3 | 编码器按钮短按切换模式,长按设备复位。 |
4 | 4颗指示灯显示风速,4颗指示灯指示8档风速,闪烁代表1档,常量代表2档。4颗指示灯还复用本地定时指示。 |
5 | 本地定时功能,时间到自动关机。定时按键:无定时->1小时->2小时->3小时->4小时->无定时。 |
6 | 电源按键,风扇工作状态按下后关闭风扇,风扇处于关闭状态按下后打开风扇。 |
7 | 长按 WiFi 按键,设备进入配网模式。 |
8 | LED有8颗,除指示风速4颗LED外,还有4颗指示灯。一颗 WiFi 指示灯,指示 WiFi 状态;其他三颗指示当前风扇模式 |
9 | led指示灯亮度可通过APP调整,正常亮度,较暗亮度。 |
10 | 断电记忆 |
三、产品创建
涂鸦IoT平台 ,创建产品->标准类目->小家电->风扇->风扇->填入产品信息->选择DP点->选择面板->选择涂鸦标准模组SDK开发,选择对应的模组。
代码编译完成后将生成的固件上传,具体信息填入,参考该链接下文档 。上传完固件后,后期也可以更新固件进行OTA,这个功能用起来也是十分方便的。
四、烧录、授权
前面说过了如果想上涂鸦云是模组是需要授权的,那么如何烧录授权呢?这是关于WB系列模组烧录的介绍,这里在开发过程中强烈推荐使用烧录授权分立方案,即烧录UA(应用区)固件。
QIO、UA、UG文件是什么意思上面的官方文档里已经有了详细的介绍,我这里就不再复述了。
芯片烧录工具下载地址
五、功能开发
1、配网及配网指示灯显示
调用该函数tuya_iot_wf_gw_unactive()
以进入配网模式,涂鸦 SDK 对于网络状态的定义有以下几种:
typedef BYTE_T GW_WIFI_NW_STAT_E;
#define STAT_LOW_POWER 0 // idle status,use to external config network
#define STAT_UNPROVISION 1 // smart config status
#define STAT_AP_STA_UNCFG 2 // ap WIFI config status
#define STAT_AP_STA_DISC 3 // ap WIFI already config,station disconnect
#define STAT_AP_STA_CONN 4 // ap station mode,station connect
#define STAT_STA_DISC 5 // only station mode,disconnect
#define STAT_STA_CONN 6 // station mode connect
#define STAT_CLOUD_CONN 7 // cloud connect
#define STAT_AP_CLOUD_CONN 8 // cloud connect and ap start
#define STAT_REG_FAIL 9 // register fail
#define STAT_OFFLINE 10 // offline
#define STAT_MQTT_ONLINE 11
#define STAT_MQTT_OFFLINE 12
#define STAT_UNPROVISION_AP_STA_UNCFG 13 //smart-cfg and ap-cfg concurrent config status
长按进入配网模式功能实现:
STATIC VOID wifi_key_process(TY_GPIO_PORT_E port,PUSH_KEY_TYPE_E type,INT_T cnt)
{
PR_DEBUG("port:%d,type:%d,cnt:%d",port,type,cnt);
OPERATE_RET op_ret = OPRT_OK;
UCHAR_T ucConnectMode = 0;
if (port = WIFI_KEY_PIN) {
if (LONG_KEY == type) { //press long enter linking network
PR_NOTICE("key long press");
/* 手动移除设备 */
tuya_iot_wf_gw_unactive();
} else if (NORMAL_KEY == type) {
PR_NOTICE("key normal press");
} else {
PR_NOTICE("key type is no deal");
}
}
return;
}
STATIC VOID wifi_config_init(VOID)
{
OPERATE_RET op_ret = OPRT_OK;
/* LED 相关初始化 */
tuya_gpio_inout_set(WIFI_LED_PIN, FALSE);
tuya_set_led_light_type(wifi_led_handle, OL_HIGH, 0, 0); //关闭 led
/* LED 相关初始化 */
op_ret = tuya_create_led_handle(WIFI_LED_PIN, TRUE, &wifi_led_handle);
if (op_ret != OPRT_OK) {
PR_ERR("key_init err:%d", op_ret);
return;
}
tuya_set_led_light_type(wifi_led_handle, OL_HIGH, 0, 0);
/* 按键相关初始化 */
KEY_USER_DEF_S key_def;
op_ret = key_init(NULL, 0, WIFI_KEY_TIMER_MS);
if (op_ret != OPRT_OK) {
PR_ERR("key_init err:%d", op_ret);
return;
}
/* 初始化 key 相关参数 */
memset(&key_def, 0, SIZEOF(key_def));
key_def.port = WIFI_KEY_PIN; //按键引脚
key_def.long_key_time = WIFI_KEY_LONG_PRESS_MS; //长按时间配置
key_def.low_level_detect = WIFI_KEY_LOW_LEVEL_ENABLE; //TRUE:低电平算按下,FALSE:高电平算按下
key_def.lp_tp = LP_ONCE_TRIG; //
key_def.call_back = wifi_key_process; //按键按下后回调函数
key_def.seq_key_detect_time = WIFI_KEY_SEQ_PRESS_MS; //连按间隔时间配置
/* 注册按键 */
op_ret = reg_proc_key(&key_def);
if (op_ret != OPRT_OK) {
PR_ERR("reg_proc_key err:%d", op_ret);
}
return;
}
这里对于wifi 状态的做出的提示如下:
STATIC VOID wifi_state_led_reminder(IN CONST GW_WIFI_NW_STAT_E cur_stat)
{
switch (cur_stat)
{
case STAT_LOW_POWER: //wifi 连接超时,进入低功耗模式
tuya_set_led_light_type(wifi_led_handle, OL_HIGH, 0, 0); //关闭提示灯
break;
case STAT_UNPROVISION: //SamrtConfig 配网模式,等待连接
tuya_set_led_light_type(wifi_led_handle, OL_FLASH_HIGH, WIFI_LED_FAST_FLASH_MS, 0xffff); //led 快闪
break;
case STAT_AP_STA_UNCFG: //ap 配网模式,等待连接
tuya_set_led_light_type(wifi_led_handle, OL_FLASH_HIGH, WIFI_LED_LOW_FLASH_MS, 0xffff); //led 慢闪
break;
case STAT_AP_STA_DISC:
case STAT_STA_DISC: //SamrtConfig/ap 正在连接中
tuya_set_led_light_type(wifi_led_handle, OL_HIGH, 0, 0); //关闭 led
break;
case STAT_CLOUD_CONN:
case STAT_AP_CLOUD_CONN: //连接到涂鸦云
tuya_set_led_light_type(wifi_led_handle, OL_LOW, 0, 0); //led 常量
break;
default:
break;
}
}
2、风扇模式功能开发:
由于是通过 PWM 对 BLDC 进行控制,所以风扇的控制函数如下:
VOID_T fan_speed_set(UINT_T speed)
{
UINT_T fan_speed_pwm_duty_cycle = 0;
if (speed <= 0) {
vSocPwmSetDuty(BLDC_PWM_ID, (BLDC_PWM_FAN_OFF));
return;
}
//由于电机在30%以下工作时间过长会出现异常,这里对 PWM 输出进行一些处理,使输出的 PWM 在 30%-99% 之间
fan_speed_pwm_duty_cycle = (UINT_T)(BLDC_PWM_FAN_MIN + ((BLDC_PWM_FAN_MAX - BLDC_PWM_FAN_MIN) * (speed / 100.0)));
vSocPwmSetDuty(BLDC_PWM_ID, (fan_speed_pwm_duty_cycle));
return;
}
普通模式:
static VOID_T fan_mode_normal(VOID_T)
{
INT_T opRet = LIGHT_OK;
//关闭睡眠模式的定时器,防止干扰普通模式的运行
opRet = opSocSWTimerStop(SLEEP_MODE_TIMER);
if (opRet != LIGHT_OK) {
PR_ERR("stop sleep timer error");
}
//关闭自然模式的定时器,防止干扰普通模式的运行
opRet = opSocSWTimerStop(NATURAL_MODE_TIMER);
if (opRet != LIGHT_OK) {
PR_ERR("stop natural timer error");
}
fan_speed_set(fan_state.speed);
PR_NOTICE("+++ normal mode fan_state.speed : %d", fan_state.speed);
}
自然风模式:
static VOID_T fan_mode_natural_timer_cb(VOID_T)
{
//如果关机,不执行任何操作
if (fan_state.on_off == FALSE) {
opSocSWTimerStop(NATURAL_MODE_TIMER);
return;
}
if (natural_speed_low_flag) {
PR_NOTICE("natural mode low speed");
fan_speed_set(1);
} else {
PR_NOTICE("natural mode high speed");
fan_speed_set(fan_state.speed);
}
natural_speed_low_flag = ~(natural_speed_low_flag);
opSocSWTimerStart(NATURAL_MODE_TIMER, NATURAL_SPEED_CHANGE_TIME * 1000, fan_mode_natural_timer_cb);
}
static VOID_T fan_mode_natural(VOID_T)
{
INT_T opRet = LIGHT_OK;
//关闭睡眠模式的定时器,防止干扰自然模式的运行
opRet = opSocSWTimerStop(SLEEP_MODE_TIMER);
if (opRet != LIGHT_OK) {
PR_ERR("stop sleep timer error");
}
natural_speed_low_flag = ~(0x00);
fan_speed_set(fan_state.speed);
opSocSWTimerStart(NATURAL_MODE_TIMER, NATURAL_SPEED_CHANGE_TIME * 1000, fan_mode_natural_timer_cb);
}
睡眠风模式:
static VOID_T fan_sleep_mode_task(VOID_T)
{
UINT8_T cur_gear;
PR_NOTICE("enter fan_sleep_mode_task!");
//判断当前是不是最低档。若为最低档,不再降速
if (fan_state.speed <= g_fan_speed_gear[0]) {
fan_speed_set(g_fan_speed_gear[0]);
change_fan_state();
opSocSWTimerStop(SLEEP_MODE_TIMER);
return;
}
cur_gear = get_cur_gear();
PR_NOTICE("current gear is %d.", cur_gear);
fan_state.speed = g_fan_speed_gear[--cur_gear];
//改变档位转速
fan_speed_set(fan_state.speed);
fan_speed_led_set(get_cur_gear()+1);
PR_NOTICE("speed change to %d.", fan_state.speed);
//写入风扇状态到falsh中
write_flash_fan_state();
//启动睡眠模式,1h 减一档
opSocSWTimerStart(SLEEP_MODE_TIMER, SLEEP_SPEED_CHANGE_TIME * 1000, fan_sleep_mode_task);
}
static VOID_T fan_mode_sleep(VOID_T)
{
UINT8_T cur_gear;
INT_T opRet = LIGHT_OK;
SHORT_T i;
//关闭自然模式的定时器,防止干扰睡眠模式模式的运行
opRet = opSocSWTimerStop(NATURAL_MODE_TIMER);
if (opRet != LIGHT_OK) {
PR_ERR("stop sleep timer error");
}
opRet = opSocSWTimerStop(SLEEP_MODE_TIMER);
if (opRet != LIGHT_OK) {
PR_ERR("stop sleep timer error");
}
//判断当前档位
cur_gear = get_cur_gear();
fan_state.speed = g_fan_speed_gear[cur_gear];
//改变档位转速
fan_speed_set(fan_state.speed);
PR_NOTICE("speed change to %d.", fan_state.speed);
//写入风扇状态到falsh中
write_flash_fan_state();
opSocSWTimerStart(SLEEP_MODE_TIMER, SLEEP_SPEED_CHANGE_TIME * 1000, fan_sleep_mode_task);
}
3、编码器及其他按键功能开发
按键初始化:
VOID_T fan_key_init(VOID_T)
{
OPERATE_RET opRet;
tuya_gpio_inout_set(KEY_ROTARY_A, TRUE);
tuya_gpio_inout_set(KEY_ROTARY_B, TRUE);
/* 旋钮正反转检测初始化 */
BkGpioEnableIRQ(KEY_ROTARY_A, IRQ_TRIGGER_FALLING_EDGE, knod_key_cb, NULL);
opRet = key_init(NULL, 0, 0);
if (opRet != OPRT_OK) {
PR_ERR("key_init err:%d", opRet);
return;
}
memset(&KEY_DEF_T, 0, SIZEOF(KEY_DEF_T));
KEY_DEF_T.port = KEY_ROTARY_N;
KEY_DEF_T.long_key_time = 3000;
KEY_DEF_T.low_level_detect = TRUE;
KEY_DEF_T.lp_tp = LP_ONCE_TRIG;
KEY_DEF_T.call_back = key_press_cb;
KEY_DEF_T.seq_key_detect_time = 400;
opRet = reg_proc_key(&KEY_DEF_T);
if (opRet != OPRT_OK) {
PR_ERR("reg_proc_key err:%d", opRet);
return;
}
KEY_DEF_T.port = KEY_TIMER;
opRet = reg_proc_key(&KEY_DEF_T);
if (opRet != OPRT_OK) {
PR_ERR("reg_proc_key err:%d", opRet);
return;
}
KEY_DEF_T.port = KEY_POWER;
KEY_DEF_T.long_key_time = 10000;
opRet = reg_proc_key(&KEY_DEF_T);
if (opRet != OPRT_OK) {
PR_ERR("reg_proc_key err:%d", opRet);
return;
}
}
按键功能回调函数:
编码器回调函数,编码器功能的功能实现,简单的使用的外部中断触发后,开始判断A,B两个引脚电平是否相同来确认是顺时针旋转还是逆时针旋转。
STATIC VOID_T knod_key_cb(VOID_T)
{
INT8_T current_gear;
//如果关机,不执行任何操作
if (fan_state.on_off == FALSE) {
return;
}
BkGpioFinalize(KEY_ROTARY_A);
//得到当前档位
current_gear = get_cur_gear();
if(tuya_gpio_read(KEY_ROTARY_A) != tuya_gpio_read(KEY_ROTARY_B)) {
PR_DEBUG("A != B"); //顺时针方向
current_gear++;
if (current_gear > (MAX_GEAR_NUMBER-1)) {
current_gear = (MAX_GEAR_NUMBER-1);
}
fan_state.speed = g_fan_speed_gear[current_gear];
} else {
PR_DEBUG("A == B"); //逆时针方向
current_gear--;
if (current_gear < 0) {
current_gear = 0;
}
fan_state.speed = g_fan_speed_gear[current_gear];
}
/* 改变风扇状态:风速,模式,LED */
change_fan_state();
write_flash_fan_state();
PR_DEBUG("fan current_gear is : %d", current_gear);
/* 旋钮正反转检测初始化 */
BkGpioEnableIRQ(KEY_ROTARY_A, IRQ_TRIGGER_FALLING_EDGE, knod_key_cb, NULL);
}
编码器使用上面外部中断的方式后发现偷懒不成,编码器转的快,触发太快容易导致程序卡死,软件重启。于是改为了下面使用线程检测的偷懒方式。
void key_rotary_task(void)
{
INT8_T current_gear;
while(1) {
//得到当前档位
current_gear = get_cur_gear();
if((tuya_gpio_read(KEY_ROTARY_A) == FALSE) && (fan_state.on_off != FALSE)) {
while(tuya_gpio_read(KEY_ROTARY_A) == FALSE);
if(tuya_gpio_read(KEY_ROTARY_A) != tuya_gpio_read(KEY_ROTARY_B)) {
PR_NOTICE("A != B"); //顺时针方向
current_gear++;
if (current_gear > (MAX_GEAR_NUMBER-1)) {
current_gear = (MAX_GEAR_NUMBER-1);
}
fan_state.speed = g_fan_speed_gear[current_gear];
} else {
PR_NOTICE("A == B"); //逆时针方向
current_gear--;
if (current_gear < 0) {
current_gear = 0;
}
fan_state.speed = g_fan_speed_gear[current_gear];
}
/* 改变风扇状态:风速,模式,LED */
change_fan_state();
write_flash_fan_state();
PR_NOTICE("fan current_gear is : %d", current_gear);
}
tuya_hal_system_sleep(50);
}
}
VOID_T fan_key_init(VOID_T)
{
OPERATE_RET opRet;
tuya_gpio_inout_set(KEY_ROTARY_A, TRUE);
tuya_gpio_inout_set(KEY_ROTARY_B, TRUE);
...
tuya_hal_thread_create(NULL, "key_rotary_task", 512*4, TRD_PRIO_5, key_rotary_task, NULL);
}
其他普通按键回调函数:
STATIC VOID_T key_press_cb(TY_GPIO_PORT_E port,PUSH_KEY_TYPE_E type,INT_T cnt)
{
PR_DEBUG("port: %d, type: %d, cnt: %d", port, type, cnt);
/* 旋钮按键 */
if (port == KEY_ROTARY_N) {
if (fan_state.on_off == FALSE) {
return;
}
switch (type) {
case NORMAL_KEY:
PR_DEBUG("knod press.");
if (fan_state.mode == NORMAL_MODE) {
fan_state.mode = NATURAL_MODE;
} else if (fan_state.mode == NATURAL_MODE) {
fan_state.mode =SLEEP_MODE;
} else {
fan_state.mode = NORMAL_MODE;
}
change_fan_state();
break;
case LONG_KEY:
PR_DEBUG("knod long press.");
/* 复位,删除所有用户信息,恢复到默认模式 */
fan_state = fan_default_state;
change_fan_state();
write_flash_fan_state();
break;
case SEQ_KEY:
PR_DEBUG("knod SEQ press, the count is %d.", cnt);
break;
default:
break;
}
}
/* 定时按键 */
if (port == KEY_TIMER) {
if (fan_state.on_off == FALSE) {
return;
}
switch (type) {
case NORMAL_KEY:
PR_DEBUG("timer press.");
if (fan_state.local_timing == 0xFF) {
fan_state.local_timing = 1;
} else if (fan_state.local_timing >= 4) {
fan_state.local_timing = 0xFF; //取消定时
} else {
fan_state.local_timing++;
}
fan_local_timing_shutdown();
break;
case LONG_KEY:
PR_DEBUG("timer long press.");
break;
case SEQ_KEY:
PR_DEBUG("timer SEQ press, the count is %d.", cnt);
break;
default:
break;
}
}
/* 开关按键 */
if (port == KEY_POWER) {
switch (type) {
case NORMAL_KEY:
if (fan_state.on_off == FALSE) {
fan_state.on_off = TRUE;
PR_DEBUG("Turn on");
} else {
fan_state.on_off = FALSE;
PR_DEBUG("Turn off");
}
change_fan_state();
break;
case LONG_KEY:
PR_DEBUG("power long press.");
break;
case SEQ_KEY:
PR_DEBUG("power SEQ press, the count is %d.", cnt);
break;
default:
break;
}
}
write_flash_fan_state();
}
4、本地定时
本地定时功能简单的调用了一个软件定时器来实现:
VOID_T fan_timing_cd(VOID_T)
{
fan_state.local_timing--;
opSocSWTimerStop(SHUTDOWN_TIMER);
if (fan_state.local_timing == 0 || fan_state.on_off == FALSE) {
fan_turn_off();
} else {
PR_NOTICE("fan_state.local_timing ======== %d", fan_state.local_timing);
write_flash_fan_state();
opSocSWTimerStart(SHUTDOWN_TIMER, (SINGLE_TIMING*1000), fan_timing_cd);
fan_local_timing_led_set(fan_state.local_timing);
}
}
VOID_T fan_local_timing_shutdown(VOID_T)
{
fan_local_timing_led_set(fan_state.local_timing);
if (fan_state.local_timing > 4) { //无定时
opSocSWTimerStop(SHUTDOWN_TIMER);
return;
}
PR_NOTICE("run shutdown timer");
opSocSWTimerStop(SHUTDOWN_TIMER);
opSocSWTimerStart(SHUTDOWN_TIMER, (SINGLE_TIMING*1000), fan_timing_cd);
}
5、断电记忆功能
断电记忆功能依赖于flash,每次状态改变后,都将当前设备信息存储到flash中,在设备上电后首先读取flash中的数据对设备状态进行设定。
VOID_T read_flash_fan_state(VOID_T)
{
INT_T opRet, i;
UCHAR_T fan_state_data_crc;
UCHAR_T before_fan_power_off_state[FAN_STATE_STORAGE_LEN]; //断电前风扇状态
opRet = uiSocFlashRead(SAVE_TYP1, FAN_STATE_OFFSET, FAN_STATE_STORAGE_LEN*SIZEOF(UCHAR_T), before_fan_power_off_state);
if (opRet != FAN_STATE_STORAGE_LEN) {
PR_ERR("read data error for flash");
return;
}
//判断头部数据是否正确
if (before_fan_power_off_state[0] != FAN_DATA_HEAD) {
PR_ERR("data head error");
return;
}
fan_state_data_crc = get_crc_8(before_fan_power_off_state, (FAN_STATE_STORAGE_LEN - 1)*SIZEOF(UCHAR_T));
//校验数据是否正确
if (fan_state_data_crc != before_fan_power_off_state[FAN_STATE_STORAGE_LEN - 1]) {
PR_ERR("crc error, before_fan_power_off_state[%d] = %02x, crc data = %02x.", FAN_STATE_STORAGE_LEN - 1, before_fan_power_off_state[FAN_STATE_STORAGE_LEN - 1], fan_state_data_crc);
return;
}
//将从 flash 读取到的数据,存放到结构体中
fan_state.on_off = before_fan_power_off_state[FLASH_FAN_STATE_ON_OFF];
fan_state.mode = before_fan_power_off_state[FLASH_FAN_STATE_MODE];
fan_state.speed = before_fan_power_off_state[FLASH_FAN_STATE_SPEED];
fan_state.local_timing = before_fan_power_off_state[FLASH_FAN_STATE_TIMING];
return;
}
VOID_T write_flash_fan_state(VOID_T)
{
INT_T opRet, i;
UCHAR_T fan_state_buffer[FAN_STATE_STORAGE_LEN];
fan_state_buffer[0] = FAN_DATA_HEAD;
fan_state_buffer[1] = fan_state.on_off;
fan_state_buffer[2] = fan_state.mode;
fan_state_buffer[3] = fan_state.speed;
fan_state_buffer[4] = fan_state.local_timing;
fan_state_buffer[5] = get_crc_8(fan_state_buffer, (FAN_STATE_STORAGE_LEN - 1)*SIZEOF(UCHAR_T));
for (i=0; i<FAN_STATE_STORAGE_LEN; i++) {
PR_NOTICE(" +++ fan_state_buffer is [%d] : %02x", i, fan_state_buffer[i]);
}
opRet = opSocFlashWrite(SAVE_TYP1, FAN_STATE_OFFSET, fan_state_buffer, FAN_STATE_STORAGE_LEN * SIZEOF(UCHAR_T));
if (opRet != LIGHT_OK) {
PR_ERR("write flash error");
}
return;
}
6、设备复位功能
设备复位功能的实现是将 flash 中存储的设备状态更改为初始化值,或者你也可以直接将存储区域给擦除掉:
VOID_T erase_flash_fan_state(VOID_T)
{
INT_T opRet, i;
UCHAR_T fan_state_buffer[FAN_STATE_STORAGE_LEN];
fan_state.on_off = FALSE;
fan_state.mode = NORMAL_MODE;
fan_state.speed = 1;
fan_state_buffer[0] = FAN_DATA_HEAD;
fan_state_buffer[1] = FALSE; //fan_state.on_off
fan_state_buffer[2] = NORMAL_MODE; //fan_state.mode
fan_state_buffer[3] = 1; //fan_state.speed
fan_state_buffer[4] = 0xFF; // fan_state.local_timing
fan_state_buffer[5] = get_crc_8(fan_state_buffer, (FAN_STATE_STORAGE_LEN - 1)*SIZEOF(UCHAR_T));
for (i=0; i<FAN_STATE_STORAGE_LEN; i++) {
PR_NOTICE(" +++ fan_state_buffer is [%d] : %02x", i, fan_state_buffer[i]);
}
opRet = opSocFlashWrite(SAVE_TYP1, FAN_STATE_OFFSET, fan_state_buffer, FAN_STATE_STORAGE_LEN * SIZEOF(UCHAR_T));
if (opRet != LIGHT_OK) {
PR_ERR("write flash error");
}
return;
}
7、风扇状态改变
当通过 APP 或 按键想要改变设备状态时调用该函数对设备进行改变
VOID_T change_fan_state(VOID_T)
{
if (fan_state.on_off == FALSE) {
fan_turn_off();
hw_report_all_dp_status();
PR_NOTICE("stop sleep & natural timer");
return;
}
if (fan_state.bright == 1) {
fan_led_dimmer(100);
} else {
fan_led_dimmer(900);
}
if (fan_state.mode == SLEEP_MODE) {
PR_NOTICE("enter sleep mode");
fan_mode_sleep();
} else if (fan_state.mode == NATURAL_MODE) {
PR_NOTICE("enter natural mode");
fan_mode_natural();
} else {
PR_NOTICE("enter normal mode");
fan_mode_normal();
}
hw_report_all_dp_status();
/* speed LED set */
fan_speed_led_set(get_cur_gear()+1);
fan_mode_led_set();
return;
}
到这里智能风扇改装的软件部分就已经完成了,感兴趣的同学可以动手试一下。改造后的智能风扇支持APP&按键双控制,多种风速模式调整,定时关机,同时也可以和家里的语音控制设备联动实现语音控制,当然如果你有其它需求也可以自定义。有任何疑问欢迎留言讨论~