改造一台智能空气净化器【APP控制,滤芯NFC识别、寿命计算】

简介: 本文方案在保持原始功能的同时改善了净化强度工作方式,增加滤芯寿命计算功能。可以自动检测空气中有害物质的浓度,根据污染浓度实时自动调节净化强度,还能检测空气净化器滤芯使用情况,计算滤芯使用寿命,提醒更换滤芯等功能。

先附上源码:
[github]: “空气净化器demo

一. 硬件方案

1. 硬件框图

在这里插入图片描述

2. 特性描述

  • 主控模块主控模块采用涂鸦自研的WBRU模组,用于控制整机运行,包括按键检测,LED指示,负载控制,数据通讯等工作
  • 采用四合一粉尘传感器实时测量输出温湿度、VOC 浓度、PM1.0/PM2.5 /PM10质量浓度
  • 蜂鸣器采用压电式无源蜂鸣器,通过模组输出相应频率PWM信号驱动
  • 微动开关可以防止误打开滤芯所在后盖
  • 光敏传感器可以检测环境光线强度,自动调节显示亮度
  • LED可直观显示定时、异常警报等信息
  • NFC读卡器可以有效检测滤芯的匹配情况与使用寿命

3. 设计方案

3.1 主控单元

本方案选用一款低功耗WBRU模组作为主控板,并基于该模组进行SoC开发;实现电机控制和传感器数据采集。

alt

3.2 电机控制

空气净化器的风机是由选用稻津的 MI-JK48HM710,控制信号主要由PWM、FG、BRK,控制信号与模组之间通讯需进行电平转换。

alt

3.3 按键与感光传感器

​ 系统有两个按键,主按键: 长按开关机,短按切换不同的工作模式(自动,睡眠,最爱);屏幕按键: 短按切换屏幕和指示灯的状态(PM2.5,PM10,TVOC,温度,湿度,关), 长按进入配网模式。系统中有一个光敏二极管,用于控制夜间光线较暗时显示的亮度。

alt

3.4 显示电路

​ 方案使用一个TM1650驱动IC来驱动一个3位8段数码管和5个模式指示灯。数码管用于显示空气指标,通过屏幕按键控制,分别为PM2.5数据,PM10数据,TVOC数据,温度,湿度。

alt

3.5 NFC驱动

方案使用MFRC522,它是高度集成的非接触式读写卡芯片,可根据不同主机接口实现SPI、串行UART、I2C协议通讯。方案中使用SPI通讯来实现滤芯NFC检测功能(通过NFC判断滤芯工作时间,以及是否更换新滤芯)。

alt

alt

3.6 四合一传感器驱动

AM1002 是一款高性价比的多参数空气质量传感器集成模组,能够实时测量输出温湿度、VOC 浓度、PM1.0/PM2.5 /PM10 质量浓度。

alt

3.7 电源

​ 方案使用 LOWPOWER(微源半导体)的lp6498ab6f输出5V给一些传感器供电,ASM1117由5V转3.3V给模组供电,供电电流不低于150mA。lp6498ab6f的规格如下:最大输入电压4.530V;输出电压范围0.812V。开关频率600KHZ,最大持续电流可达1.2A。

alt

3.8 其他驱动

​ 此外,还有蜂鸣器,UV灯,以及机械微动开关用于仓门检测。

在这里插入图片描述

在这里插入图片描述

alt

4. 原理图与PCB

alt

alt

alt

二. 整机搭建

1. 实物展示图

alt

三. 嵌入式功能实现

1. 功能需求

**产品功能简述:**有自动、手动、睡眠三种模式,可以通过按键和APP选择切换。其中自动模式会根据环境自动控制风扇转速、UV灯、屏幕灯光等;还可以通过NFC检测滤芯是否安装,并读取滤芯生产信息;同时能够智能计算滤芯寿命。

功能说明
设备功能1. 主按键: 长按开关机,短按切换不同的工作模式(自动、手动、睡眠)。
2. 屏幕按键: 短按切换屏幕和指示灯的状态(PM2.5,PM10,TVOC,温度,湿度,关), 长按进入配网模式。
3. 显示屏:三位断码屏,显示空气指标,通过屏幕按键控制,分别为PM2.5数据、PM10数据、TVOC数据、温度、湿度。
4. 蜂鸣器:按键音及报警提醒。
5. 如果滤芯仓门是打开的,则设备断电不工作。
6. NFC芯片:每次开机,检测滤芯NFC是否存在,读取滤芯信息并上报。
APP功能1. 总开关。
2. 童锁:APP上开启后,设备端按键无法使用。
3. 静音:APP上开启后,设备端按键静音。
4. 定时:APP上设置后,启动定时开关功能。
5. 智能杀菌:需要APP手动开启。
6. 空气指标显示:分别为PM2.5数据、PM10数据、TVOC数据、温度、湿度。
7. 滤芯寿命显示及重置,支持APP手动重置,也支持NFC识别后自动重置。
8. 工作模式:自动模式,手动模式,睡眠模式三种。
自动模式,运行自动模式功能。
手动模式,可以手动设置此模式下的风速档位。
睡眠模式,进入睡眠档位20%风速,屏幕LED灯关闭。
9. 风速档位:睡眠(20%),低(40%),中(70%),高(100%)。
智能功能1. 自动模式—风扇自动调节功能。
2. 自动模式—环保功能:当室内空气质量优(0-35)且保持一段时间(2h)时,关闭风扇,以节省能源,如果PM2.5再次超过35,则启动风扇自动调节。
3. 自动模式—睡眠功能:当光线传感器检测到房间已经变暗,设备自动关闭灯光和屏幕。此时如果空气质量优保持一段时间时(30min),则风扇降为最低档位(20%风速)。
4. 智能杀菌功能:温度20°以上,湿度65%以上,且近4h无触发过杀菌(开启UV灯),则开启UV杀菌持续1h。
5. 滤芯NFC检测功能:检测滤芯是否存在,并读取滤芯类别和生产信息,可防盗版。
6. 滤芯寿命智能计算功能:智能计算滤芯的粉尘累计吸附量,得到滤芯剩余寿命,并根据粉尘吸附速度和设备使用频率估算滤芯剩余可用时间。
配网功能通用Wi-Fi+BLE配网,长按配网按键重置配网。

