智能暖风机——3.利用B3950实现温度采集功能

前言

前面我们已经创建好产品并建立起app端和设备端的连接,接下来我们将以模块化的思想去实现智能暖风机的各个功能,本文主要内容是实现温度采集功能,传感器采用B3950热敏电阻。

一、智能暖风机的整体功能设定

  • 智能暖风机的设定功能如下,我们将整个暖风机拆分成几个模块,逐个实现功能;本文实现暖风机的温度采集功能,温度信息主要用于当前室温的显示以及档位的温控调节。
功能说明
开关

触摸按键:1个

按键控制,app控制

控制暖风机开关。

目前开发的开启有三种方式:

1.App面板控制

2:按键控制

3:定时控制

模式

触摸按键:1个

按键控制,app控制

1:风扇功能:只吹风,不制热。

2:1档加热:风扇+加热1

3:2档加热:风扇+加热2(档位更高)

定时

触摸按键:1个

按键控制,app控制

倒计时默认枚举值有cancel, 1h, 2h, 3h, 4h, 5h, 6h,7h, 8h。

倒计时功能针对暖风机开关。

app暖风机倒计时剩余时间。

灯光

触摸按键:1个

按键控制,app控制

4种照明模式: 1 rgb1 2 rgb2 3 rgb3 4 rgb4

设温

触摸按键:1个

按键控制,app控制

默认温度设置区间为15-40℃,客户可以自行设定温度。

温度显示

硬件:断码显示

按键控制,app控制

只是在设定的时候显示

设备上和当前温度显示复用,app单独显示

设备:温度设定时,显示设定温度,误操作3秒后显示当前室温

摇头

触摸按键:1个

按键可控制,app可控制。

开/关

待机记忆

按键,app,主动操作开关键关机为待机状态。

再开启后恢复上一次设置:

温度设置:上一次设置

温度显示:当前环境温度

灯光模式:上一次设置

设备定时关:默认关闭

app定时关:默认关闭。

app定时开:上一次设置。

断电记忆

断电后为断电状态,再上电恢复上一次设置:

开关状态:默认关

温度设置:上一次设置

温度显示:当前环境温度

灯光模式:上一次设置

设备定时关:默认关闭

app定时关:默认关闭。

app定时开:上一次设置。

二、温度采集的方案制定


1.温度采集原理

本案例中温度采集方案是使用热敏电阻,热敏电阻在不同温度下有不同的阻值,根据此特性,通过电路设计和软件程序配合采集到热敏电阻的阻值,从而计算出当前的温度值。
采样电路图如下:R1为10k的定值电阻,CN1为热敏电阻(B3950),ADC是电压采样点,采集电压后,根据欧姆定律即可算出热敏电阻的阻值。
在这里插入图片描述

  • 得到热敏电阻阻值Rt,根据B3950的热敏曲线即可算出当前温度值。

在这里插入图片描述

上图是该系列热敏电阻的热敏曲线

上述参数含义
R0:25℃下的电阻阻值  本文中选用的热敏电阻在25℃阻值为10k
R:当前温度下电阻的阻值
T0:开尔文温度(273.15+25)
T:开尔文温度(273.15+当前摄氏度温度)
B:	热敏特性常数
exp:e^n(e的n次方)
通过采样电路采样热敏电阻两端的电压,从而计算出R的阻值,再根据R的阻值计算当前的温度
R=R0 expB (1/T-1/T0) 
		||
	    \/
T=1/(ln(R/R0)/B+1/T0)近似为T=1/(log(R/R0)/B+1/T0)
得到的T为开尔文温度,换算成摄氏度即
temp = T - 273.15

测温原理已经知道了,接下来是软件实现阶段。

2.软件方案设定

上一篇文章我们对现有的tuya_demo_template工程进行了修改,实现了一个基础的嵌入式框架,现在我们在此基础上进行开发,实现温度采集功能。

  • 所用的SOC引脚图如下:

在这里插入图片描述

  • 温度采集嵌入式方案设定

根据上面所述的测温原理可以知道,我们需要使用AD采集功能,测量出热敏电阻的两端电压;故我们使用所选SOC自带的ADC采样功能进行电压采集,此SOC自带12位精度的ADC,可以满足我们的采样精度。
得到电压后,按照上述原理所推出的公式实现C语言代码,计算出当前温度值。

三、功能实现

1.代码实现

  • 将上一篇基于tuya_demo_template实现的嵌入式demo改名为calorifier(工程名可以自己定义),建立几个文件,b3950.c 、tuya_thread.c、timer.c,最后的文件组成如下:
├── src	
|    ├── tuya_drive
|    |  	└── b3950
|    |    		└── b3950.c           //温度传感器驱动相关
|    |  	└── timer
|    |    		└── timer.c           //定时器相关
|    ├── tuya_device.c             //应用层入口文件
|    ├── tuya_thread.c             //主要线程处理文件
|    └── tuya_dp_process.c            //dp数据触发的主要应用文件
|
├── include				//头文件目录
|    ├── tuya_drive_h
|    |  	└── b3950_h
|    |    		└── b3950.h        
|    |  	└── timer_h
|    |    		└── timer.h           //定时器相关   
|    ├── tuya_device.h           
|    ├── tuya_thread.h       
|    └── tuya_dp_process.h       
|
└── output              //编译产物

其中b3950.c文件中的内容主要是实现温度采集的代码,tuya_thread.c 文件中主要是创建线程以及线程管理的一系列函数,timer.c函数中是定时器的相关代码。

timer.c代码及说明如下:

/*定时器0做为时基定时器,每次间隔为10s,每10s计数一次作为开机时间的记录*/
#include "timer.h"
#include "tuya_device.h"
#include "tuya_cloud_error_code.h"
#include "b3950.h"
#include "tuya_dp_process.h"
#include <string.h>
#include "uni_time.h"
#include "uni_log.h"
static tuya_timer_t *timer = NULL;
uint32_t timercount = 0;
void timer_init(void)
{
    timer = (tuya_timer_t *)tuya_driver_find(TUYA_DRV_TIMER, TUYA_TIMER0);
    //The timer uses cycle mode
    TUYA_TIMER_CFG(timer, TUYA_TIMER_MODE_PERIOD, tuya_timer0_cb, NULL);
    tuya_timer_init(timer);
    //Start the timer, 1ms is a counting cycle
    tuya_timer_start(timer, 1000);

}
void timer_stop(void)
{
    tuya_timer_stop(timer);

}
void tuya_timer0_cb(void *arg)
{
    static uint32_t s_tick;
    
    //10000 * 1ms = 10s
    if (s_tick++ >= 10000) {
        s_tick = 0;
        timercount++;
        //Transmitting semaphore Temperature acquisition is performed every 10 seconds
        tuya_hal_semaphore_post(g_temper_binsemap);
    }
}

b3950.c代码及说明如下:

/* Private includes ----------------------------------------------------------*/
#include "b3950.h"
#include "uni_log.h"
#include "tuya_dp_process.h"
/* Private variables ---------------------------------------------------------*/
tuya_adc_t *temper_adc;
UINT16_T adc_buf = 0;
float volt = 0;

/**
 * @Function: b3950_init
 * @Description: b3950-temperature-sensor init
 * @Input: none
 * @Output: none
 * @Return: none
 * @Others: 
 */
void b3950_init(void)
{
    /*create adc device,get handle*/
    temper_adc = (tuya_adc_t *)tuya_driver_find(TUYA_DRV_ADC, TUYA_ADC2);
    /*adc_dev cfg*/
    TUYA_ADC_CFG(temper_adc, TUYA_ADC2, 0);
    /*adc_dev init*/
    tuya_adc_init(temper_adc);
}
/**
 * @Function: cur_temper_get
 * @Description: current temperature get
 * @Input: none
 * @Output: none
 * @Return: temperature value
 * @Others: 
 */
int  cur_temper_get()
{
    float Rt = 0;
    float Rp = 10000;
    float T2 = 273.15 + 25;
    float Bx = 3950;
    float Ka = 273.15;
    int temp = 0;
    /*Collect AD data and store it in adc_buffer*/
    tuya_adc_convert(temper_adc, &adc_buf, 1);
    /*req_val(0-4096) - V(0-2.4)*/
    volt = (float)adc_buf *2.4/ 4096;
    //volt = adc_buf;
    Rt = volt*10000/(3.3 - volt);
    temp = (int)(1/(1/T2+log(Rt/Rp)/Bx)-Ka+0.5);
    PR_DEBUG("volt:%f", volt);
    return temp;


}

tuya_thread.c代码及说明如下:上报温度信息的线程中每10秒采集一次温度信息

#include "tuya_thread.h"
#include "uni_thread.h"
#include "tuya_cloud_error_code.h"
#include "tuya_device.h"
#include "b3950.h"
#include "tuya_dp_process.h"
#include "tuya_iot_wifi_api.h"
#include "timer.h"
#include "uni_log.h"

void update_temperature_thread(void);

