前言
在上几篇文章中我们用MQTT.fx模拟客户端实现了与阿里云物联网平台的双向通信,接下来我们自己动手编程使用mosquitto库实现一个发布端
iniparser配置文件
iniparser简介
在上篇文章中阿里云配置文件另外之前发布和订阅的topic也要记下来:
[mqtt_server_addr]
host =iot-06z00c6bu42mu5l.mqtt.iothub.aliyuncs.com
port =1883
[user_passwd]
username =ds18b20&i5oqviRE56z
passwd =e84ffa7d0c4bf58c2d00bff75d222384f5400112c175917635339219021b9467
[client_id]
id =i5oqviRE56z.ds18b20|securemode=2,signmethod=hmacsha256,timestamp=1673699121744|
[sub_topic]
topic =/sys/i5oqviRE56z/ds18b20/thing/service/property/set
[pub_topic]
topic =/sys/i5oqviRE56z/ds18b20/thing/event/property/post
[ali_json]
method =thing.service.property.set
id =244036390
identifier =CurrentTemperature
version =1.0.0"
这些都是我们在项目中要用到的配置信息,具体应用在:
#客户端id
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。
cJSON
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
阿里云上payload must be json format 负载(消息)必须为cJSON格式。
因为使用的阿里云提供的标准物模型,所以消息格式应该遵循阿里云的规范。这就是我们要使用cJSON的原因。
要是已其他格式发送,阿里云收到了消息,但是因为参数不匹配,无法对消息进行解析,也就无法获取到温度消息
话不多说看项目实例
流程图
ini配置信息
mqtt_aliyun_conf_ini
[mqtt_server_addr]
host =iot-06z00c6bu42mu5l.mqtt.iothub.aliyuncs.com
port =1883
[user_passwd]
username =ds18b20&i5oqviRE56z
passwd =e84ffa7d0c4bf58c2d00bff75d222384f5400112c175917635339219021b9467
[client_id]
id =i5oqviRE56z.ds18b20|securemode=2,signmethod=hmacsha256,timestamp=1673699121744|
[sub_topic]
topic =/sys/i5oqviRE56z/ds18b20/thing/service/property/set
[pub_topic]
topic =/sys/i5oqviRE56z/ds18b20/thing/event/property/post
[ali_json]
method =thing.service.property.set
id =244036390
identifier =CurrentTemperature
version =1.0.0"
[KEEP_ALIVE]
alive =60
[ali_Qos]
Qos =0
mqtt_aliyun_conf.c
/********************************************************************************
* Copyright: (C) 2023 Yangpeng
* All rights reserved.
*
* Filename: get_temperature.h
* Description: This file
*
* Version: 1.0.0(2023年01月05日)
* Author: Yangpeng <1023769078@qq.com>
* ChangeLog: 1, Release initial version on "2023年01月05日 17时02分31秒"
*
********************************************************************************/
#include "iniparser.h"
#include "mqtt_aliyun_conf.h"
#include <stdio.h>
#include <string.h>
int get_mqtt_conf(char *ini_path,data_mqtt *mqtt,int type)
{
dictionary *ini=NULL;
const char *hostname;
const char *username;
int port;
const char *passwd;
const char *clientid;
const char *topic;
int Qos;
const char *method;
const char *jsonid;
const char *identifier;
const char *version;
if(!ini_path || !mqtt)
{
printf("invail input parameter in %s\n", __FUNCTION__) ;
return -1 ;
}
ini=iniparser_load(ini_path);
if( ini ==NULL)
{
printf("inipar_load failure\n");
return -1;
}
hostname =iniparser_getstring(ini, "mqtt_server_addr:host", DEFAULT_HOSTNAME);
port =iniparser_getint(ini,"mqtt_server_addr:port",DEFAULT_PORT);
username =iniparser_getstring(ini,"user_passwd:username",DEFAULT_USERNAME);
passwd =iniparser_getstring(ini,"user_passwd:passwd",DEFAULT_PASSWD);
clientid =iniparser_getstring(ini,"client_id:id",DEFAULT_CLIENTID);
identifier =iniparser_getstring(ini,"ali_json:identifier",DEFAULT_IDENTIFIER) ;
Qos =iniparser_getint(ini,"ali_Qos:Qos",DEFAULT_QOS);
if(type == SUB)
topic = iniparser_getstring(ini, "sub_topic:topic", DEFAULT_SUBTOPIC) ;
else if(type == PUB)
{
topic = iniparser_getstring(ini,"pub_topic:topic", DEFAULT_PUBTOPIC) ;
method = iniparser_getstring(ini,"json:method", DEFAULT_METHOD) ;
jsonid = iniparser_getstring(ini,"json:id", DEFAULT_JSONID) ;
version = iniparser_getstring(ini,"json:version", DEFAULT_VERSION) ;
}
mqtt->Qos=Qos;
strncpy(mqtt->hostname, hostname,BUF_SIZE);
mqtt->port = port;
strncpy(mqtt->username, username,BUF_SIZE);
strncpy(mqtt->passwd, passwd,BUF_SIZE);
strncpy(mqtt->clientid, clientid,BUF_SIZE);
strncpy(mqtt->topic, topic,BUF_SIZE);
if(type == PUB)
{
strncpy(mqtt->method, method,BUF_SIZE) ;
strncpy(mqtt->identifier, identifier,BUF_SIZE);
strncpy(mqtt->jsonid, jsonid,BUF_SIZE) ;
strncpy(mqtt->version, version,BUF_SIZE) ;
}
iniparser_freedict(ini);
return 0;
}
mqtt_aliyun_conf.h
********************************************************************************
* Copyright: (C) 2023 Yangpeng
* All rights reserved.
*
* Filename: get_temperature.h
* Description: This file
*
* Version: 1.0.0(2023年01月05日)
* Author: Yangpeng <1023769078@qq.com>
* ChangeLog: 1, Release initial version on "2023年01月05日 17时02分31秒"
*
********************************************************************************/
#ifndef MQTT_ALIYUN_CONF_H
#define MQTT_ALIYUN_CONF_H
#define BUF_SIZE 512
#define DEFAULT_CLIENTID "i5oqviRE56z.ds18b20|securemode=2,signmethod=hmacsha256,timestamp=1673699121744|"
#define DEFAULT_USERNAME "ds18b20&i5oqviRE56z"
#define DEFAULT_PASSWD "e84ffa7d0c4bf58c2d00bff75d222384f5400112c175917635339219021b9467"
#define DEFAULT_HOSTNAME "iot-06z00c6bu42mu5l.mqtt.iothub.aliyuncs.com"
#define DEFAULT_PORT 1883
#define DEFAULT_SUBTOPIC "/sys/i5oqviRE56z/ds18b20/thing/service/property/set"
#define DEFAULT_PUBTOPIC "/sys/i5oqviRE56z/ds18b20/thing/event/property/post"
#define DEFAULT_QOS 0
#define DEFAULT_METHOD "thing.service.property.set"
#define DEFAULT_JSONID "244036390"
#define DEFAULT_IDENTIFIER "CurrentTemperature"
#define DEFAULT_VERSION "1.0.0"
#define KEEP_ALIVE 60
enum{
SUB,
PUB
};
typedef struct data_mqtt
{
char hostname[BUF_SIZE] ;
int port ;
char username[BUF_SIZE] ;
char passwd[BUF_SIZE] ;
char clientid[BUF_SIZE] ;
char topic[BUF_SIZE] ;
int Qos;
char method[BUF_SIZE] ;
char jsonid[BUF_SIZE] ;
char identifier[BUF_SIZE] ;
char version[BUF_SIZE] ;
}data_mqtt;
int get_mqtt_conf(char *ini_path,data_mqtt *mqtt,int type);
#endif
ds18b20获取温度模块
/*********************************************************************************
* Copyright: (C) 2023 Yangpeng
* All rights reserved.
*
* Filename: get_temperature.c
* Description: This file get temperature
*
* Version: 1.0.0(2023年01月05日)
* Author: Yangpeng <1023769078@qq.com>
* ChangeLog: 1, Release initial version on "2023年01月05日 16时39分48秒"
*
********************************************************************************/
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<dirent.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include"get_temperature.h"
int get_temperature(float *temp)
{
char path[64]="/sys/bus/w1/devices/";
char buf[128];
char chip[32];
DIR *dirp=NULL;
struct dirent *direntp=NULL;
int fd=-1;
char *ptr=NULL;
int found=0;
if(NULL==(dirp=opendir(path)))
{
printf("opendir failure:%s\n",strerror(errno));
return -1;
}
memset(chip,0,sizeof(chip));
while((direntp=readdir(dirp))!=NULL)
{
if(strstr(direntp->d_name,"28-"))
{
strcpy(chip,direntp->d_name);
found=1;
}
}
closedir(dirp);
if(!found)
{
printf("readdir failure\n");
return -2;
}
strncat(path,chip,sizeof(path)-strlen(path));
strncat(path,"/w1_slave",sizeof(path)-strlen(path));
if((fd=open(path,O_RDONLY))<0)
{
printf("open file failure:%s\n",strerror(errno));
return -3;
}
memset(buf,0,sizeof(buf));
if(read(fd,buf,sizeof(buf))<0)
{
printf("read file failure:%s\n",strerror(errno));
return -4;
}
ptr=strstr(buf,"t=");
ptr+=2;
if(!ptr)
{
printf("error:can not ptr\n");
return -5;
}
*temp=atof(ptr)/1000;
printf("temprature:%f\n",*temp);
close(fd);
return 0;
}
/********************************************************************************
* Copyright: (C) 2023 Yangpeng
* All rights reserved.
*
* Filename: get_temperature.h
* Description: This file
*
* Version: 1.0.0(2023年01月05日)
* Author: Yangpeng <1023769078@qq.com>
* ChangeLog: 1, Release initial version on "2023年01月05日 17时02分31秒"
*
********************************************************************************/
#ifndef _GET_TEMPERATURE_H_
#define _GET_TEMPERATURE_H_
int get_temperature(float *temp);
#endif
发布端代码
/********************************************************************************
* Copyright: (C) 2023 Yangpeng
* All rights reserved.
*
* Filename: get_temperature.h
* Description: This file
*
* Version: 1.0.0(2023年01月05日)
* Author: Yangpeng <1023769078@qq.com>
* ChangeLog: 1, Release initial version on "2023年01月05日 17时02分31秒"
*
********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <libgen.h>
#include <getopt.h>
#include <string.h>
#include <mosquitto.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <signal.h>
#include <time.h>
#include "get_temperature.h"
#include "mqtt_aliyun_conf.h"
#include "cJSON.h"
#include "dictionary.h"
#include "iniparser.h"
#define PROG_VERSION "1.0.0"
#define INI_PATH "./mqtt_aliyun_conf.ini"
static int g_stop=0;
/* 调用信号 */
void sig_handler(int sig_num)
{
if(sig_num == SIGUSR1)
g_stop = 1;
}
int get_time(char *tim);
void connect_callback(struct mosquitto *mosq,void *obj,int rc);
static inline void print_usage(char *progname);
int main(int argc,char **argv)
{
int rv;
int port=1883;
char *hostname=NULL;
char *username=NULL;
char *clientid=NULL;
char *topic=NULL;
char *passwd=NULL;
char *program_name=basename(argv[0]);
int daemon_run=0;
int opt = -1;
int log_fd;
data_mqtt mqtt;
struct mosquitto *mosq = NULL;
struct option options[] =
{
{"daemon",no_argument,NULL,'d'},
{"topic", required_argument,NULL,'t'},
{"hostname", required_argument,NULL,'H'},
{"clientid", required_argument, NULL, 'i'},
{"port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{"username",required_argument,NULL,'u'},
{"passwd",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':
hostname = optarg;
break;
case 'u':
username = optarg;
break;
case 'P':
passwd = optarg;
break;
case 'd':
daemon_run = 1;
break;
case 'p':
port = atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
default:
break;
}
}
/* 创建日志 */
if(daemon_run)
{
printf("program %s running in backgrund\n", program_name);
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_mqtt_conf(INI_PATH,&mqtt,PUB);
/* MQTT 初始化 */
mosquitto_lib_init();
/* 创建MQTT 客户端 */
mosq = mosquitto_new(mqtt.clientid,true,(void *)&mqtt);
if(!mosq)
{
printf("mosquitto_new() failed: %s\n",strerror(errno));
goto cleanup;
return -1;
}
/* 设置账号密码 */
if(mosquitto_username_pw_set(mosq,mqtt.username,mqtt.passwd) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_username_pw_set failed: %s\n",strerror(errno));
goto cleanup;
}
/* 设置回调函数 */
mosquitto_connect_callback_set(mosq,connect_callback);
while(!g_stop)
{
/* 连接MQTT服务器,ip,端口,时间 */
if(mosquitto_connect(mosq, mqtt.hostname,mqtt.port,KEEP_ALIVE) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_connect() failed: %s\n",strerror(errno));
goto cleanup;
}
printf("connect successful\n");
/*无阻塞 断线连接 */
mosquitto_loop_forever(mosq,-1,1);
sleep(3);
}
/* 释放客户端,清除 */
cleanup:
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
/* 获取时间 */
int get_time(char *tim)
{
time_t t;
struct tm *p;
time(&t);
p = gmtime(&t);
snprintf(tim, 32, "%04d-%02d-%02d %02d:%02d:%02d", 1900+p->tm_year,1+p->tm_mon, p->tm_mday, (p->tm_hour + 8), p->tm_min, p->tm_sec);
return 0 ;
}
/* 回调函数*/
void connect_callback(struct mosquitto *mosq,void *obj,int rc)
{
float tem = 0.00000000;
char tim[64];
char *msg;
cJSON * root = cJSON_CreateObject();
cJSON * item = cJSON_CreateObject();
memset(root,0,sizeof(root));
memset(item,0,sizeof(item));
memset(tim,0,sizeof(tim));
if(get_temperature(&tem)<0)
{
printf("get_temperature failed:%s\n",strerror(errno));
return ;
}
if(get_time(tim)<0)
{
printf("get_time failured:%s\n",strerror(errno));
return ;
}
if(!obj)
{
printf("invalid_argument in %s\n",__FUNCTION__);
return;
}
struct data_mqtt *mqtt = (data_mqtt *)obj;
cJSON_AddItemToObject(root, "method", cJSON_CreateString(mqtt->method));
cJSON_AddItemToObject(root, "id", cJSON_CreateString(mqtt->jsonid));
cJSON_AddItemToObject(root, "params",item);
cJSON_AddItemToObject(root,"time",cJSON_CreateString(tim));
cJSON_AddItemToObject(item, "CurrentTemperature", cJSON_CreateNumber(tem));
cJSON_AddItemToObject(root, "version", cJSON_CreateString(mqtt->version));
msg=cJSON_Print(root);
printf("%s\n",msg);
if(!rc)
{
if(mosquitto_publish(mosq,NULL,mqtt->topic,strlen(msg),msg,mqtt->Qos,NULL) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_publish failed: %s\n",strerror(errno));
return;
}
}
printf("mqtt_publish successful!\n");
mosquitto_disconnect(mosq);
}
/* 帮助信息 */
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 hostname.\n");
printf("-p(--port): sepcify port.\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(--passwd): sepcify password of the username.\n");
printf("-t(--topic): sepcify topic of the client.\n");
printf("-d(--daemon): running in backgrund.\n");
}
makefile
CC = gcc
LDFLAGS+=-lm -lmosquitto -lcjson
COMPILE=*.c
all:${COMPILE}
${CC} ${COMPILE} -o mqtt_pub ${LDFLAGS}
clean:
rm -rf mqtt_pub
运行结果展示
希望这篇博客能帮助到你