2. 环境搭建

2.1 开发环境搭建

(1)开发环境搭建可以参考Wi-Fi 模组二次开发教程——1. SoC开发环境搭建。如果已经有虚拟机和乌班图的开发环境可直接跳至4.2 下载编译依赖工具处进行剩余环境搭建。

(2)产品创建可以参考Wi-Fi模组二次开发教程——2. 涂鸦IoT平台介绍。创建产品后,添加产品标准功能DP点和自定义功能DP点。空气净化器DP点如下表。

(3)参考Wi-Fi模组二次开发课程——3. 快速上手来完成代码修改编译、固件上传、获取token、烧录授权和设备配网。

2.2 功能DP点
DP ID功能点标识符数据传输类型数据类型功能点属性
1开关switch可下发可上报bool
2PM2.5pm25只上报value数值范围: 0-700,间距: 1, 倍数: 0,单位: ug/m3
3模式mode可下发可上报enum枚举值: manual, auto, sleep
4风速fan_speed_enum可下发可上报enum枚举值: sleep, low, mid, high
5滤芯寿命filter_life只上报value数值范围: 0-100,间距: 1, 倍数: 0,单位: %
7童锁child_lock可下发可上报bool
8灯光light可下发可上报bool
9UV杀菌uv可下发可上报bool
11滤芯复位filter_reset可下发可上报bool
12室内温度temp_indoor只上报value数值范围: -20-50,间距: 1, 倍数: 0,单位: ℃
13室内湿度humidity只上报value数值范围: 0-100,间距: 1, 倍数: 0,单位: %
14TVOCtvoc只上报value数值范围: 0-999,间距: 1, 倍数: 0,单位: ug/m3
16滤芯剩余天数filter_days只上报value数值范围: 0-1000,间距: 1, 倍数: 0,单位: day
17累计工作时间runtime_total只上报value数值范围: 0-5256000,间距: 1, 倍数: 0,单位: min
18倒计时countdown_set可下发可上报enum枚举值: cancel, 1h, 2h, 3h, 4h, 5h
19倒计时剩余时间countdown_left只上报value数值范围: 0-360,间距: 1, 倍数: 0,单位: min
20累计吸收颗粒pm_total只上报value数值范围: 0-10000000,间距: 1, 倍数: 0,单位: mg
22故障告警fault只上报fault故障值: e1, e2
23温标切换temp_unit_convert可下发可上报enum枚举值: c, f
101PM10pm10只上报value数值范围: 0-999,间距: 1, 倍数: 0,单位: ug/m3
102滤芯种类filter_type只上报enum枚举值: standard, Antibacterial, Aldehyde_removal, Professional
103静音sound_switch可下发可上报bool

3. 总体设计

3.1 模块划分

对功能需求进行分析梳理后,可将空气净化器demo程序划分为以下八大模块:

