【平头哥RVB2601创意应用开发】环境监测终端06-将数据同步到阿里云
[复制链接]
本篇文章介绍将采集到的温度、湿度、噪声水平数据和指示灯设置上传到阿里云平台,还可以通过云端控制指示灯开关。
1、阿里云设置
在我的第二篇文章中介绍了在阿里云飞燕平台创建产品的过程。这次介绍在阿里云IoT Studio平台创建产品,添加设备的过程,具体的这两个平台有什么区别,可以参见我之前文章的介绍,链接如下。
https://occ.t-head.cn/community/post/detail?id=4031864498838708224
登陆阿里云IoT Studio平台,地址如下。
https://account.aliyun.com/login/login.htm?qrCodeFirst=false&oauth_callback=https%3A%2F%2Fstudio.iot.aliyun.com%2Fproject%2Fa123jX2VXQqAfhnk%2Fdevices%2Fa1iO8mTqhOz%2Fdev_001%2F1
在主页中点击“产品”一栏,进入产品列表,然后点击“创建产品”如下图:
图1、主页操作
在如下界面中录入自己想要的信息,点击最下面的确认,产品就创建完成。
图2、创建产品
进入产品管理界面,按照下图添加产品属性,其中标识符是单片机和阿里云通讯的重要握手信息,最好设成简明好记的,编程时方便调试。
图3、添加产品属性
我的产品主要是温度、湿度、噪声水平三个参数的采集,和一个指示灯的设置,共有四个属性。这些参数的数据类型和数据定义也要和单片机程序里面的一致。
产品设置好后,添加设备,按照下图操作,填入自己喜欢的信息,点击确定添加成功。
图4、添加设备
在设备信息界面可以查看相关的证书密钥,这个回头用于单片机程序和阿里云连接用。具体如何使用见我的第二篇文章。
图5、设备信息
至此阿里云端的设置就完成了。可以通过不同的界面查看各种配置,多熟悉一下就能熟练使用,整个操作界面还是很友好的。
2、单片机程序编写
在我的第二篇文章中已经介绍了如何连接到阿里云平台,这里不再赘述,重点介绍上传相关的代码和调试中遇到的问题。
首先说一下我遇到代码容量不够问题。当把lvgl库和wifi库同时加入工程后,编译会提示编译完的代码超范围,貌似之前其他同学也遇到过此类问题。我仔细研究了flash默认配置,发现总共512KB的容量,只给了用户程序分配了256KB,剩下还有好多留给升级缓存了。在我这个作品中,没有计划做OTA升级,因此完全可以把用户空间扩大。具体操作如下图。
图6、修改分区表
打开config.yaml分区表文件,将prim用户空间扩大,具体扩大到多少根据自己需要改,别超出flash有效范围就行。我这改到384KB。misc为升级缓存空间,起始地址也要注意修改成对应的新地址。改完注意保存。打开gcc_flash.ld文件,将flash长度修改成和config.yaml一样的,保存。
这两个文件改完后,我们就有更多空间存代码了。我的代码编译完成后如下图,还有足够的空间扩展。
图7、代码占用情况
然后是在原来物联网代码基础上添加属性上报代码。在w800_api.c中添加通用属性上报函数,代码如下。
int w800_living_idmpp(const char *dev_id, const char *msg, int *packet_id)
{
int ret = -1;
int rsp_dev_id = -1;
int rsp_packet_id = -1;
int rsp_code = -1;
int rsp_reply_len = -1;
char event_str[64] = {0};
if (!dev_id || !msg || !packet_id) {
return ret;
}
aos_mutex_lock(&g_cmd_mutex, AOS_WAIT_FOREVER);
atparser_clr_buf(g_atparser_uservice_t);
/**
* AT+IDMPP=0,"{"temp":25.8,"humi":26.5,"noise":18,"led":0}"
*/
char at_string_msg[80] = {0};
snprintf(at_string_msg, 80, "AT+IDMPP=0,\"%s\"", msg);
LOGD(TAG, "Send msg: %s\r\n", at_string_msg);
if (atparser_send(g_atparser_uservice_t, at_string_msg) == 0) {
if (((atparser_recv(g_atparser_uservice_t, "+IDMPP:%d\n", packet_id) == 0) &&
(atparser_recv(g_atparser_uservice_t, "OK\n") == 0)) ||
(atparser_recv(g_atparser_uservice_t, "+IDMPP:%d,%d,%d,%d,%s\n",
&rsp_dev_id, &rsp_packet_id, &rsp_code, &rsp_reply_len, event_str) == 0)) {
ret = 0;
LOGD(TAG, "Send at cmd ok\r\n");
}
} else {
LOGD(TAG, "Send at cmd err\r\n");
}
atparser_cmd_exit(g_atparser_uservice_t);
aos_mutex_unlock(&g_cmd_mutex);
return ret;
}
创建yun_app.c文件,将云端操作相关代码放这个里面。代码如下。
#include <stdlib.h>
#include <string.h>
#include <aos/list.h>
#include <aos/debug.h>
#include <drv/gpio.h>
#include <drv/gpio_pin.h>
#include <drv/pin.h>
#include <uservice/uservice.h>
#include <uservice/eventid.h>
#include <yoc/sysinfo.h>
#include <board.h>
//#include "player_demo.h"
#include "cJSON.h"
#include "thp_get.h"
#include "yun_app.h"
#define TAG "APP"
extern Result_S res_data;
int iot_con_flag = 0;
extern int w800_living_wjap(const char *myssid,const char *mypassword);
extern int w800_living_idmau(const char *mykey,const char *myname,const char *mysecret,const char *mypsecretconst);
extern int w800_living_idmcon(void);
extern int w800_living_idmpp(const char *dev_id, const char *msg, int *packet_id);
int iot_upload(void)
{
int ret = -1;
const char *dev_id = "0";
char msg_buf[80];
int pkt_id = 0;
if(iot_con_flag==1)
{
snprintf(msg_buf,80,"{\\\"temp\\\":%.1f,\\\"humi\\\":%.1f,\\\"noise\\\":%d,\\\"led\\\":%d}",res_data.temp,res_data.humi,res_data.noise,res_data.led);
printf( "==> start attribute upload\n");
ret = w800_living_idmpp(dev_id, msg_buf, &pkt_id);
}
return(ret);
}
static csi_gpio_pin_t g;
void led_pin_init()
{
csi_pin_set_mux(PA25, PIN_FUNC_GPIO);
csi_gpio_pin_init(&g, PA25);
csi_gpio_pin_dir(&g, GPIO_DIRECTION_OUTPUT);
csi_gpio_pin_write(&g, GPIO_PIN_HIGH);
}
void led_refreshed(int sw_led)
{
if (sw_led == 1)
{
csi_gpio_pin_write(&g, GPIO_PIN_LOW);
}
else
{
csi_gpio_pin_write(&g, GPIO_PIN_HIGH);
}
}
unsigned char buff_accept[68];
cJSON *str_json, *str_value; //初始化json结构体指针
int iot_apply(char *strbuff)
{
strcpy((char *)buff_accept,strbuff);
str_json = cJSON_Parse((char *)buff_accept); //创建JSON解析对象,返回JSON格式是否正确
if(str_json == NULL)
{
LOGI(TAG"error:%s;\r\n",cJSON_GetErrorPtr());
cJSON_Delete(str_json);//释放内存
return -1;
}
else
{
cJSON *str_value = cJSON_GetObjectItem(str_json, "led");
LOGI(TAG,"led powerstate:%d;\r\n",str_value->valueint);
led_refreshed(str_value->valueint);
res_data.led = str_value->valueint;
cJSON_Delete(str_json);//释放内存
return 1;
}
}
int iot_connect_dome(void)
{
char *my_ssid = "**********";//2.4GHZ WiFi ssid
char *my_password = "**********";//2.4GHZ WiFi password
char *my_key = "**********";//ProductKey
char *my_name = "**********";//DeviceName
char *my_secret = "**********";//DeviceSecret
char *my_p_secret = "**********";//Product Secret
int ret1 = -1;
int ret2 = -1;
int ret3 = -1;
ret1 = w800_living_wjap(my_ssid,my_password);
if (ret1 == 0){
printf("AT+WJAP:OK!\n");
}
else{
printf("AT+WJAP:ERROR!\n");
}
ret2 = w800_living_idmau(my_key,my_name,my_secret,my_p_secret);
if (ret2 == 0){
printf("AT+IDMAU:OK!\n");
}
else{
printf("AT+IDMAU:ERROR!\n");
}
ret3 = w800_living_idmcon();
if (ret3 == 0){
printf("AT+IDMCON:OK!\n");
}
else{
printf("AT+IDMCON:ERROR!\n");
}
if(ret1 == 0 && ret2 == 0 && ret3 == 0){
return 0;
}else{
return -1;
}
}
static void network_event(uint32_t event_id, const void *param, void *context)
{
switch(event_id) {
case EVENT_NETMGR_GOT_IP:
LOGD(TAG, "net got ip");
break;
case EVENT_NETMGR_NET_DISCON:
LOGD(TAG, "net disconnect");
break;
}
/*do exception process */
app_exception_event(event_id);
}
int yun_app(void)
{
int ret = -1;
// board_yoc_init();
led_pin_init();
/* Subscribe */
event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL);
event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL);
ret = iot_connect_dome();
if (ret == 0)
{
iot_con_flag = 1;
printf("connect iot success\n");
}else
{
iot_con_flag = 0;
printf("connect iot error\n");
}
return(0);
}
注意在编辑上报字符串中,”\”的数量,我开始没注意,少敲了一个,导致数据上传出错,查找了好长时间才发现。
在数据更新任务中添加属性上报函数调用,此处由于屏幕更新较快,我做了一个累加,将上传频次降低。代码如下。
static void res_update_task(void *arg)
{
char cdata[10] = { 0 };
int time_num = 0;
while(1)
{
res_data.noise = sound_max / 15; //此处系数用于校准分贝大小
if(res_data.noise > 100)
res_data.noise = 100;
sprintf((char*)cdata, "%.1fC", res_data.temp);//格式化输出 浮点数转成字符串输出
lv_label_set_text_fmt(pGui_label_temp,"%s", (const char*)cdata);
sprintf((char*)cdata, "%.1f%%", res_data.humi);//格式化输出 浮点数转成字符串输出
lv_label_set_text_fmt(pGui_label_humi,"%s", (const char*)cdata);
lv_label_set_text_fmt(pGui_label_noise, "%ddB", res_data.noise);
lv_bar_set_value(pGui_bar_noise, res_data.noise+1, LV_ANIM_OFF);
time_num++;
if(time_num > 10)
{
iot_upload();//更新云端数据
time_num = 0;
}
aos_msleep(200);
}
}
整个工程编译好后下载、运行,从云端就能看到数据更新了。如下图。
图8、上传成功
通过设备管理界面的在线调试功能,还可以控制指示灯开关,如下图。
图9、在线调试
本次作品单片机端的预期功能基本都实现完成,接下来在云平台设计一个web应用,用于展示采集数据,就大功告成了。
|