涂鸦智能暖风机软件实现之利用B3950实现温度采集功能
Posted 三明治开发社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了涂鸦智能暖风机软件实现之利用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去查看室内的温度,智能暖风机其他模块的开发将在后续文章推出。
技术支持
您可以通过以下方法获得涂鸦的支持:
以上是关于涂鸦智能暖风机软件实现之利用B3950实现温度采集功能的主要内容,如果未能解决你的问题,请参考以下文章