前言
我们在上一篇文章中使用它MQTT.fx模拟客户端实现了与阿里云物联网平台的双向通信。接下来,我们自己编程和使用它mosquitto库实现客户端的发布端。
一.iniparser 和 json
1.为什么要用?iniparser库
与阿里云进行通信时,我们需要如下一些信息,例如在使用MQTT.fx时: 复制保存,以及之前发布和订阅的topic还要写下: 这些都是我们在项目中使用的配置信息,具体应用于:
clientid:struct mosquitto *mosquitto_new( const char * id, bool clean_session, void * obj ) brokeraddress and brokerport:int mosquitto_connect( struct mosquitto * mosq, const char * host, int port, int keepalive ) username and password:int mosquitto_username_pw_set(struct mosquitto * mosq, const char *username, const char *password ) topic:int mosquitto_publish( struct mosquitto * mosq, int * mid, const char * topic, int payloadlen, const void * payload, int qos, bool retain )
当然,我们可以直接写在代码中,但这样,代码的重用性就不高了。如果更改设备,则需要更改源代码以更改设备配置信息。 所以我们写一个.ini这样,当我们更换设备时,我们只需要修改此文件.ini文件中的信息。然后在代码中使用iniparser库中API。
2.为什么要用?json库
JSON(JavaScript Object Notation, JS 对象简谱) 是轻量级数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)子集采用完全独立于编程语言的文本格式存储和表示数据。简洁清晰的层次结构使层次结构简洁明了 JSON 成为理想的数据交换语言。 易于阅读和编写,易于机器分析和生成,有效提高网络传输效率。
payload must be json format 负载(消息)必须是json格式。 所以我们这样写: 再看阿里云: 现在就成功了!
因为我们使用阿里云提供的标准模型,我们的新闻格式应该遵循阿里云的规范。这就是我们想要使用的json的原因。 其中:param 代表参数。有两个参数。 一个 data 一个 CurrentTemperature (这两个参数是我们之前定义的功能中的标识符),你可以这样理解,阿里云可以接收json格式发送的任何信息。
二.实现客户端编程
1.大致流程
2.配置信息处理
#ifndef CONFIGOPERATE_H #define CONFIGOPERATE_H #define SIZE 256
#define DEFAULT_CLIENTID "12345|securemode=3,signmethod=hmacsha1|"
#define DEFAULT_USERNAME "test&ghcey1O2Yxm"
#define DEFAULT_PASSWORD "D090FBDCC327200DAD3083684A891A1C7992FACB"
#define DEFAULT_BROKER_ADDRESS "ghcey1O2Yxm.iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define DEFAULT_BROKER_PORT 1883
#define DEFAULT_SUB_TOPIC "/sys/ghcey1O2Yxm/test/thing/service/property/set"
#define DEFAULT_PUB_TOPIC "/sys/ghcey1O2Yxm/test/thing/event/property/post"
#define DEFAULT_METHOD "thing.event.property.post"
#define DEFAULT_JSONID "0801"
#define DEFAULT_IDENTIFIER "data"
#define DEFAULT_VERSION "1.0.0"
#define KEEP_ALIVE 60
enum mode
{
SUB,
PUB
};
typedef struct mqtt_user_data{
char clientid[SIZE];
char username[SIZE];
char password[SIZE];
char brokeraddress[SIZE];
int brokerport;
char topic[SIZE];
char method[SIZE];
char jsonid[SIZE];
char identifier[SIZE];
char version[SIZE];
}mqtt_user_data;
int set_config(char *ini,char *host,int port, char *clientid,char *uname,char *pwd,char *topic);
int get_config(char *path,mqtt_user_data *mqtt,int mode);
#endif
3.主要流程
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <netdb.h>
#include <signal.h>
#include <libgen.h>
#include "mosquitto.h"
#include "ds18b20.h"
#include "cJSON.h"
#include "iniparser.h"
#include "configoperate.h"
#define INI_PATH "./mqtt_aliyun_conf.ini"
#define KEEP_ALIVE 60
int g_stop = 0;
static inline void print_usage(char *progname);//内联函数,空间换时间。编译时期展开。
void sig_handler(int sig_num)
{
if(sig_num == SIGUSR1)
g_stop = 1;
}
void connect_callback(struct mosquitto *mosq,void *obj,int rc);
int main (int argc, char **argv)
{
char *progname = basename(argv[0]);
int brokerport = 0;
char *brokeraddress = NULL;
char *clientid = NULL;
char *username = NULL;
char *password = NULL;
char *topic = NULL;
struct mosquitto *mosq = NULL;
int daemon_run = 0;
int opt = -1;
int rv;
char buffer[BUF_SIZE];
int log_fd;
struct mqtt_user_data mqtt;
struct option options[] =
{
{
"daemon",no_argument,NULL,'d'},
{
"topic", required_argument,NULL,'t'},
{
"brokeraddress", required_argument,NULL,'H'},
{
"clientid", required_argument, NULL, 'i'},
{
"brokerport",required_argument,NULL,'p'},
{
"help",no_argument,NULL,'h'},
{
"username",required_argument,NULL,'u'},
{
"password",required_argument,NULL,'P'},
{
NULL,0,NULL,0}
};
while((opt = getopt_long(argc,argv,"dhp:t:i:u:P:H:",options,NULL)) != -1)
{
switch(opt)
{
case 't':
topic = optarg;
break;
case 'i':
clientid = optarg;
break;
case 'H':
brokeraddress = optarg;
break;
case 'u':
username = optarg;
break;
case 'P':
password = optarg;
break;
case 'd':
daemon_run = 1;
break;
case 'p':
brokerport = atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
default:
break;
}
}
//set_config(INI_PATH,brokeraddress,brokerport,clientid,username,password,topic);
if(daemon_run)
{
printf("program %s running in backgrund\n", progname);
if( (log_fd = open("client.log", O_CREAT|O_RDWR, 0666)) < 0)
{
printf("open() failed:%s\n", strerror(errno)) ;
return -2;
}
dup2(log_fd, STDOUT_FILENO) ;
dup2(log_fd, STDERR_FILENO) ;
daemon(1,1);
}
signal(SIGUSR1,sig_handler);
memset(&mqtt,0,sizeof(mqtt));
rv = get_config(INI_PATH,&mqtt,PUB);
mosquitto_lib_init();
mosq = mosquitto_new(mqtt.clientid,true,(void *)&mqtt);
if(!mosq)
{
printf("mosquitto_new() failed: %s\n",strerror(errno));
mosquitto_lib_cleanup();
return -1;
}
if(mosquitto_username_pw_set(mosq,mqtt.username,mqtt.password) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_username_pw_set failed: %s\n",strerror(errno));
/*mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return -1;*/
goto cleanup;
}
printf("Create mosquitto successfully!\n");
mosquitto_connect_callback_set(mosq,connect_callback);
while(!g_stop)
{
printf("into while:\n");
if(mosquitto_connect(mosq, mqtt.brokeraddress,mqtt.brokerport,KEEP_ALIVE) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_connect() failed: %s\n",strerror(errno));
//continue;
goto cleanup;
}
printf("connect successfully\n");
mosquitto_loop_forever(mosq,-1,1);
sleep(30);
}
cleanup:
close(log_fd);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
void print_usage(char *progname)
{
printf("%s usage:\n",progname);
printf("Example: %s -h ${brokeraddress} -p ${brokerport} -i ${clientid} -u ${username} -p ${password} -t${topic} -h ${help} -d ${daemon}\n",progname);
printf("-h(--host): sepcify brokeraddress.\n");
printf("-p(--port): sepcify brokerport.\n");
printf("-h(--Help): print this help information.\n");
printf("-d(--daemon): set program running on background.\n");
printf("-i(--clientid): sepcify the clientid.\n");
printf("-u(--username): sepcify username of the client.\n");
printf("-p(--password): sepcify password of the username.\n");
printf("-t(--topic): sepcify topic of the client.\n");
printf("-d(--daemon): running in backgrund.\n");
}
void connect_callback(struct mosquitto *mosq,void *obj,int rc)
{
printf("get into connect_callback():\n");
float t = 27.000000;
if(ds18b20_get_temperature(&t) < 0)
{
printf("ds18b20_get_temperature failed.\n");
return;
}
cJSON *root = cJSON_CreateObject();
cJSON *item = cJSON_CreateObject();
if(!obj)
{
printf("invalid_argument in %s\n",__FUNCTION__);
return;
}
struct mqtt_user_data *mqtt = (mqtt_user_data *)obj;
cJSON_AddItemToObject(root,"method",cJSON_CreateString(mqtt->method));
cJSON_AddItemToObject(root,"id",cJSON_CreateString(mqtt->jsonid));
cJSON_AddItemToObject(root,"params",item);
cJSON_AddItemToObject(item,"CurrentTemperature",cJSON_CreateNumber(t));
cJSON_AddItemToObject(root,"version",cJSON_CreateString(mqtt->version));
char *msg = cJSON_Print(root);
printf("%s\n",msg);
if(!rc)
{
if(mosquitto_publish(mosq,NULL,mqtt->topic,strlen(msg),msg,0,NULL) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_publish failed: %s\n",strerror(errno));
return;
}
}
mosquitto_disconnect(mosq);
}
4.运行结果
打开阿里云可以看到温度在实时更新啦。
三.总结
此次MQTT的项目到此也是告一段落了。本次项目设计简要介绍了MQTT协议的发展历史、应用领域、通信模型、服务质量,并以mosquitto库作为研究对象,使用JSON格式和INI文件格式处理各类信息,采用MQTT协议高质量低能耗的物联网通信模式,运用调库使用研究了树莓派上的实时温度上报。最终结合阿里云平台的特点,编程实现了客户端查看任意环境的实时温度。 消防安全云端管理系统有着较大的代码量,调库使用也对代码提出了很高的模块化要求。独立完成整个项目,让我对物联网有着更深入的了解和认识,特别是对一个完整的物联网项目开发有了全面的了解。本次项目设计也为我后面的研究生留学带来了不小的启发,使我更加坚定地在物联网专业走下去。同时这些研究对于消防安全的检测以及物联网家电的应用有着重要的参考价值。