No.模块处理内容
1外设驱动组件按键、电机、段码显示屏、NFC读卡器、空气质量传感器等驱动程序
2设备基础服务设备开关、状态处理、模式切换、本地定时等
3显示处理服务段码液晶屏显示空气指标数据、LED灯指示段码屏显示的空气指标内容
4环境检测服务检测空气质量环境、光线亮暗情况
5数据计算处理自动风扇转速算法滤芯寿命和粉尘吸附量的计算和存储、滤芯NFC数据解析
6用户事件处理按键事件检测和处理、仓门开关事件检测和处理
7定时事件处理各定时事件的判断和处理
8联网相关处理配网相关处理、数据上报与接收处理、云端时间获取
3.2 代码结构
tuya_air_cleaner_demo
├── platform             /* 涂鸦通用 Tuya IoTOS SDK 的开发编译环境和工具链 */
├── sdk                  /* 存放涂鸦通用 Tuya IoTOS SDK 的头文件和库文件 */
└──  app                 /* 存放涂鸦通用 Tuya IoTOS SDK 的 demo */
    ├── src              /* 源文件目录 */
    │   ├── common
    │   │   ├── tuya_device.c                     /* 应用层入口 */
    │   │   ├── tuya_dp_process.c                 /* DP上下发处理 */
    │   │   ├── tuya_iot_funtion.c                /* 连接IOT云 */
    │   │   └── tuya_key_funtion.c                /* 按键处理 */
    │   ├── driver
    │   │   ├── mfrc522
    │   │   │   ├── tuya_mfrc522_app.c            /* NFC芯片mfrc522中间层驱动 */
    │   │   │   └── tuya_mfrc522.c                /* NFC芯片mfrc522驱动 */
    │   │   ├── tm1650
    │   │   │   ├── soc_i2c.c                     /* 软件I2C模拟驱动 */
    │   │   │   ├── tm1650_app.c                  /* 液晶显示屏芯片tm1650中间层驱动 */
    │   │   │   └── tm1650.c                      /* 液晶显示屏芯片tm1650驱动 */
    │   │   ├── tuya_buz_driver.c                 /* 蜂鸣器驱动 */
    │   │   ├── tuya_hardware_driver.c            /* 硬件GPIO驱动 */
    │   │   ├── tuya_lcd_display.c                /* 液晶屏应用层驱动 */
    │   │   └── tuya_motor_driver.c               /* 风扇电机驱动 */
    │   └── function
    │       ├── tuya_air_quality_funtion.c        /* 空气质量获取和显示功能 */
    │       ├── tuya_automatic_mode_funtion.c     /* 自动模式相关功能 */
    │       ├── tuya_countdown_funtion.c          /* 倒计时功能 */
    │       ├── tuya_filter_funtion.c             /* 滤芯寿命相关功能 */
    │       ├── tuya_mode_funtion.c               /* 模式切换相关功能 */
    │       ├── tuya_nfc_funtion.c                /* 滤芯NFC检测相关功能 */
    │       └── tuya_timer_funtion.c              /* Timer相关功能 */
    └── include
        ├── common
        │   ├── tuya_device.h
        │   ├── tuya_dp_process.h
        │   ├── tuya_iot_funtion.h
        │   └── tuya_key_funtion.h
        ├── driver
        │   ├── mfrc522
        │   │   ├── tuya_mfrc522_app.h
        │   │   └── tuya_mfrc522.h
        │   ├── tm1650
        │   │   ├── soc_i2c.h
        │   │   ├── tm1650_app.h
        │   │   └── tm1650.h
        │   ├── tuya_buz_driver.h
        │   ├── tuya_hardware_driver.h
        │   ├── tuya_lcd_display.h
        │   └── tuya_motor_driver.h
        └── function
            ├── tuya_air_quality_funtion.h
            ├── tuya_automatic_mode_funtion.h
            ├── tuya_countdown_funtion.h
            ├── tuya_filter_funtion.h
            ├── tuya_mode_funtion.h
            ├── tuya_nfc_funtion.h
            └── tuya_timer_funtion.h
3.3 应用框架

下图所示为基于 Tuya Wi-Fi SDK 的应用框架:

  • Platform:所使用的芯片平台,芯片 + 协议栈由芯片公司维护。

  • Port:Tuya Wi-Fi SDK 所需要的抽象接口,需要用户根据具体的芯片平台移植实现。

  • Tuya Wi-Fi SDK : 封装了涂鸦 Wi-Fi 通信协议,提供构建涂鸦 Wi-Fi 应用所需的服务接口。

  • Application:基于Tuya Wi-Fi SDK 构建的应用。

  • Tuya SDK API:API用于设备实现Wi-Fi相关的管理、通信等,API的调用将采用基于消息的异步机制,API的执行结果将会以 Message 或者 Call back 的方式通知给设备的 Application。

  • SDK Config:Tuya Wi-Fi SDK 可裁剪可配置,通过配置文件中的宏定义可将 Tuya Wi-Fi SDK 设置成不同模式,例如配置成适用于多协议设备的通用配网模式、单模配网模式、是否使用 OS 等。

  • Main Process:为 Tuya SDK API 的主引擎,Application 需要一直调用,如果 Platform 架构是带OS的,Tuya Wi-Fi SDK 会基于 Port 层提供的OS相关接口自动创建一个任务用于执行Main Process,如果是非OS平台,需要设备 Application 循环调用。

  • Message or Call back:SDK 通过 Message 或者设备 Application注册的 Call back 函数向设备 Application 发送数据(状态、数据等)。

3.4 驱动软件模块

3.5 方案流程图

(1)模式选择功能流程图:

模式流程图.drawio

(2)按键功能流程图

(3)滤芯检测流程图

4. 功能实现

4.1 外设驱动
4.1.1 段码液晶屏

段码液晶屏由 3 * 8 断码屏和 5 个指示灯构成,分别对应PM2.5,PM10,TVOC,温度,湿度。

其中外挂驱动芯片 TM1650,I2C 通信,本文demo中使用 2 * GPIO 模拟 I2C 。

段码液晶屏部分代码:

