阿里云物联网平台使用教程(三)
教程(一)主要内容为平台介绍、如何注册开通平台账号以及创建产品和设备
教程(二)该平台如何添加自定义功能
教程(三)如何下载设备端代码和SDK以及代码解析、修改和编译运行
一、下载设备端代码和SDK
1.1 设备端代码的下载
我们再产品添加完功能之后,接着想着是能够去操作和使用这些功能,但是如果自己去编写控制是很麻烦的,阿里云物联网平台给我们提供了基本的代码,称之为设备端代码,我们只需要下载下来就可以去操作数据
点击指定产品的“功能定义”,然后点击“生成设备端代码”即可
2.2 SDK的选择和下载
产品和设备到此就已经添加完毕了,接下来就要跟代码打交道,需要在Ubuntu中运行程序操作数据通过阿里云传递到网页端,首先要下载的是SDK。
在设备中集成物联网平台提供的SDK,实现物联网平台接入。设备开发完成,接入物联网平台,设备激活,在物联网平台显示在线。
我们编程主要是使用C语言,所以我们要下载C相关的SDK,但是阿里云提供了不同版本的C SDK,所以我们要根据设备端代码选择合适的SDK,这里我们选择3.2.0版本的SDK。
首先点击物联网平台控制台的“概览”,然后再右上角选择“下载设备SDK”
也可以点击左侧“文档和工具”,选择“设备接入SDK”中“Link SDK”下方的“查看工具包”
然后在当前页面下翻找到“基于设备端SDK开发”,点击“C SDK”
然后点击左侧“3.x”下拉菜单中的“SDK获取”,右侧下翻找到“历史版本清单”,接着点击3.2.0版本右侧的“sdk下载”即可
最终下载好的设备端代码和SDK如图所示
二、代码解析、修改以及编译运行
接下来我们要做的就是在Ubuntu中添加SDK然后运行设备端代码,先将两个下载好的文件进行解压,解压之后的结果如图所示
SDK解压之后是一个文件夹,可以修改名称为memory_control,设备端代码解压完之后就是一个.c程序,暂时不管
接下来将SDK拷贝到Ubuntu中,并进入当前文件夹
在这里需要强调一下,阿里云的SDK对于Ubuntu和内部软件有版本的要求,请确保已安装以下软件,并且版本不低于指定的版本:Ubuntu 16.04 LTS、make-4.1、git-2.7.4、gcc-5.4.0、gcov-5.4.0、lcov-1.12、bash-4.3.48、tar-1.28、cmake3.51、awk4.1.3、sed 4.2.2
如果大家使用我们提供的Ubuntu,只需要安装一下gcov、lcov、cmake即可,其他符合要求,所以需要执行一下命令
sudo apt-get install gcov
sudo apt-get install lcov
sudo apt-get install cmake
接下来需要在SDK中执行make即可(如果是开发板运行的程序,需要打开makefile修改编译工具为交叉编译工具,Ubuntu中默认即可)
执行make进行编译需要一段时间,最终执行完毕如图所示
在当前目录中会多出一个目录文件,名为output,output就是接下来要操作的目录,进入output目录
进入examples目录,在目录中会有示例代码,通过output目录中的Makefile就可以直接编译,所以为了方便,我们可以把解压缩后的设备端代码修改成examples目录下任意文件的名字,这样就不需要修改Makefile,最终生成的可执行文件就是当前.c文件的名字
这里我们将设备端代码命名为mqtt_example.c,将当前目录的mqtt_example.c进行备份,然后将改名后的设备端代码拷贝到examples目录
此时我们已经将SDK和设备端代码都已经成功添加到Ubuntu中了,接下来需要修改设备端代码,将其能够链接到阿里云物联网平台下指定的产品的设备,打开设备端代码
首先要修改的是第21行到27行的代码,指定产品秘钥、设备名称和设备秘钥
产品秘钥需要在阿里云物联网平台控制台寻找,点击左侧“产品”,找到指定产品后点击右侧“查看”(注意每个产品和设备的秘钥都不同,添加自己创建的即可,并且作为公司产品不可外泄秘钥)
在弹出的对话框中可以看到ProductSecret和ProductKey,复制ProductSecret并粘贴到代码中
然后点击左侧“设备”,在指定设备相同的位置点击“查看”
将DeviceName和DeviceSecret复制到代码中
接下来我们可以编译代码并运行了,回到output目录,然后再当前目录下运行make prog,注意是执行make prog而不是make
编译成功之后的结果如图所示,会发现在当前目录下多个一个名为build的目录文件,这个文件保存的就是最终生成的可执行文件,我们最终设备端代码生成的可执行文件名为mqtt-example
接下来到了激动人心的时刻,运行可执行文件与阿里云建立链接,首先需要打开阿里云的代码调试页面,同样在控制台左侧,点击“监控运维”中的“在线调试”,然后再中间页面中“请选择设备”右侧指定产品和设备,此时发现没有任何数据,是因为还没有建立链接
接下来运行可执行文件mqtt-example,出现如图所示的结果说明链接成功了
接着查看阿里云控制台,发现右侧已经有数据了
我们现在可以在“调试功能”里面选择自己定义的功能,可以获取或者设置属性,例如功能选择“power_LED”,方法为“设置”,下方代码中设置为1,点击“发送指令”,就可以将数据发送正在运行的可执行文件
当然,现在大家还不知道设置的数据到底在哪接收,接下来我们的任务就是看懂设备端代码并且可以操作数据
首先是获取阿里云发送过来的数据,具体函数在380行左右,
函数名为user_property_set_event_handler,
这个函数将从阿里云接收到的数据保存在了第二个参数request里面,并且先输出了这个结果,我们可以自己在当前函数里面写一个打印信息
接着重新编译并通过阿里云发送数据,接收到的信息如下
接下来在当前函数中调用另一个函数app_parse_property对接收的数据进行解析,最终解析得到power_LED的值为1还是0,将值保存在powerled->valueint中
接下来我们给阿里云发数据,在发数据前,我们再控制台可以尝试接收数据,我接收内存空闲数据,最终发送指令得到的结果是0,说明是可以正常接收数据的,只不过没有设置而已
查看代码的主函数,会有一个while循环,循环里面有很多的函数调用,这些函数就是给阿里云发数据的函数,每一个函数的第二个参数就是发送阿里云控制台的具体值
比如我们设置app_post_property_cpu_usage的第二个参数为30,编译运行看一下控制台接收的数据
发现获取到的值就是30,这样我们就搞清楚如何与阿里云进通信了,接下来要做的就是把每一个数据都正确的获取到,需要在Ubuntu设备端代码中编写程序并在主函数调用即可
编写的代码如下:
添加头文件:
#include <unistd.h>
定义结构体并定义全局变量:
typedef struct{
long DISK_total; //磁盘总量
long DISK_used_space; //磁盘已用空间
float DISK_used_percentage; //磁盘使用率
int RAM_total; //内存总量
int RAM_used; //内存占用
int RAM_free; //内存空闲
float pcpu; //cpu使用率
}MSG;
MSG msg;
声明并定义函数
void get_data()
{
FILE *fp_disk, *fp_mem;
char cmd_disk[] = "df -l | grep /dev/sda";
char cmd_mem[] = "free -m | grep Mem";
fp_disk = popen(cmd_disk, "r");
if(fp_disk == NULL)
{
perror("fail to popen");
exit(1);
}
fp_mem = popen(cmd_mem, "r");
if(fp_mem == NULL)
{
perror("fail to popen");
exit(1);
}
char buf_disk[128] = "";
fgets(buf_disk, 128, fp_disk);
printf("buf_disk = %s\n", buf_disk);
char buf_mem[128] = "";
fgets(buf_mem, 128, fp_mem);
printf("buf_mem = %s\n", buf_mem);
sscanf(buf_disk, "%*s %ld %ld", &msg.DISK_total, &msg.DISK_used_space);
msg.DISK_used_percentage = (float)msg.DISK_used_space/(float)msg.DISK_total*100;
sscanf(buf_mem, "%*s %d %d", &msg.RAM_total, &msg.RAM_used);
msg.RAM_free = msg.RAM_total - msg.RAM_used;
printf("磁盘总量:%.0fG, 磁盘已用:%.2fG,磁盘使用率:%.2f%%\n", \
(float)msg.DISK_total/1024/1024, (float)msg.DISK_used_space/1024/1024,\
(float)msg.DISK_used_space/(float)msg.DISK_total*100);
printf("内存总量:%.0fM, 内存占用:%.2fM, 内存空闲:%.2fM\n", \
(float)msg.RAM_total, (float)msg.RAM_used, (float)msg.RAM_free);
//**************************cpu********************************
FILE *fp1, *fp2;
char cmd[] = "cat /proc/stat";
fp1 = popen(cmd, "r");
if(fp1 == NULL)
{
perror("fail to popen");
exit(1);
}
fp2 = popen(cmd, "r");
if(fp1 == NULL)
{
perror("fail to popen");
exit(1);
}
char buf1[128] = "";
char buf2[128] = "";
fgets(buf1, 128, fp1);
fgets(buf2, 128, fp2);
printf("%s\n", buf1);
printf("%s\n", buf2);
int data1[10];
int data2[10];
sscanf(buf1, "%*s %d %d %d %d %d %d %d %d %d %d", &data1[0], &data1[1], &data1[2], &data1[3], &data1[4],\
&data1[5], &data1[6], &data1[7], &data1[8], &data1[9]);
sscanf(buf2, "%*s %d %d %d %d %d %d %d %d %d %d", &data2[0], &data2[1], &data2[2], &data2[3], &data2[4],\
&data2[5], &data2[6], &data2[7], &data2[8], &data2[9]);
int sum1 = 0, sum2 = 0;
int totalCpuTime;
int i;
for(i = 0; i <= 9; i++)
{
sum1 += data1[i];
}
sleep(1);
for(i = 0; i <= 9; i++)
{
sum2 += data2[i];
}
totalCpuTime = sum2 - sum1;
printf("totalCpuTime = %d\n", totalCpuTime);
int sum3 = 0, sum4 = 0;
int idle;
for(i = 0; i <=3; i++)
{
sum3 += data1[i];
}
for(i = 0; i <=3; i++)
{
sum4 += data2[i];
}
idle = sum4 - sum3;
printf("idle = %d\n", idle);
msg.pcpu = 100 * (float)(totalCpuTime - idle) / (float)totalCpuTime;
printf("cpu使用率:%.2f%%\n", msg.pcpu);
fclose(fp_mem);
fclose(fp_disk);
fclose(fp1);
fclose(fp2);
}
将函数在主函数里面调用并给指定功能赋值传参
/**
* @brief main函数
*/
int main(int argc, char **argv)
{
int res = 0;
int cnt = 0;
iotx_linkkit_dev_meta_info_t master_meta_info;
int dynamic_register = 0, post_reply_need = 0;
memset(&g_user_example_ctx, 0, sizeof(user_example_ctx_t));
#ifdef ATM_ENABLED
if (IOT_ATM_Init() < 0) {
EXAMPLE_TRACE("IOT_ATM_Init failed!\n");
return -1;
}
#endif
memset(&master_meta_info, 0, sizeof(iotx_linkkit_dev_meta_info_t));
memcpy(master_meta_info.product_key, g_product_key, strlen(g_product_key));
memcpy(master_meta_info.product_secret, g_product_secret, strlen(g_product_secret));
memcpy(master_meta_info.device_name, g_device_name, strlen(g_device_name));
memcpy(master_meta_info.device_secret, g_device_secret, strlen(g_device_secret));
IOT_SetLogLevel(IOT_LOG_DEBUG);
/* 注册回调函数 */
IOT_RegisterCallback(ITE_STATE_EVERYTHING, user_sdk_state_dump);
IOT_RegisterCallback(ITE_CONNECT_SUCC, user_connected_event_handler);
IOT_RegisterCallback(ITE_DISCONNECTED, user_disconnected_event_handler);
IOT_RegisterCallback(ITE_SERVICE_REQUEST, user_service_request_event_handler);
IOT_RegisterCallback(ITE_PROPERTY_SET, user_property_set_event_handler);
IOT_RegisterCallback(ITE_REPORT_REPLY, user_report_reply_event_handler);
IOT_RegisterCallback(ITE_TRIGGER_EVENT_REPLY, user_trigger_event_reply_event_handler);
IOT_RegisterCallback(ITE_TIMESTAMP_REPLY, user_timestamp_reply_event_handler);
IOT_RegisterCallback(ITE_INITIALIZE_COMPLETED, user_initialized);
IOT_RegisterCallback(ITE_FOTA, user_fota_event_handler);
IOT_RegisterCallback(ITE_CLOUD_ERROR, user_cloud_error_handler);
IOT_RegisterCallback(ITE_DYNREG_DEVICE_SECRET, dynreg_device_secret);
/* 选择上线方式(是否使用动态注册) */
dynamic_register = 0;
IOT_Ioctl(IOTX_IOCTL_SET_DYNAMIC_REGISTER, (void *)&dynamic_register);
/* 是否需用属性、事件上报(设置)应答 */
post_reply_need = 1;
IOT_Ioctl(IOTX_IOCTL_RECV_EVENT_REPLY, (void *)&post_reply_need);
do {
g_user_example_ctx.master_devid = IOT_Linkkit_Open(IOTX_LINKKIT_DEV_TYPE_MASTER, &master_meta_info);
if (g_user_example_ctx.master_devid >= 0) {
break;
}
EXAMPLE_TRACE("IOT_Linkkit_Open failed! retry after %d ms\n", 2000);
HAL_SleepMs(2000);
} while (1);
do {
res = IOT_Linkkit_Connect(g_user_example_ctx.master_devid);
if (res >= 0) {
break;
}
EXAMPLE_TRACE("IOT_Linkkit_Connect failed! retry after %d ms\n", 5000);
HAL_SleepMs(5000);
} while (1);
while (1) {
IOT_Linkkit_Yield(EXAMPLE_YIELD_TIMEOUT_MS);
/* Post Proprety Example */
if ((cnt % 20) == 0) {
get_data();
app_post_property_PowerLed(EXAMPLE_MASTER_DEVID, 1);
app_post_property_DISK_used_percentage(EXAMPLE_MASTER_DEVID, msg.DISK_used_percentage);
app_post_property_DISK_used_space(EXAMPLE_MASTER_DEVID, (float)msg.DISK_used_space/1024/1024);
app_post_property_DISK_total(EXAMPLE_MASTER_DEVID, (float)msg.DISK_total/1024/1024);
app_post_property_RAM_free(EXAMPLE_MASTER_DEVID, (float)msg.RAM_free);
app_post_property_RAM_used(EXAMPLE_MASTER_DEVID, (float)msg.RAM_used);
app_post_property_RAM_total(EXAMPLE_MASTER_DEVID, (float)msg.RAM_total);
app_post_property_cpu_usage(EXAMPLE_MASTER_DEVID, msg.pcpu);
app_post_property_cpu_temperature(EXAMPLE_MASTER_DEVID, 30);
}
cnt++;
HAL_SleepMs(200);
}
IOT_Linkkit_Close(g_user_example_ctx.master_devid);
IOT_SetLogLevel(IOT_LOG_NONE);
return 0;
}
注意:由于是在虚拟机运行Ubuntu,所以无法获取cpu温度,所以在这里将温度固定设置为30
接下来编译代码并运行,通过阿里云控制台接收数据
除了可以在这里查看以外,还可以实时查看设备数据,在指定设备中点击“物模型数据”,下方就可以实时查看数据的变化
备注:
于作者水平,难免有理解和描述上有疏漏或者错误的地方,欢迎共同交流;部分参考已经在正文和参考文献中列表注明,但仍有可能有疏漏的地方,有任何侵权或者不明确的地方,欢迎指出,必定及时更正或者删除;文章供于学习交流,转载注明出处。