涂鸦智能暖风机软件实现之LED驱动和断电记忆功能
Posted 三明治开发社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了涂鸦智能暖风机软件实现之LED驱动和断电记忆功能相关的知识,希望对你有一定的参考价值。
智能暖风机软件实现之LED驱动和断电记忆功能
前言
前面我们已经实现了暖风机的离线按键控制,本文将实现暖风机的LED驱动和断电记忆功能。
一、暖风机的LED灯带显示功能
- 此次改装的智能暖风机拥有灯光效果,用户可以自行设置氛围灯的开启关闭以及更改颜色;同时配网模式时也会采用灯光渐变的效果作为提示。
功能 | 说明 |
---|---|
灯光 | 触摸按键:1个 按键控制,app控制 4种照明模式: 1 rgb1 2 rgb2 3 rgb3 4 rgb4 |
1.LED作为氛围灯时方案设定
-
硬件原理图:
采用RGB灯带,根据三路PWM的输出调节灯带的显示颜色,暖风机灯光效果提供四种颜色。 -
嵌入式实现方案
使用三个PWM输出引脚,配置输出的模式以及输出占空比。
PWM引脚设置:
GIN | P8
RIN | P7
BIN | P6
- 软件实现
在已有的开发基础上,增加led_color_set.c文件以及头文件,主要内容为引脚的PWM初始化,设置LED不同工作模式的函数代码。
目前整个工程的文件结构如下:
├── src
| ├── tuya_drive
| | └── b3950
| | └── b3950.c //温度传感器驱动相关
| | └── tm1650
| | └── tm1650.c
| | └── rgb_led //LED控制相关
| | └── led_color_set.c
| | └── tc309 //按键驱动相关
| | └── tc309.c
| | └── interrupt //外部中断
| | └── interrupt.c
| | └── gpio_control //gpio控制相关
| | └── gpio_control.c
| | └── timer
| | └── timer.c //定时器相关
| ├── tuya_device.c //应用层入口文件
| ├── tuya_thread.c //主要线程处理文件
| └── tuya_dp_process.c //dp数据触发的主要应用文件
|
├── include //头文件目录
| ├── tuya_drive_h
| | └── b3950_h
| | └── b3950.h
| | └── tm1650_h
| | └── tm1650.h
| | └── rgb_led_h //LED控制相关
| | └── led_color_set.h
| | └── tc309_h
| | └── tc309.h
| | └── interrupt_h
| | └── interrupt.h
| | └── gpio_control_h
| | └── gpio_control.h
| | └── timer_h
| | └── timer.h
| ├── tuya_device.h
| ├── tuya_thread.h
| └── tuya_dp_process.h
|
└── output //编译产物
led_color_set.c代码及说明如下:
#include "led_color_set.h"
#define LED_CLOSE 0
#define COLOR1 1
#define COLOR2 2
#define COLOR3 3
#define COLOR4 4
#define STAT_UNPROVISION_LED 5
#define STAT_AP_STA_UNCFG_LED 6
#define STAT_AP_STA_DISC_LED 7
static tuya_pwm_t *rgb_r_pwm = NULL;
static tuya_pwm_t *rgb_g_pwm = NULL;
static tuya_pwm_t *rgb_b_pwm = NULL;
tuya_pwm_t *buzzer_pwm = NULL;
uint8_t rgb_r_pwm_pin = TY_GPIOA_7;
uint8_t rgb_g_pwm_pin = TY_GPIOA_8;
uint8_t rgb_b_pwm_pin = TY_GPIOA_6;
void led_init()
{
/*color Red init*/
rgb_r_pwm = (tuya_pwm_t *)tuya_driver_find(TUYA_DRV_PWM, TUYA_PWM0);
TUYA_PWM_CFG(rgb_r_pwm, rgb_r_pwm_pin, 10 * 1000, 0);
tuya_pwm_init(rgb_r_pwm);
tuya_pwm_start(rgb_r_pwm);
/*color Green init*/
rgb_g_pwm = (tuya_pwm_t *)tuya_driver_find(TUYA_DRV_PWM, TUYA_PWM1);
TUYA_PWM_CFG(rgb_g_pwm, rgb_g_pwm_pin, 10 * 1000, 0);
tuya_pwm_init(rgb_g_pwm);
tuya_pwm_start(rgb_g_pwm);
/*color Bule init*/
rgb_b_pwm = (tuya_pwm_t *)tuya_driver_find(TUYA_DRV_PWM, TUYA_PWM5);
TUYA_PWM_CFG(rgb_b_pwm, rgb_b_pwm_pin, 10 * 1000, 0);
tuya_pwm_init(rgb_b_pwm);
tuya_pwm_start(rgb_b_pwm);
}
VOID color_handle(IN int mode)
{
switch (mode){
case LED_CLOSE:
{
led_close();
}
break;
case COLOR1:
{
color1_set();
}
break;
case COLOR2:
{
color2_set();
}
break;
case COLOR3:
{
color3_set();
}
break;
case COLOR4:
{
color4_set();
}
break;
case STAT_UNPROVISION_LED:
{
//配网模式指示灯
stat_unprovision_led();
}
break;
case STAT_AP_STA_UNCFG_LED:
{
//配网模式指示灯
stat_unprovision_led();
}
break;
case STAT_AP_STA_DISC_LED:
{
//配网模式指示灯
stat_unprovision_led();
}
break;
default:
break;
}
}
void color1_set()
{
tuya_pwm_duty_set(rgb_r_pwm, 1.0);
tuya_pwm_duty_set(rgb_g_pwm, 0);
tuya_pwm_duty_set(rgb_b_pwm, 0);
}
void color2_set()
{
tuya_pwm_duty_set(rgb_r_pwm, 0);
tuya_pwm_duty_set(rgb_g_pwm, 1.0);
tuya_pwm_duty_set(rgb_b_pwm, 0);
}
void color3_set()
{
tuya_pwm_duty_set(rgb_r_pwm, 0);
tuya_pwm_duty_set(rgb_g_pwm, 0);
tuya_pwm_duty_set(rgb_b_pwm, 1.0);
}
void color4_set()
{
tuya_pwm_duty_set(rgb_r_pwm, 0.15);
tuya_pwm_duty_set(rgb_g_pwm, 0.8);
tuya_pwm_duty_set(rgb_b_pwm, 0.8);
}
void led_close()
{
tuya_pwm_duty_set(rgb_r_pwm, 0);
tuya_pwm_duty_set(rgb_g_pwm, 0);
tuya_pwm_duty_set(rgb_b_pwm, 0);
}
void stat_unprovision_led()
{
static float pwm_duty = 0;
tuya_pwm_duty_set(rgb_r_pwm, pwm_duty);
tuya_pwm_duty_set(rgb_g_pwm, 0);
tuya_pwm_duty_set(rgb_b_pwm, 0);
pwm_duty = pwm_duty + 0.1;
if(pwm_duty > 1)
{
pwm_duty = 0;
}
}
void stat_ap_sta_uncfg_led()
{
static float pwm_duty = 0;
tuya_pwm_duty_set(rgb_r_pwm, 0);
tuya_pwm_duty_set(rgb_g_pwm, 0);
tuya_pwm_duty_set(rgb_b_pwm, pwm_duty);
pwm_duty = pwm_duty + 0.1;
if(pwm_duty > 1)
{
pwm_duty = 0;
}
}
void stat_ap_sta_disc_led()
{
static float pwm_duty = 0;
tuya_pwm_duty_set(rgb_r_pwm, 0);
tuya_pwm_duty_set(rgb_g_pwm, pwm_duty);
tuya_pwm_duty_set(rgb_b_pwm, 0);
pwm_duty = pwm_duty + 0.1;
if(pwm_duty > 1)
{
pwm_duty = 0;
}
}
上面是LED的驱动程序,led_init()函数放到calorifier_init()中初始化,之后按键控制或者APP控制就可以使用color_handle(IN int mode)来设置LED灯带工作的模式。
2.配网模式下LED状态显示
- 前面以及实现了LED的驱动,在正常情况用户可以自行切换灯光颜色和选择关闭灯光;配网模式下LED将渐变闪烁提示用户配网,配网成功后灯光将关闭。
- 什么是配网?
在刚拿到设备时,打开暖风机长按led调节按键将进入配网模式,此时手机连接WIFI,打开涂鸦智能app点击添加设备->搜索设备,此时就可以搜到暖风机这个设备,下一步按照提示输入WIFI名和密码即可进行连接;连接成功即手机和暖风机配网成功。
配网时LED进行红光渐变闪烁,若三分钟未连接上设备将退出配网模式,LED显示关闭,若连接成功也将关闭LED显示。
- 配网功能实现
长按led调节按键时,启动配网程序:
case KEY4:
{
//Short press:led mode key long press: Reconfigure the network mode
if(dp_memory_s.switch_bool)
{
timer1_init();
static uint32_t count = 0;
while(tuya_gpio_read(9) == 0){
count++;
if(count > 700000){
break;
}
}
if(count > 700000){
count = 0;
PR_DEBUG("wf_unc");
tuya_iot_wf_gw_unactive();
}else{
PR_DEBUG("key_value4");
static uint8_t cur_ledmode = 0;
cur_ledmode = dp_memory_s.led_mode;
cur_ledmode = (cur_ledmode + 1 < 5) ? (cur_ledmode + 1) : 0;
led_handle(cur_ledmode);
}
PR_DEBUG("led mode:%d",dp_memory_s.led_mode);
}
}
调用tuya_iot_wf_gw_unactive()进入配网,此时系统将调用tuya_device.c文件中的wf_nw_status_cb(IN CONST GW_WIFI_NW_STAT_E stat) 函数从而调用wifi_state_led_reminder(stat)函数。
更改原有的函数内容
STATIC VOID wifi_state_led_reminder(IN CONST GW_WIFI_NW_STAT_E cur_stat)
{
wf_nw_status_temp = cur_stat;
PR_NOTICE("wf_nw_status_temp:%d",wf_nw_status_temp);
}
此时idle_task线程将根据配网的状态进行led的状态设定
void idle_task(void)
{
static uint8_t wf_nw_led = 1;
while(1)
{
//离线模式倒计时关闭相关代码
shutdown_time_s.value = ((dev_key_s.temp_time_count > timercount) && (0 < (dev_key_s.temp_time_count - timercount)/6 < 720)) ? (dev_key_s.temp_time_count - timercount)/6 : 0;
dev_key_s.timer_hour = shutdown_time_s.value/60;
if((timercount > dev_key_s.temp_time_count) && dev_key_s.temp_time_count && dev_key_s.key_notice){
PR_DEBUG("SHUTDOWN TIME");
time_off_handle(1);
dev_key_s.temp_time_count = 0;
dev_key_s.timer_hour = 0;
dev_key_s.key_notice = 0;
tuya_hal_system_sleep(1000);
}
if((dp_memory_s.temperature_value > dp_memory_s.set_temper_value) && dp_memory_s.relay_mode){
relay_handle(0);
}
//配网模式LED灯光的相关状态配置
if(wf_nw_led && dp_memory_s.switch_bool){
switch (wf_nw_status_temp)
{
case STAT_LOW_POWER: //WiFi connection network timeout, enter low power mode
{
color_handle(0);
wf_nw_led = 0;
}
break;
case STAT_UNPROVISION: //SamrtConfig connected network mode, waiting for connection
{
color_handle(5);
}
break;
case STAT_AP_STA_UNCFG: //ap connected network mode, waiting for connection
{
color_handle(6);
}
break;
case STAT_AP_STA_DISC:
case STAT_STA_DISC: //SamrtConfig/ap connecting...
{
color_handle(7);
}
break;
case STAT_CLOUD_CONN:
case STAT_AP_CLOUD_CONN: //Already connected to Tuya Cloud
{
color_handle(0);
wf_nw_led = 0;
}
break;
default:
break;
}
tuya_hal_system_sleep(200);
}
}
}
- 上面我们已经实现了配网时LED的提示功能
二、智能暖风机断电记忆功能
1.断电记忆功能
智能暖风机提供的记忆功能如下
功能 | 说明 |
---|---|
待机记忆 | 按键,app,主动操作开关键关机为待机状态。 再开启后恢复上一次设置: 温度设置:上一次设置 温度显示:当前环境温度 灯光模式:上一次设置 设备定时关:默认关闭 app定时关:默认关闭。 app定时开:上一次设置。 |
断电记忆 | 断电后为断电状态,再上电恢复上一次设置: 开关状态:默认关 温度设置:上一次设置 温度显示:当前环境温度 灯光模式:上一次设置 设备定时关:默认关闭 app定时关:默认关闭。 app定时开:上一次设置。 |
2.记忆功能的实现
-
记忆功能嵌入式方案设定
我们采用将要保存的数据写入到FLASH闪存中,在需要时再读取出来,从而实现记忆功能。 -
代码实现
在已有的开发基础上,增加soc_flash.c文件以及头文件,主要内容为数据写入到flash和从flash读取数据的实现代码。
目前整个工程的文件结构如下:
├── src
| ├── tuya_drive
| | └── b3950
| | └── b3950.c //温度传感器驱动相关
| | └── tm1650
| | └── tm1650.c
| | └── soc_flash
| | └── soc_flash.c
| | └── rgb_led //LED控制相关
| | └── led_color_set.c
| | └── tc309 //按键驱动相关
| | └── tc309.c
| | └── interrupt //外部中断
| | └── interrupt.c
| | └── gpio_control //gpio控制相关
| | └── gpio_control.c
| | └── timer
| | └── timer.c //定时器相关
| ├── tuya_device.c //应用层入口文件
| ├── tuya_thread.c //主要线程处理文件
| └── tuya_dp_process.c //dp数据触发的主要应用文件
|
├── include //头文件目录
| ├── tuya_drive_h
| | └── b3950_h
| | └── b3950.h
| | └── tm1650_h
| | └── tm1650.h
| | └── soc_flash_h
| | └── soc_flash.h
| | └── rgb_led_h //LED控制相关
| | └── led_color_set.h
| | └── tc309_h
| | └── tc309.h
| | └── interrupt_h
| | └── interrupt.h
| | └── gpio_control_h
| | └── gpio_control.h
| | └── timer_h
| | └── timer.h
| ├── tuya_device.h
| ├── tuya_thread.h
| └── tuya_dp_process.h
|
└── output //编译产物
soc_flash.c增加的代码如下:
#include "soc_flash.h"
#include "uf_file.h"
#include "tuya_cloud_error_code.h"
STATIC BOOL_T bSocFlashInitFlag = FALSE;
/**
* @brief: wifi uf write(a+ mode)
* @param {IN CHAR_T *pFilename -> file name}
* @param {IN UCHAR_T *pData -> save data}
* @param {IN USHORT_T usLen -> save data len}
* @retval: OPERATE_LIGHT
*/
STATIC INT_T opSocFlashFileWrite(IN CHAR_T *pFilename, IN UCHAR_T *pData, IN USHORT_T usLen)
{
int opRet = -1;
uFILE * fp = NULL;
UINT_T uiOffSet = 0;
UINT_T uiWriteCnt = 0;
fp = ufopen(pFilename, "a+");
if(NULL == fp) {
PR_ERR("uf file %s can't open and write data!", pFilename);
return OPRT_COM_ERROR;
}
uiOffSet = ufseek(fp, 0, APP_DP_DATA_OFFSET);
if(uiOffSet != 0) {
PR_ERR("uf file %s Set file offset to 0 error!", pFilename);
return OPRT_COM_ERROR;
}
uiWriteCnt = ufwrite(fp, pData, usLen);
if(uiWriteCnt != usLen) {
PR_ERR("uf file %s write data error!", pFilename);
return OPRT_COM_ERROR;
}
opRet = ufclose(fp);
if(opRet != OPRT_OK) {
PR_ERR("uf file %s close error!", pFilename);
return opRet;
}
return OPRT_OK;
}
/**
* @brief: soc data save
* @param {IN SOC_FLASH_SAVE_TYPE_E eDataType -> save type(meaning data kind)}
* @param {IN UINT_T uiAddr -> this type data address offset}
* @param {IN UCHAR_T *pData -> save data}
* @param {IN USHORT_T usLen -> save data len}
* @retval: OPERATE_LIGHT
*/
INT_T opSocFlashWrite(IN SOC_FLASH_SAVE_TYPE_E eDataType, IN UINT_T uiAddr, IN UCHAR_T *pData, IN USHORT_T usLen)
{
int opRet = -1;
CHAR_T cTemp[4] = {0};
if(bSocFlashInitFlag != TRUE) {
bSocFlashInitFlag = TRUE;
}
if(eDataType >= SAVE_TYP_MAX) {
PR_ERR("Write soc flash type error!");
return OPRT_INVALID_PARM;
}
/*set data type tag*/
snprintf(cTemp, 4, "%d", eDataType);
opRet = opSocFlashFileWrite(cTemp, pData, usLen);
if(opRet != OPRT_OK) {
return opRet;
}
return OPRT_OK;
}
/**
* @brief: wifi uf read
* @param {IN CHAR_T *pFilename -> read file name}
* @param {IN USHORT_T usLen -> read data len}
* @param {OUT UCHAR_T *pData -> read data}
* @retval: read data cnt
*/
STATIC INT_T uiSocFlashFileRead(IN CHAR_T *pFilename, IN USHORT_T usLen, OUT UCHAR_T *pData)
{
int opRet = -1;
uFILE * fp = NULL;
INT_T uiReadCnt = 0;
fp = ufopen(pFilename, "r+");
if(NULL == fp) {
PR_ERR("uf file %s can't open and read data!", pFilename);
return OPRT_COM_ERROR;
}
PR_DEBUG("uf open OK");
uiReadCnt = ufread(fp, pData, usLen);
PR_DEBUG("uf file %s read data %d!", pFilename, uiReadCnt);
opRet = ufclose(fp);
if(opRet != OPRT_OK) {
PR_ERR("uf file %s close error!", pFilename);
return opRet;
}
return uiReadCnt;
}
/**
* @brief: soc flash save data read
* @param {IN SOC_FLASH_SAVE_TYPE_E eDataType -> read data type(meaning data kind)}
* @param {IN UINT_T uiAddr -> this type data address offset}
* @param {IN USHORT_T ucLen -> read data len}
* @param {OUT UCHAR_T *pData -> read data}
* @retval: read data cnt
*/
INT_T uiSocFlashRead(IN SOC_FLASH_SAVE_TYPE_E eDataType, IN UINT_T uiAddr, IN USHORT_T usLen, OUT UCHAR_T *pData)
{
int opRet = -1;
INT_T uiReadCnt = 0;
CHAR_T cTemp[4] = {0};
if(bSocFlashInitFlag != TRUE) {
opRet = uf_file_app_init("12345678901234567890123456789012", 32);
if(opRet != OPRT_OK) {
PR_ERR("uf file init error! can't write or read!");
return opRet;
}
bSocFlashInitFlag = TRUE;
}
if(eDataType >= SAVE_TYP_MAX) {
PR_ERR("Read soc flash type error!");
return OPRT_INVALID_PARM;
}
snprintf(cTemp, 4, "%d", eDataType);
PR_DEBUG("file name %s",cTemp);
uiReadCnt = uiSocFlashFileRead(cTemp, usLen, pData);
return uiReadCnt;
}
/**
* @brief: soc flash special block delete
* @param {none}
* @retval: OPERATE_LIGHT
*/
INT_T opSocFlashEraseSpecial(IN SOC_FLASH_SAVE_TYPE_E DataType, IN UINT_T addr)
{
int opRet = 0;
CHAR_T cTemp[4] = {0};
if(bSocFlashInitFlag != TRUE) {
return OPRT_OK; /* directly return */
}
snprintf(cTemp, 4, "%d", DataType);
opRet = ufdelete(cTemp);
if(opRet != OPRT_OK) {
PR_ERR("Delete %s file error!", cTemp);
}
return OPRT_OK;
}
/**
* @brief: soc flash erase all
* @param {none}
* @retval: OPERATE_LIGHT
*/
INT_T opSocFlashErase(VOID)
{
int opRet = -1;
CHAR_T cTemp[4] = {0};
if(bSocFlashInitFlag != TRUE) {
return OPRT_OK; /* directly return */
}
snprintf(cTemp, 4, "%d", SAVE_TYP1);
opRet = ufdelete(cTemp);
if(opRet != OPRT_OK) {
PR_ERR("Delete %s file error!", cTemp);
}
snprintf(cTemp, 4, "%d", SAVE_TYP2);
opRet = ufdelete(cTemp);
if(opRet != OPRT_OK) {
PR_ERR("Delete %s file error!", cTemp);
}
snprintf(cTemp, 4, "%d", SAVE_TYP3);
opRet = ufdelete(cTemp);
if(opRet != OPRT_OK) {
PR_ERR("Delete %s file error!", cTemp);
}
snprintf(cTemp, 4, "%d", SAVE_TYP4);
opRet = ufdelete(cTemp);
if(opRet != OPRT_OK) {
PR_ERR("Delete %s file error!", cTemp);
}
opRet = ufdelete("oem_cfg");
if(opRet != OPRT_OK) {
PR_ERR("Delete oem_cfg file error!");
}
return OPRT_OK;
}
- 代码使用说明:
//创建了一个dp数据保存的结构体
Data_Memory_T dp_memory_s = {
.switch_bool = 0,
.led_mode = 0,
.relay_mode = 0,
.temperature_value = 0,
.shake_bool = 0,
.set_temper_value = 0,
.time_on_bool = 0,
.time_off_bool = 0,
.status = 0,
};
//创建了一个flash写入线程,接受到信号量后会将dp_memory_s结构写入flash
void flash_write_thread(void)
{
while(1)
{
tuya_hal_semaphore_wait(g_flash_binsemap);
//写入数据到flash
opUserFlashWriteAppData(&dp_memory_s);
}
}
/*上电后会执行记忆恢复函数,将dp_memory_s数据读出,根据读出的数据恢复断电前使用者的
暖风机控制*/
VOID_T Power_data_recovery(void)
{
Data_Memory_T *power_down_memory = (Data_Memory_T *)Malloc(SIZEOF(Data_Memory_T));
memset(power_down_memory,0,sizeof(Data_Memory_T));
uiSocFlashRead(SAVE_TYP1, APP_DP_DATA_OFFSET, sizeof(Data_Memory_T), (UCHAR_T *)(power_down_memory));
dp_memory_s.led_mode = power_down_memory->led_mode;
dp_memory_s.switch_bool = 0;
dp_memory_s.set_temper_value = power_down_memory->set_temper_value;
//device timer close clear
dev_key_s.temp_time_count = 0;
dev_key_s.timer_hour = 0;
Free(power_down_memory);
}
此时断电记忆功能已经实现,暖风机功能又完善了一些,后面我们将为暖风机进行赋能,实现智能控制。
技术支持
您可以通过以下方法获得涂鸦的支持:
以上是关于涂鸦智能暖风机软件实现之LED驱动和断电记忆功能的主要内容,如果未能解决你的问题,请参考以下文章