/**
* @brief: tuya_lcd_show_num
* @desc: show num on lcd
* @param[in] number:-99<number<999
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
OPERATE_RET tuya_lcd_show_num(IN INT_T number)
{
    OPERATE_RET op_ret = OPRT_OK;
    MINUS_FLAG_E minus_flag = MINUS_NULL;
    UCHAR_T i = 0;
    UCHAR_T bit[3];

    if (number > MAX_NUM) {
        number = MAX_NUM;
    }
    if (number < MIN_NUM) {
        number = MIN_NUM;
    }
    if (number < 0) {
        if (number > -10) {  //-10<number<0
            minus_flag = MINUS_TEN;
        } else {             //-100<number<-10
            minus_flag = MINUS_HUNDRED;
        }
        number = number * (-1);
    }

    bit[0] = number / 100 % 10;  //hundred
    bit[1] = number / 10 % 10;   //ten
    bit[2] = number % 10;        //one

    /* hundred == 0 */
    if (bit[0] == 0) {
        if (minus_flag == MINUS_HUNDRED) {
            bit[0] = SEG_SHOW_MINUS;  //hundred bit show minus
        } else {
            bit[0] = SEG_SHOW_NULL;   //hundred bit show null
        }
    /* ten == 0 */
        if (bit[1] == 0) {
            if (minus_flag == MINUS_TEN) {
                bit[1] = SEG_SHOW_MINUS;  //ten bit show minus
            } else {
                bit[1] = SEG_SHOW_NULL;  //ten bit show null
            }
        }
    }

    /* i2c send command and data */
    for (i = 0; i < 3; i++) {
        op_ret = tuya_tm1650_app_write(dig[i], seg_num[bit[i]]); 
        if (op_ret != OPRT_OK) {
            PR_ERR("tuya_tm1650_app_write error:%d", op_ret);
            return op_ret;
        }
    }
    
    return op_ret;
}

/**
* @brief: tuya_led_show
* @desc: Choose the led that led show
* @param[in] show_led: 0,1,2,3,4,5
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
OPERATE_RET tuya_led_show(IN CONST UCHAR_T show_led)
{
    OPERATE_RET op_ret = OPRT_OK;

    //Check the input parameter, show_led: 0~5
    if (show_led > 5) {
        PR_ERR("show_led input parameter error!");
        return OPRT_INVALID_PARM;
    }
    
    /* i2c send command and data */
    op_ret = tuya_tm1650_app_write(dig[3], seg_led[show_led]); 
    if (op_ret != OPRT_OK) {
        PR_ERR("tuya_tm1650_app_write error:%d", op_ret);
        return op_ret;
    }
    return op_ret;
}
4.1.2 风扇电机

采用空气净化器专用的直流无刷电机,模块主要引脚如下表。

引脚I/O直流电压值说明
CLKINVIH:4.5~5.0
VIL:0.5V max
CLK = 50~425 Hz
Duty 50%
FGOUTVIH:4.5~5.0
VIL:0.5V max
FG(Hz)= SPEED[r/min] * 15/60
BRKINVIH:4.5~5.0
VIL:0.5V max
Hi:BREAK ON (Motor Stop)
Low:BREAK OFF (Motor Start)

电源和I/O引脚输入步骤:

开机:5V on > 24V on > CLK

关机:BRK输入Hi > CLK信号输入Low > 24V off > 5V off

风扇电机驱动部分代码

/**
* @brief: _tuya_motor_start
* @desc: start motor to control fan speed
* @param[in] percent:set fan speed percent, 0.2~1
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
STATIC OPERATE_RET _tuya_motor_start(IN CONST FLOAT_T percent)
{
    OPERATE_RET op_ret = OPRT_OK;
    FLOAT_T speed_percent = 0;

    speed_percent = percent;

    //Check the input parameter, percent: 0.2~1
    if (speed_percent < 0.2) {
        speed_percent = 0.2;
    }
    if (speed_percent > 1) {
        speed_percent = 1;
    }

    /* Break pin off */
    tuya_gpio_write(MOROR_BRK_PIN, TRUE);

    /* Start motor pwm */
    op_ret = tuya_motor_pwm_start(430 * speed_percent); //430hz is max frequency
    if (op_ret != OPRT_OK) {
        PR_ERR("tuya_motor_pwm_start error:%d", op_ret);
        return op_ret;
    }

    return op_ret;
}

/**
* @brief: tuya_motor_stop
* @desc: stop motor
* 
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
OPERATE_RET tuya_motor_stop(VOID_T)
{
    OPERATE_RET op_ret = OPRT_OK;

    /* Break pin on */
    tuya_gpio_write(MOROR_BRK_PIN, FALSE);

    /* stop motor pwm */
    op_ret = tuya_pwm_stop(p_pwm_motor);
    if (op_ret != OPRT_OK) {
        PR_ERR("stop motor pwm error:%d", op_ret);
        return op_ret;
    }

    /* set g_fan_speed_percent value*/
    g_fan_speed_percent = 0;

    PR_DEBUG("ali, tuya_motor_stop suc");
    return op_ret;
}
4.2 应用功能
4.2.1 空气指标获取

向空气质量传感器发送命令,然后读取UART数据缓存,解析得到空气质量指标。

空气指标获取部分代码:

CONST UCHAR_T uart_buf_tx[4] = {0x11, 0x01, 0x16, 0xD8};

