此部分在前面说明的基础上,介绍在RT-Thread Studio中使用相关组件,并进行代码修改,以实现整个项目的功能。首先创建RT-Thread项目,因在本示例中采用的正点原子MiniSTM32开发板其MCU是STM32F103RC,所以在创建项目时,选择“基于芯片”,芯片型号选择STM32F103RC,控制台串口保持默认的UART1。完成项目的创建后,打开applications下面的main.c,把LOG_D(“Hello RT-Thread!”)给注释掉,或移到while()循环上面,以免在程序运行时不停在串口控制台打印"Hello RT-Thread!"影响调试。
添加DHT11温湿度组件。在RT-Thread Settings的软件包中心中查找dht11并添加,保存配置。因dht11组件比较简单,不需要进一步进行详细的配置。打开位于packages/dht11-latest目录下的dht11_sample.c文件,以下面的代码整体替换原来的代码:
#include <rtthread.h>
#include <rtdevice.h>
#include "sensor.h"
#include "sensor_dallas_dht11.h"
#include "drv_common.h"
#define DHT11_DATA_PIN GET_PIN(C, 1) //PC1脚接DHT11的单总线
uint8_t temp_dht11 = 0; //记录温度的全局变量,被阿里云IoT线程读取并上传
uint8_t humi_dht11 = 0; //记录湿度的全局变量,被阿里云IoT线程读取并上传
//温湿度读取线程入口函数,每秒读取一次温湿度
static void read_temp_entry(void *parameter)
{
rt_device_t dev = RT_NULL;
struct rt_sensor_data sensor_data;
rt_size_t res;
rt_uint8_t get_data_freq = 1; /* 1Hz */
dev = rt_device_find("temp_dht11");
if (dev == RT_NULL)
{
return;
}
if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("open device failed!\n");
return;
}
rt_device_control(dev, RT_SENSOR_CTRL_SET_ODR, (void *)(&get_data_freq));
while (1)
{
res = rt_device_read(dev, 0, &sensor_data, 1);
if (res != 1)
{
rt_kprintf("read data failed! result is %d\n", res);
rt_device_close(dev);
return;
}
else
{
if (sensor_data.data.temp >= 0)
{
temp_dht11 = (sensor_data.data.temp & 0xffff) >> 0; // 获取温度
humi_dht11 = (sensor_data.data.temp & 0xffff0000) >> 16; // 获取湿度
rt_kprintf("temp:%d, humi:%d\n" ,temp_dht11, humi_dht11);
}
}
rt_thread_delay(2000);
}
}
//创建并启动温湿度读取线程
static int dht11_read_temp_sample(void)
{
rt_thread_t dht11_thread;
dht11_thread = rt_thread_create("dht_tem",
read_temp_entry,
RT_NULL,
1024,
RT_THREAD_PRIORITY_MAX / 2,
20);
if (dht11_thread != RT_NULL)
{
rt_thread_startup(dht11_thread);
}
return RT_EOK;
}
INIT_APP_EXPORT(dht11_read_temp_sample); //加入应用自动初始化
//DHT11传感器设备初始化
static int rt_hw_dht11_port(void)
{
struct rt_sensor_config cfg;
cfg.intf.user_data = (void *)DHT11_DATA_PIN;
rt_hw_dht11_init("dht11", &cfg);
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_dht11_port);//加入组件自动初始化
编译下载后,用串口调试工具连接,可在Finsh控制台中看见每秒打印出温湿度值。在控制台中执行list_device命令,可看到设备信息“temp_dht Sensor Device”,执行ps命令时,应该能看到“dht_tem"线程,如果温湿度不能正常打印,看不到“dht_tem"线程,很有可能是DHT11接线错误或模块损坏。在“dht_tem"线程启动时,会调用dht11_check()对DHT11模块进行检测,如不正常,则退出线程。
添加ESP8266驱动相关组件。在RT-Thread Settings中添加at_device(AT客户端也会被自动添加,之间存在依赖关系),并开启libc库(默认开启的时minilibc库)。打开drivers目录下的board.h,在串口1宏定义下面添加如下代码:
#define BSP_USING_UART3
#define BSP_UART3_TX_PIN "PB10"
#define BSP_UART3_RX_PIN "PB11"
在at_device的详细配置中,启用“乐鑫ESP8266”,并在“使能示例”中设置WIFI密码和SSID,修改使用的串口为:uart3(MiniSTM32开发板的uart2引脚被用作存储设备的片选,所以串口2不能使用),其配置见下图:
添加阿里云IoT软件包。在RT-Thread Settings中搜索“ali-iotkit”并添加,添加该组件时,因存在依赖关系,cjson、SAL等组件也被自动添加。进入该组件的详细配置界面,配置IoT产品和设备参数:
#include "rtthread.h"
#include "dev_sign_api.h"
#include "mqtt_api.h"
char DEMO_PRODUCT_KEY[IOTX_PRODUCT_KEY_LEN + 1] = {0};
char DEMO_DEVICE_NAME[IOTX_DEVICE_NAME_LEN + 1] = {0};
char DEMO_DEVICE_SECRET[IOTX_DEVICE_SECRET_LEN + 1] = {0};
extern uint8_t temp_dht11; //在dht11_sample.c中定义,为采集到的温度数值
extern uint8_t humi_dht11; //在dht11_sample.c中定义,为采集到的湿度数值
void *HAL_Malloc(uint32_t size);
void HAL_Free(void *ptr);
void HAL_Printf(const char *fmt, ...);
int HAL_GetProductKey(char product_key[IOTX_PRODUCT_KEY_LEN + 1]);
int HAL_GetDeviceName(char device_name[IOTX_DEVICE_NAME_LEN + 1]);
int HAL_GetDeviceSecret(char device_secret[IOTX_DEVICE_SECRET_LEN]);
uint64_t HAL_UptimeMs(void);
int HAL_Snprintf(char *str, const int len, const char *fmt, ...);
#define EXAMPLE_TRACE(fmt, ...) \
do { \
HAL_Printf("%s|%03d :: ", __func__, __LINE__); \
HAL_Printf(fmt, ##__VA_ARGS__); \
HAL_Printf("%s", "\r\n"); \
} while(0)
static void example_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
iotx_mqtt_topic_info_t *topic_info = (iotx_mqtt_topic_info_pt) msg->msg;
switch (msg->event_type) {
case IOTX_MQTT_EVENT_PUBLISH_RECEIVED:
/* print topic name and topic message */
EXAMPLE_TRACE("Message Arrived:");
EXAMPLE_TRACE("Topic : %.*s", topic_info->topic_len, topic_info->ptopic);
EXAMPLE_TRACE("Payload: %.*s", topic_info->payload_len, topic_info->payload);
EXAMPLE_TRACE("\n");
break;
default:
break;
}
}
static int example_subscribe(void *handle) //Topic主题订阅函数
{
int res = 0;
const char *fmt = "/sys/%s/%s/thing/event/property/post";
char *topic = NULL;
int topic_len = 0;
topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
topic = HAL_Malloc(topic_len);
if (topic == NULL) {
EXAMPLE_TRACE("memory not enough");
return -1;
}
memset(topic, 0, topic_len);
HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);
res = IOT_MQTT_Subscribe(handle, topic, IOTX_MQTT_QOS0, example_message_arrive, NULL);
if (res < 0) {
EXAMPLE_TRACE("subscribe failed");
HAL_Free(topic);
return -1;
}
HAL_Free(topic);
return 0;
}
static int example_publish(void *handle) //向Topic主题发布属性函数
{
int res = 0;
const char *fmt = "/sys/%s/%s/thing/event/property/post";
const char *fmt_payload = "{\"params\" : { \"wendu\":%d,\"shidu\":%d } }";
char *topic = NULL;
int topic_len = 0;
int payload_len = 0;
char *payload = NULL;
payload_len = strlen(fmt_payload)+4;
payload = HAL_Malloc(payload_len);
if (payload == NULL) {
EXAMPLE_TRACE("memory not enough");
return -1;
}
memset(payload, 0, payload_len);
HAL_Snprintf(payload,payload_len,fmt_payload,temp_dht11,humi_dht11);
topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
topic = HAL_Malloc(topic_len);
if (topic == NULL) {
EXAMPLE_TRACE("memory not enough");
return -1;
}
memset(topic, 0, topic_len);
HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);
res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, strlen(payload));
if (res < 0) {
EXAMPLE_TRACE("publish failed, res = %d", res);
HAL_Free(topic);
HAL_Free(payload);
return -1;
}
HAL_Free(topic);
HAL_Free(payload);
return 0;
}
static void example_event_handle(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
EXAMPLE_TRACE("msg->event_type : %d", msg->event_type);
}
static void mqtt_example_main(void *parameter) //mqtt线程主入口函数
{
void *pclient = NULL;
int res = 0;
iotx_mqtt_param_t mqtt_params;
HAL_GetProductKey(DEMO_PRODUCT_KEY);
HAL_GetDeviceName(DEMO_DEVICE_NAME);
HAL_GetDeviceSecret(DEMO_DEVICE_SECRET);
EXAMPLE_TRACE("mqtt example");
memset(&mqtt_params, 0x0, sizeof(mqtt_params));
mqtt_params.handle_event.h_fp = example_event_handle;
pclient = IOT_MQTT_Construct(&mqtt_params);
if (NULL == pclient) {
EXAMPLE_TRACE("MQTT construct failed");
return;
}
res = example_subscribe(pclient);
if (res < 0) {
IOT_MQTT_Destroy(&pclient);
return;
}
while (1) {
example_publish(pclient);
rt_thread_delay(3000);
IOT_MQTT_Yield(pclient, 200);
}
return;
}
static rt_thread_t tid1 = RT_NULL;
int mqtt_thread(void) //创建mqtt线程
{
tid1 = rt_thread_create("mqtt_thd", mqtt_example_main, RT_NULL, 4096, 14, 10);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
return 0;
}
//#ifdef FINSH_USING_MSH
//MSH_CMD_EXPORT(mqtt_thread,mqtt thread);
//#endif
INIT_APP_EXPORT(mqtt_thread); //加入应用自动初始化
此部分代码的主要作用是建立一个"mqtt_thd"线程,该线程定时读取温湿度数据,再以json格式{“params” : { “wendu”:xx,“shidu”:xx } }"通过ESP8266上报到阿里云IoT平台中对应设备esp8266-dev1的属性Topic中(/sys/a1iMUSvxKOL/esp8266-dev1/thing/event/property/post),然后由阿里云平台再转发用户应用中。
编译下载运行后,可在控制台中看到"mqtt_thd"线程定时打印出温湿度数值,用手机扫描在第二部分介绍的移动应用二维码,就可以在手机上实时看到上报的温湿度信息。本文介绍的物联网平台是阿里云的企业物联网平台,在该平台目前还不能以app方式提供手机应用,但阿里云的生活物联网平台(飞燕平台)支持生成手机app,有兴趣的可以试试。