void thread_init(void)
{
    int rt = OPRT_OK;

    rt = tuya_hal_thread_create(NULL, "update_dp_thread", 512*4, TRD_PRIO_4, update_temperature_thread, NULL);
    if (rt != OPRT_OK) {
        PR_ERR("Create update_dp_thread error!: %d", rt); 
        return;
    }
    
}
/**
 * @Function: update_temperature_thread
 * @Description: 上传温度信息到APP端
 * @Input: none
 * @Output: none
 * @Return: none
 * @Others: 
 */
void update_temperature_thread(void)
{
    static int last_temper = 0;
    while(1)
    {
        tuya_hal_semaphore_wait(g_temper_binsemap);
        last_temper = cur_temper_get();
        //只上传有效的温度值
        if(last_temper > 50)
        {
            last_temper = 50;
        }
        else if(last_temper < -20)
        {
            last_temper = -20;
        }
        temper_s.value = last_temper;
        report_one_dp_status(DP_TEMPER);//上传温度dp数据到app
    }
}

tuya_dp_process.c中添加单个dp数据上报函数以及设备初始化函数:

/*calorifier init function*/
VOID_T calorifier_init()
{
    b3950_init();
    thread_init();
    timer_init();
    thread_init();
}
/*update the dp data*/
VOID_T report_one_dp_status(int dp_id)
{
    
    OPERATE_RET op_ret = OPRT_OK;
    GW_WIFI_NW_STAT_E wifi_state = 0xFF;
    op_ret = get_wf_gw_nw_status(&wifi_state);
    if (OPRT_OK != op_ret) {
        PR_ERR("get wifi state err");
        return;
    }
    if (wifi_state <= STAT_AP_STA_DISC || wifi_state == STAT_STA_DISC) {
        return;
    }
    TY_OBJ_DP_S *dp_arr = (TY_OBJ_DP_S *)Malloc(SIZEOF(TY_OBJ_DP_S));
    if(NULL == dp_arr) {
        PR_ERR("malloc failed");
        return;
    }
    memset(dp_arr, 0, SIZEOF(TY_OBJ_DP_S));

    switch (dp_id){
    case DP_SWITCH:
        {
        dp_arr[0].dpid = switch_s.dp_id;
        dp_arr[0].type = PROP_BOOL;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_bool = switch_s.power;

        }
        break;

    case DP_TEMPER:
        {
        dp_arr[0].dpid = temper_s.dp_id;
        dp_arr[0].type = PROP_VALUE;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_value = temper_s.value;
        }
        break;

    case DP_SHAKE:
        {
        dp_arr[0].dpid = shake_s.dp_id;
        dp_arr[0].type = PROP_BOOL;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_bool = shake_s.power;
        }
        break;

    case DP_MODE:
        {
        dp_arr[0].dpid = mode_s.dp_id;
        dp_arr[0].type = PROP_ENUM;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_enum = mode_s.value;
        }
        break;

    case DP_LED:
        {
        dp_arr[0].dpid = led_s.dp_id;
        dp_arr[0].type = PROP_ENUM;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_enum = led_s.value;
        }
        break;

    case DP_SET_TEMP:
        {
        dp_arr[0].dpid = set_temper_s.dp_id;
        dp_arr[0].type = PROP_VALUE;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_value = set_temper_s.value;
        }
        break;

    case DP_TIME_ON:
        {
        dp_arr[0].dpid = time_to_open_s.dp_id;
        dp_arr[0].type = PROP_BOOL;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_bool = time_to_open_s.power;
        }
        break;

    case DP_TIME_OFF:
        {
        dp_arr[0].dpid = time_to_close_s.dp_id;
        dp_arr[0].type = PROP_BOOL;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_bool = time_to_close_s.power;
        }
        break;
    case DP_SHUTDOWN_TIME:
        {
        dp_arr[0].dpid = shutdown_time_s.dp_id;
        dp_arr[0].type = PROP_VALUE;
        dp_arr[0].time_stamp = 0;
        dp_arr[0].value.dp_value = shutdown_time_s.value;
        }
        break;
    default:
    
        break;
    }

    op_ret = dev_report_dp_json_async(NULL , dp_arr, 1);
    Free(dp_arr);
    dp_arr = NULL;
    if(OPRT_OK != op_ret) {
        PR_ERR("dev_report_dp_json_async relay_config data error,err_num",op_ret);
    }
}
  • 此时一个温度采集上报的功能就已经实现了,可以通过app去查看室内的温度,智能暖风机其他模块的开发将在后续文章推出。

技术支持

您可以通过以下方法获得涂鸦的支持:

涂鸦IoT智能平台

Demo中心

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值