/**
* @brief: tuya_get_air_quality_index
* @desc: get air quality index
* 
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
STATIC OPERATE_RET tuya_get_air_quality_index(VOID_T)
{
    OPERATE_RET op_ret = OPRT_OK;
    UINT_T tmp_len = 0, read_len = 0, read_time = 0;
    UCHAR_T uart_buf_rx[UART_BUFSZ];
    UINT_T cs_check_num = 0;

    /* write command 11 01 16 D8 */
    tuya_uart_write(p_uart0, uart_buf_tx, sizeof(uart_buf_tx));

    /* read the return data */
    do {
        tmp_len = tuya_uart_read(p_uart0, &uart_buf_rx[read_len], UART_BUFSZ - read_len);
        read_len += tmp_len;
        if (read_time++ > 20) {
            PR_ERR("uart no return data");
            return OPRT_COM_ERROR;
        }
        tuya_hal_system_sleep(10);
    } while (read_len < UART_BUFSZ);

    /* check frame header */
    if ((uart_buf_rx[0] != 0x16) || (uart_buf_rx[1] != 0x13) || (uart_buf_rx[2] != 0x16)) {
        PR_ERR("uart receive data error, frame header");
        return OPRT_COM_ERROR;
    }

    /* check cs check num */
    cs_check_num = 256 - tuya_get_check_sum(uart_buf_rx, UART_BUFSZ-1);
    if (uart_buf_rx[UART_BUFSZ-1] != cs_check_num) {
        PR_ERR("uart receive data error, cs check num");
        return OPRT_COM_ERROR;
    }

    /* set air quality index value */
    g_air_quality_index.pm25 = uart_buf_rx[9]*256 + uart_buf_rx[10];
    g_air_quality_index.pm10 = uart_buf_rx[11]*256 + uart_buf_rx[12];
    g_air_quality_index.tvoc = (uart_buf_rx[3]*256 + uart_buf_rx[4]);  //ppb,alitest
    g_air_quality_index.temperature = (uart_buf_rx[13]*256 + uart_buf_rx[14] - 500)/10;
    g_air_quality_index.humidity = (uart_buf_rx[15]*256 + uart_buf_rx[16])/10;

    return op_ret;
}
4.2.2 风扇自动调节

风扇自动调节规则:

PM2.5(ug/m3)空气质量风扇转速
115+污染保持最大风速运行,对应最大占空比,同时主控监测PM2.5的变化,如果一直在 115+,则一直最高档运行。
75 - 115微污染80% 风速运行,同时检测PM2.5的变化,每隔 30s 检测一次PM2.5的变化数值,如果下降小于 1,则切换最大风速运行,在PM2.5到达 75 下之前,保持最大风速。
35 - 7560% 风速运行,同时检测PM2.5的变化,30s 内数值下降小于 1,则切换为80% 风速,下个 30s 数值下降仍小于 1,则切换 100% 风速(如果大于 1,则保持 80% ),之后保持 100% 风速,直到pm2.5降到 35 以内。
0 - 3540% 风速运行,同时检测PM2.5变化,30s 数值如果下降超过1,则维持风速运行;如果数值不降反升(超过 35 ),则切换为上述工作模式。

滤芯寿命降低导致净化效果衰减,通过风速提升进行补偿:

80-100%, 按上述规则

50-80%, 风速提升10%

20-50%,风速提升20%

0-20%,风速提升30%

风扇自动调节部分代码:

/**
* @brief: tuya_auto_fan_speed_start
* @desc: start auto fan speed
* @param[in] start:
*             TRUE: start
*             FLASE: stop
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
OPERATE_RET tuya_auto_fan_speed_start(IN CONST BOOL_T start)
{
    OPERATE_RET op_ret = OPRT_OK;

    if (FALSE == start) {
        tuya_pm25_scan_timer_stop();
        PR_DEBUG("stop auto fan speed");
        /* release flag */
        compare_flag.pm25_75_115 = FALSE;
        compare_flag.pm25_35_75 = FALSE;
        compare_flag.pm25_35_75_again = FALSE;
        motor_already_stop = FALSE;
        return op_ret;
    }

    /* release time */
    g_pm25_good_time = 0;

    /* scan pm2.5 value once */
    PR_DEBUG("start auto fan speed");
    tuya_pm25_scan();

    /* cycle scan pm2.5, 30s */
    tuya_pm25_scan_timer_start();

    return op_ret;
}

