在本项目中,采集模块是最核心的模块,也就是所有前端数据的来源。由于简化复杂度便于理解,项目中仅使用了温湿度的传感数据,而板载资源还有AP3216C 接近与光强传感器与红外传感器等,甚至通过板子引出的排针,还可以外接各种各样的传感器,实现更多种类、更多样化的监测功能。虽然传感器的种类、功能多种多样,但其用在实际工程中时的逻辑都是大同小异的,理解了一个,其他的也就不在话下,因此本章我们着重讲解温湿度传感器的源码。
目录
AHT10软件包的使用
采集模块源码详解
模块结构体
模块函数简介
模块函数详解
aht10_module_init
aht10_thread_entry
aht10_device_init
aht10_temp_get
aht10_humi_get
json_create_aht10_current_data
json_create_aht10_saved_data
温湿度传感器型号为AHT10,查看软件包链接。
软件包中包含了AHT10的驱动,完美对接了RTT设备框架,将AHT10注册为了RTT的Sensor设备,这使得我们无需关注对AHT10芯片的底层驱动,仅需调用RTT提供的Sensor设备操作API即可。在本项目中,所用到的API有如下:
rt_device_find() //获取设备句柄
rt_device_open() //打开设备
rt_device_read() //读取数据
需要注意的是,RTT设备框架所支持的API也不仅仅这三个,你可以使用更多其他API实现更多的操作,具体的设备框架说明在以下链接中,感兴趣的可以点击此处查看。
除了以上提到的三个接口,要想使用AHT10软件包还有一个前提工作,就是进行硬件层初始化。该初始化操作位于/W601_APP/ports/sensor路径下的 sensor_port.c源文件中:
#include
#ifdef BSP_USING_AHT10
#include "sensor_asair_aht10.h"
#define AHT10_I2C_BUS "i2c1soft"
int rt_hw_aht10_port(void)
{
struct rt_sensor_config cfg;
cfg.intf.dev_name = AHT10_I2C_BUS;
cfg.intf.user_data = (void *)AHT10_I2C_ADDR;
rt_hw_aht10_init("aht10", &cfg);
return RT_EOK;
}
INIT_ENV_EXPORT(rt_hw_aht10_port);
#endif
在上述代码主要完成了以下操作:
1. 设备配置和初始化(根据传入的配置信息配置接口设备);
2. 注册相应的传感器设备,完成 aht10 设备的注册;
其中 INIT_ENV_EXPORT(rt_hw_aht10_port); 代码是让RTT内核自动调用 rt_hw_aht10_port 函数,实现硬件接口的自动初始化,无需用户调用。有关RTT自动初始化机制,可以点击此处查看。
完成了硬件层的初始化后,我们的应用即可以调用Sensor设备驱动的API对AHT10进行一系列的操作。
采集模块的源代码位于 /W601_APP/module 文件夹下的 aht10_module.c 与 aht10_module.h 两个源文件中。
W601采集模块结构体如下所示:
typedef struct
{
float cur_temp; //当前温度
float cur_humi; //当前湿度
float temp_warn; //温度报警值
float temp_data[24]; //24小时内的温度数据
float humi_data[24]; //24小时内的湿度数据
uint8_t cur_temp_index; //当前写入的温度数组索引
uint8_t cur_humi_index; //当前写入的湿度数组索引
} w601_aht10_t;
cur_temp与cur_humi为当前温湿度存储变量,这两个变量每次采集一次就会变化。temp_warn是温度报警阈值,若温度超过该值同时打开了邮件报警功能,W601就会向指定邮件发送一条报警邮件。temp_data与humi_data为温湿度历史数据数组,这两个数组存储了近24小时之内的温湿度数据。cur_temp_index与cur_humi_index为当前写入的温湿度数组索引,用于标记当前温湿度数组记录到了第几个,该数组主要用于与前端的交互,后文会有详细说明。
函数名 | 函数简介 |
---|---|
aht10_module_init | aht10模块初始化,用于初始化采集线程 |
aht10_thread_entry |
aht10采集主函数 |
aht10_device_init |
aht10设备初始化,用于获取aht10设备句柄 |
aht10_temp_get |
获取aht10温度数据 |
aht10_humi_get |
获取aht10湿度数据 |
json_create_aht10_current_data |
生成实时温湿度数据的json字符串 |
json_create_aht10_saved_data |
生成历史数据json字符串 |
该函数为aht10模块的初始化函数,主要用于初始化采集线程,并且线程的主处理函数为 aht10_thread_entry 。若线程创建成功标志模块初始化的成功,若模块初始化失败则会在串口输出相应的错误提示。
int aht10_module_init(void)
{
rt_thread_t aht10_tid = RT_NULL;
aht10_tid = rt_thread_create("aht10_t", aht10_thread_entry,
NULL, 1024, 15, 5);
if (!aht10_tid)
{
LOG_E("aht10 thread create fail");
return -1;
}
else
{
rt_thread_startup(aht10_tid);
}
return 0;
}
该函数为采集线程的主处理函数,由下方的函数体可以看到,进入该函数时首先会对aht10设备进行初始化,该函数会在下文详细介绍。之后会进入一个死循环,以1秒为周期进行循环采集温湿度数据。函数开头的sec_count变量为小时计数变量,采集函数每循环一个周期便会减一,减到0时为3600个循环即一个小时,此时会将当前采集到的温湿度数据记录到历史数据数组中,且最多存放24组数据,因此历史数据支持的最大时间跨度为24个小时,若已经记录了24组数据,此时再来了新的数据时,会将原数组内的数据整体前移并且去除第一个数据,最后将新数据记录到数组的最后一位。
static void aht10_thread_entry(void *param)
{
int sec_count = 3600; //时间计数
//aht10初始化
aht10_device_init();
while (1)
{
w601_aht10.cur_temp = aht10_temp_get();
w601_aht10.cur_humi = aht10_humi_get();
//LOG_D("temp: %d.%d *C --- humi: %d.%d %%", (int)temp, (int)(temp * 10) % 10, (int)humi, (int)(humi * 10) % 10);
//一个小时
if (sec_count == 3600)
{
sec_count = 1;
//湿度
if (w601_aht10.cur_humi_index < 23)
{
w601_aht10.humi_data[w601_aht10.cur_humi_index++] = w601_aht10.cur_humi;
}
else
{
for (int i = 1; i <= 23; i++)
{
w601_aht10.humi_data[i - 1] = w601_aht10.humi_data[i];
}
w601_aht10.humi_data[23] = w601_aht10.cur_humi;
}
//温度
if (w601_aht10.cur_temp_index < 23)
{
w601_aht10.temp_data[w601_aht10.cur_temp_index++] = w601_aht10.cur_temp;
}
else
{
for (int i = 1; i <= 23; i++)
{
w601_aht10.temp_data[i - 1] = w601_aht10.temp_data[i];
}
w601_aht10.temp_data[23] = w601_aht10.cur_temp;
}
}
sec_count++;
rt_thread_mdelay(1000);
}
}
该函数为aht10设备初始化函数。RTT中对于设备的所有操作都需要通过对应的设备句柄来实现,设备句柄就相当于设备的身份证,有了设备句柄系统才知道要操作哪个设备。由下方函数体可以看到,通过该函数,内核可以获得温湿度的设备句柄temp_dev,humi_dev。并且调用rt_device_open打开对应的设备,使其可以正常工作。
int aht10_device_init(void)
{
LOG_D("Temperature and Humidity Sensor initialize start...");
rt_thread_mdelay(2000);
rt_memset(&w601_aht10, 0, sizeof(w601_aht10_t));
//获取温度设备句柄
temp_dev = rt_device_find(TEMP_DEV);
if (temp_dev == RT_NULL)
{
LOG_E("can not find TEMP device: %s", TEMP_DEV);
return -1;
}
else
{
if (rt_device_open(temp_dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
{
LOG_E("open TEMP device failed!");
return -1;
}
}
//获取湿度设备句柄
humi_dev = rt_device_find(HUMI_DEV);
if (humi_dev == RT_NULL)
{
LOG_E("can not find HUMI device: %s", HUMI_DEV);
return RT_ERROR;
}
else
{
if (rt_device_open(humi_dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
{
LOG_E("open HUMI device failed!");
return RT_ERROR;
}
}
LOG_I("Temperature and Humidity Sensor initialize success !");
return 0;
}
该函数是读取aht10温度数据的函数,RTT设备驱动中读取数据接口统一都是 rt_device_read,而通过我们上面获取到的温度句柄temp_dev,即可将对应的温度数据读取到指定的变量中,在此为temp_dev_data。由于直接读取的数据往往为了便于传输或者便于电路的设计会进行一定的编码,因此读取的数据有时并不能直接使用,我们还需要对其进行对应的转化(在此是将最后一位变为小数位),最终便可获得对人来说比较直观的温度数据。
static float aht10_temp_get(void)
{
float temp_data = 0;
//获取数据
rt_device_read(temp_dev, 0, &temp_dev_data, 1);
//组织数据
temp_data = (int)(temp_dev_data.data.temp / 10) + ((float)(temp_dev_data.data.temp % 10) / 10);
return temp_data;
}
该函数是读取aht10湿度数据的函数,与上文读取温度数据的函数比较,很容易发现其接口都是完全一样的,仅仅传入的设备句柄不同,也正体现了RTT设备驱动层的编程优势,即无论什么设备,只要注册进了设备驱动,就会拥有完全相同的操作接口,非常方便。
static float aht10_temp_get(void)
{
float temp_data = 0;
//获取数据
rt_device_read(temp_dev, 0, &temp_dev_data, 1);
//组织数据
temp_data = (int)(temp_dev_data.data.temp / 10) + ((float)(temp_dev_data.data.temp % 10) / 10);
return temp_data;
}
该函数是将aht10的当前温湿度数据转换为json格式的字符串,主要用于与网页的通信。将实时数据传输给网页显示。
char *json_create_aht10_current_data(void)
{
char *json_data = RT_NULL;
char value[10] = "";
cJSON *root = cJSON_CreateObject();
snprintf(value, sizeof(value), "%.2f", w601_aht10.cur_temp);
cJSON_AddItemToObject(root, "temp", cJSON_CreateString(value));
snprintf(value, sizeof(value), "%.2f", w601_aht10.cur_humi);
cJSON_AddItemToObject(root, "humi", cJSON_CreateString(value));
json_data = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
return json_data;
}
该函数是将aht10的历史温湿度数据转换为json格式的字符串,主要用于与网页的通信。将历史数据传输给网页显示。
char *json_create_aht10_saved_data(void)
{
char *json_data = RT_NULL;
char value[10] = "";
cJSON *root = cJSON_CreateObject();
cJSON *temp = cJSON_CreateArray();
cJSON *humi = cJSON_CreateArray();
cJSON_AddItemToObject(root, "temp", temp);
cJSON_AddItemToObject(root, "humi", humi);
for (int i = 0; i < w601_aht10.cur_humi_index; i++)
{
snprintf(value, sizeof(value), "%.2f", w601_aht10.humi_data[i]);
cJSON_AddItemToObject(humi, "humi", cJSON_CreateString(value));
}
for (int i = 0; i < w601_aht10.cur_temp_index; i++)
{
snprintf(value, sizeof(value), "%.2f", w601_aht10.temp_data[i]);
cJSON_AddItemToObject(temp, "temp", cJSON_CreateString(value));
}
json_data = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
return json_data;
}