/**
* @brief: tuya_pm25_scan
* @desc: scan pm2.5 value once
* 
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
OPERATE_RET tuya_pm25_scan(VOID_T)
{
    OPERATE_RET op_ret = OPRT_OK;
    PM25_VALUE_RANGE_E pm25_value_range;
    FLOAT_T speed_percent = 0;
    UINT_T pm25_new = 0;

    pm25_new = g_air_quality_index.pm25;
    g_pm25_reduction = g_pm25_last - pm25_new;
    g_pm25_last = pm25_new;

    /* Check whether to into the dark sleep mode */
    if (TRUE == stop_auto_speed_flag) {
        return op_ret;
    }

    /* Check whether to into the environmental mode */
    if (pm25_new < 35) {
        //hold for 2 hours, 240*30 s
        if (g_pm25_good_time++ >= 240) {
            if (g_pm25_good_time > 1000) {
                g_pm25_good_time = 1000;
            }
            if (FALSE == motor_already_stop) {
                tuya_motor_stop();
                motor_already_stop = TRUE;
            }
            return op_ret;
        }
    } else {
        g_pm25_good_time = 0;
        motor_already_stop = FALSE;
    }

    /* Check pm2.5 value range */
    if (pm25_new >= 115) {
        pm25_value_range = PM25_RANGE_115;
    } else if (pm25_new >= 75) {
        pm25_value_range = PM25_RANGE_75_115;
    } else if (pm25_new >= 35) {
        pm25_value_range = PM25_RANGE_35_75;
    } else {
        pm25_value_range = PM25_RANGE_35;
    }

    /* control fan speed */
    switch (pm25_value_range)
    {
        /* pm2.5: 115+ */
        case PM25_RANGE_115:
            speed_percent = 1;
            break;
        /* pm2.5: 75-115 */
        case PM25_RANGE_75_115:
            if (TRUE == compare_flag.pm25_35_75_again) {
                speed_percent = 1;
            } else {
                speed_percent = 0.8;
            }

            if (TRUE == compare_flag.pm25_75_115) {
                speed_percent = tuya_pm25_compare_75_115();
            } else {
                compare_flag.pm25_75_115 = TRUE;
            }
            break;
        /* pm2.5: 35-75 */
        case PM25_RANGE_35_75:
            compare_flag.pm25_75_115 = FALSE;
            speed_percent = 0.6;
            if (TRUE == compare_flag.pm25_35_75) {
                speed_percent = tuya_pm25_compare_35_75();
                compare_flag.pm25_35_75 = FALSE;
            } else if ((TRUE == compare_flag.pm25_35_75_again)) {
                speed_percent = tuya_pm25_compare_35_75_again();
            } else {
                compare_flag.pm25_35_75 = TRUE;
            }
            break;
        /* pm2.5: 0-35 */
        case PM25_RANGE_35:
            compare_flag.pm25_75_115 = FALSE;
            compare_flag.pm25_35_75_again = FALSE;
            speed_percent = 0.4;
            break;  
        default:
            break;
    }

    /* start fan motor */
    op_ret = tuya_motor_start(speed_percent);
    if (OPRT_OK != op_ret) {
        PR_ERR("tuya_motor_start error:%d", op_ret);
        return op_ret;
    }  

    return op_ret;
}
4.2.3 滤芯NFC检测

NFC读卡器芯片为 MFRC522,滤芯上的NFC卡片类型为 NXP MIFARE Ultralight,内存 180 bytes,45 页(4 bytes 每页)。

NFC读卡器的步骤:寻卡 > 防冲撞 > 选卡 > 校验密码 > 读写卡片。Mifare Ultralight 类型卡片没有密码认证(Mifare One 类型卡片支持密码认证),所以使用 Mifare Ultralight 类型卡片的话就可以跳过校验密码步骤。

滤芯NFC卡片信息数据协议定义如下表:

内存读写地址长度(byte)说明
12页4Data[0]:标识,0x74,”t“,表示 tuya。
Data[1]:Type,滤芯的类型。0x00 表示标准,0x01 表示抗菌,0x02 表示除醛, 0x03 表示专业。
Data[2]:滤芯ID,高位
Data[3]:滤芯ID,低位
13页4Data[0]:生产年份,0x00 表示 2000 年
Data[1]:生产月份,从 1 开始到 12 结束
Data[2]:生产日份,从 1 开始到 31 结束
Data[3]:保留

滤芯NFC检测部分代码:

/**
* @brief: tuya_nfc_detect_filter
* @desc: nfc detect to read filter message
* 
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
STATIC OPERATE_RET tuya_nfc_detect_filter(VOID_T)
{
    OPERATE_RET op_ret = OPRT_OK;
    UCHAR_T i = 0;
    UCHAR_T page = 0;
    UCHAR_T read_buf[16] = {0};
    UCHAR_T TagType[2];
    UCHAR_T SelectedSnr[4];

    /* mfrc522 reset, and open antenna */
    PcdReset();
	PcdAntennaOff(); 
	PcdAntennaOn(); 

    /* look for card */
    op_ret = PcdRequest(0x52, TagType);
    if (OPRT_OK != op_ret) {
        PR_INFO("PcdRequest err:%d", op_ret);
        PR_INFO("Do not find NFC Card, filter do not install");
        return op_ret;
    }

    /* printf the type of card */
    PR_DEBUG_RAW("TagType: 0x");
    for (i = 0; i < 2; i++) {
        PR_DEBUG_RAW("%02X", TagType[i]);
    }
    PR_DEBUG_RAW("\n");

    /* card collision protection */
    op_ret = PcdAnticoll(SelectedSnr);
    if (OPRT_OK != op_ret) {
        PR_INFO("PcdAnticoll err:%d", op_ret);
        PR_INFO("Do not find NFC Card, filter do not install");
        return op_ret;
    }

    /* select card */
    op_ret = PcdSelect(SelectedSnr);
    if (OPRT_OK != op_ret) {
        PR_INFO("PcdSelect err:%d", op_ret);
        PR_INFO("Do not find NFC Card, filter do not install");
        return op_ret;
    }

    /* read card */
    PR_DEBUG("Find NFC Card, the filter is install ok");
    PR_DEBUG_RAW("Read NFC Card data:\n");
    for (page = 0; page < 45; page +=4) {
        //read
        op_ret = PcdRead(page, read_buf);
        if (OPRT_OK != op_ret) {
            PR_ERR("PcdRead err:%d, page:%d~%d", op_ret, page, (page + 3));
        }
        //get filter message
        if (CARD_READ_PAGE_ADDR == page) {
            memset(filter_buf, 0x00, CARD_READ_BYTE_LEN);
            for (i = 0; i < CARD_READ_BYTE_LEN; i++) {
                filter_buf[i] = *(read_buf + i);
            }
        }
        //printf read buf
        PR_DEBUG_RAW("  ");
        for (i = 0; i < 16; i++) {
            PR_DEBUG_RAW("%02X ", read_buf[i]);
        }
        PR_DEBUG_RAW("----- page: %d~%d\n", page, (page + 3));     
    }
    PR_DEBUG_RAW("\n");

    op_ret = tuya_analyze_CardData_to_get_filter_msg();
    if (OPRT_OK != op_ret) {
        return op_ret;
    }

    return op_ret;
}

/**
* @brief: tuya_analyze_CardData_to_get_filter_msg
* @desc: analyze CardData to get filter msg
* 
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
STATIC OPERATE_RET tuya_analyze_CardData_to_get_filter_msg(VOID_T)
{
    OPERATE_RET op_ret = OPRT_OK;
    UCHAR_T i = 0;

    /* check filter_buf */
    if (NULL == filter_buf) {
        PR_ERR("filter_buf is error");
        return OPRT_INVALID_PARM;
    }

    /* printf filter buf */
    PR_DEBUG_RAW("filter buf data: ");
    for (i = 0; i < CARD_READ_BYTE_LEN; i++) {
        PR_DEBUG_RAW("%02X ", filter_buf[i]);
    }
    PR_DEBUG_RAW("\n");

    /* check if filter is piratic */
    if (CARD_READ_HEAD_TAG != filter_buf[0]) {
        PR_INFO("Do not have tuya tag, filter is piratic");
        return OPRT_COM_ERROR;
    }

    /* get filter type */
    switch (filter_buf[1]) {
    case FILTER_STANDARD:
        g_filter_card_msg.type = FILTER_STANDARD;
        PR_DEBUG_RAW("filter type: standard\n");
        break;
    case FILTER_ANTIBACTERIAL:
        g_filter_card_msg.type = FILTER_ANTIBACTERIAL;
        PR_DEBUG_RAW("filter type: antibacterial\n");
        break;
    case FILTER_ALDEHYDE_REMOVAL:
        g_filter_card_msg.type = FILTER_ALDEHYDE_REMOVAL;
        PR_DEBUG_RAW("filter type: aldehyde removal\n");
        break;
    case FILTER_PROFESSIONAL:
        g_filter_card_msg.type = FILTER_PROFESSIONAL;
        PR_DEBUG_RAW("filter type: professional\n");
        break;
    default:
        PR_INFO("filter type buf data is erro, maybe filter is piratic");
        return OPRT_COM_ERROR;
        break;
    }
    
    /* get filter ID */
    g_filter_card_msg.ID = filter_buf[2] * 256 + filter_buf[3];
    PR_DEBUG_RAW("filter ID: %d\n", g_filter_card_msg.ID);

    /* get filter product date */
    g_filter_card_msg.product_date.year  = filter_buf[4] + 2000;
    g_filter_card_msg.product_date.month = filter_buf[5];
    g_filter_card_msg.product_date.day   = filter_buf[6];
    PR_DEBUG_RAW("filter product date: %d-%d-%d\n", filter_buf[4]+2000, filter_buf[5], filter_buf[6]);

    /* DP report */
    tuya_update_single_dp(FILTER_TYPE_DPID, PROP_ENUM, g_filter_card_msg.type);
    /* check whether the filter is new */
    tuya_check_whether_the_filter_is_new();

    return op_ret;
}
4.2.4 滤芯寿命计算

(1)滤芯剩余寿命算法:

说明:

a、根据公式,只要知道当前滤芯粉尘的吸附量和pm2.5颗粒浓度,就可以算出当前粉尘吸附量;

b、每1分钟计算一次粉尘吸附量并累加;

c、当空气净化器收到关机命令时或者每过24h,系统会将此前累加的粉尘吸附量存入flash,下次开机可以继续累加;

d、滤芯寿命根据每周粉尘累加的吸附量上报一次。

**注:**若设备发生断电情况,可能会产生0~1天的误差。

(2)滤芯剩余可用天数算法:

粉尘吸附量累加一周后,就可以得到粉尘吸附的速度,根据粉尘吸附的速度和滤芯剩余的寿命,就可以估算出滤芯剩余可用天数。

滤芯寿命计算部分代码:

/**
* @brief: tuya_filter_1minute_task
* @desc: filter 1minute task
* 
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
STATIC VOID_T tuya_filter_1minute_task(VOID_T)
{
    STATIC UINT_T uv_close_time = 240;
    STATIC UINT_T s_tick;

    while(1) 
    {
        UINT_T fan_speed_percent = 0;
        
        /* check whether air cleaner power on */
        if (POWER_ON == tuya_get_power_status()) {
            //get current fan_speed
            fan_speed_percent = tuya_get_fan_speed_percent();
            //figure pm_total
            g_filter_pm_total = g_filter_pm_total + (FLOAT_T)fan_speed_percent/100 * FILTER_FIGURE_CONST;
            g_filter_msg.pm_total = g_filter_msg.pm_total + (FLOAT_T)fan_speed_percent/100 * FILTER_FIGURE_CONST;

            //DP report, pm_total
            tuya_update_single_dp(PM_TOTAL_DPID, PROP_VALUE, g_filter_msg.pm_total/1000);  //ug -> mg
            //DP report, runtime_total
            tuya_update_single_dp(RUNTIME_TOTAL_DPID, PROP_VALUE, ++g_runtime_total);  //runtime_total add
        }

        /* enter every 60 minutes, 1hour */
        if (s_tick++ >= 60){
            s_tick = 0;
            tuya_filter_1hour_task();
        }

        /* if uv close time more than 240min, scan for uv funtion */
        if (FALSE == tuya_get_uv_status) {
            if (uv_close_time++ >= 240) {
                tuya_uv_funtion_scan();
                if (uv_close_time > 1000) {
                    uv_close_time = 1000;
                }
            }
        } else {
            uv_close_time = 0; 
        }

        tuya_hal_system_sleep(FILTER_TASK_DELAY_TIME); //1min   
    }
}

/**
* @brief: tuya_filter_1hour_task
* @desc: filter 1hour task
* 
* @return OPERATE_RET  OPRT_OK is sucess, other is fail
*/
STATIC VOID_T tuya_filter_1hour_task(VOID_T)
{
    POSIX_TM_S local_time;
    UINT_T now_days = 0;

    uni_local_time_get(&local_time);
    now_days = local_time.tm_year*365 + local_time.tm_mon*30 + local_time.tm_mday;

    if (g_now_days == 0) {
        g_now_days = now_days;
    }

    /* if days pass */
    if (g_now_days < now_days) {
        /* figure filter_use_days */
        g_filter_use_days = g_filter_use_days + (now_days - g_now_days);
        /* figure air cleaner run_days */
        g_run_days = g_run_days + (now_days - g_now_days);

        /* if air cleaner run_days more than 7 days */
        if (g_run_days >= 7) {
            //figure filter_life
            g_filter_msg.filter_life = (1 - (FLOAT_T)g_filter_pm_total / FILTER_MAX_PM_CAN_ABSORB) * 100;
            //figure filter_days
            if (g_filter_pm_total == 0) {
                g_filter_msg.filter_days = 180;
            } else {
                g_filter_msg.filter_days = ((FILTER_MAX_PM_CAN_ABSORB * g_filter_use_days) / g_filter_pm_total) - g_filter_use_days;
            }
            
            //DP report
            if (g_filter_msg.filter_days > 1000) {
                g_filter_msg.filter_days = 1000;
            }
            tuya_update_single_dp(FILTER_DAYS_DPID, PROP_VALUE, g_filter_msg.filter_days);  //filter_days
            tuya_update_single_dp(FILTER_LIFE_DPID, PROP_VALUE, g_filter_msg.filter_life);  //filter_life

            g_run_days = 0;
        }

        g_now_days = now_days;

        /* write save data in flash */
        tuya_write_save_data_in_flash();
    }
}
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
空气净化器电路原理图PCB图源程程序 ⑴.接通电源后,机子没有显示; ⑵.按下遥控器上的“电源开关”按键后:显示板上橙色灯亮,CN4(M FAN)通电,电机按照中速运行;电源板上CN5、CN6、CN7、CN8通电,4个摆风电机M2运行,摆风叶片摆动;电源板上CN2通电,摆风电机M1运行;电源板上CN3通电,负离子运行;再按下遥控器上的“电源开关”按键后,机子恢复之前状态。 ⑶.按下遥控器上“低”按键后,电机低速运行,绿色LED灯亮。按下遥控器上“中”按键后,电机中速运行,橙色LED灯亮。按下遥控器上“高”按键后,电机高速运行,红色LED灯亮。 ⑷.按下遥控器上的“摆风”按键后,电源板上的M1和4个M2断电,摆风停止。再按下遥控器上的“摆风”按键后,机子恢复之前状态。 ⑸.按下遥控器上的“负离子”按键后,CN3断电,负离子停止工作。再按下遥控器上的“负离子”按键后,机子恢复之前状态。 ⑹.CN4电源所用电机为:三速电机,功率为100W,输入电源AC110-127V/60HZ或者AC220-240V/50-60HZ。 ⑺.CN2电源所用电机M1为:步进电机,功率为10W,输入电源AC110-127V/60HZ或者AC220-240V/50-60HZ。 ⑻.CN3电源所用负离子为:功率小于5W(要求按照100W功率来设置,以后可以考虑用来做其他功能的电源),输入电源AC110-127V/60HZ或者AC220-240V/50-60HZ。 ⑼.CN5,CN6,CN7,CN8电源所用M2电机为:步进电机,型号为:28BYJ46,输入电源为12VDC。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值