arm linux 移植 MQTT ,paho、mosquitto

前言

我们在这里做2件事情:

1)编译 paho.mqttmosquitto 2个开源项目的c版本库(mosquitto库没有用上)

2)编译好 依赖 paho.mqtt的库编写例程 + mosquitto 服务器。

host平台   :Ubuntu 16.04

arm 平台 : s5p6818

ssl : openssl-1.0.2t

MQTT-client : paho.mqtt.c

现在我们就来进行MQTT客户端的移植。

MQTT服务器(Broker)在很多 云服务器中自带了,没有特殊需求是不做移植的;而且就算做了 Broker 移植,也相对简单。

主机准备

使用以下脚本即可。

之前《搭建MQTT通信环境,并抓包测试》 没有介绍在:Linux下 编译运行 mosquitto 服务器,所以在下面的脚本中也有介绍,具体参考:make_mosquitto ()

##
#    Copyright By Schips, All Rights Reserved
#    https://gitee.com/schips/
#    File Name:  make.sh
#    Created  :  Sat 30 Nov 2019 01:56:37 PM CST
##
#!/bin/sh
BUILD_HOST=arm-linux-
ARM_GCC=${BUILD_HOST}gcc
ARM_CPP=${BUILD_HOST}g++
BASE=`pwd`
OUTPUT_PATH=${BASE}/install

OPENSSL=openssl-1.0.2t

make_dirs () {
    #为了方便管理,创建有关的目录
    cd ${BASE} && mkdir compressed install source -p
}

tget () { #try wget
    filename=`basename $1`
    echo "Downloading [${filename}]..."
    if [ ! -f ${filename} ];then
        wget $1
    fi

    echo "[OK] Downloaded [${filename}] "
}

tgit () { #try git and tar
    filename=`basename $1 | sed 's/.git//g'`

    echo "Clone [${filename}]..."
    if [ ! -f ${filename}.tgz ];then
        git clone $1
        rm ${filename}/.git* -rf
        echo "Making a tar file."
        tar -zcf ${filename}.tgz ${filename}
        rm ${filename} -rf
    fi

    echo "[OK] Cloned [${filename}] "
}

download_package () {
    cd ${BASE}/compressed
    #下载包
    tget  https://www.openssl.org/source/${OPENSSL}.tar.gz
    tgit  https://github.com/eclipse/paho.mqtt.c.git
    tgit  https://github.com/eclipse/mosquitto.git
}

tar_package () {
    cd ${BASE}/compressed
    ls * > /tmp/list.txt
    for TAR in `cat /tmp/list.txt`
    do
        tar -xf $TAR -C  ../source
    done
    rm -rf /tmp/list.txt
}

pre_make_ssl () {
    cd ${BASE}/source/${OPENSSL}
    startLine=`sed -n '/install_html_docs\:/=' Makefile`
    echo $startLine
    # 为了避免 多行结果
    for startline in $startLine
    do
        lineAfter=99
        endLine=`expr $startline + 999`
        sed -i $startline','$endLine'd' Makefile
        echo "install_html_docs:" >> Makefile
        echo "\t@echo skip by Schips" >> Makefile
        echo "install_docs:" >> Makefile
        echo "\t@echo skip by Schips" >> Makefile
        echo "# DO NOT DELETE THIS LINE -- make depend depends on it." >> Makefile
        break
    done
}

make_ssl () {
    # 编译安装 zlib
    cd ${BASE}/source/${OPENSSL}
    rm ${OUTPUT_PATH}/${OPENSSL} -rf
    echo "SSL ABOUT"
    CC=${ARM_GCC} ./Configure no-asm shared --prefix=${OUTPUT_PATH}/${OPENSSL}

    sed 's/-m64//g'  -i Makefile # 删除-m64 关键字 (arm-gcc 不支持)
    #sudo mv /usr/bin/pod2man /usr/bin/pod2man_bak
    #mv doc/apps /tmp/
    pre_make_ssl
    make && make install
}

make_paho_mqtt_c () {
    cd ${BASE}/source/paho.mqtt.c

    make CFLAGS+="-I ${OUTPUT_PATH}/${OPENSSL}/include" \
         LDFLAGS+="-L${OUTPUT_PATH}/${OPENSSL}/lib" \
         CC=${ARM_GCC} \
         prefix=${OUTPUT_PATH}/paho.mqtt.c

    # BUG: make install 不符合我们的意愿
    rm ${OUTPUT_PATH}/paho.mqtt.c -rf
    mkdir ${OUTPUT_PATH}/paho.mqtt.c/lib -p
    mkdir ${OUTPUT_PATH}/paho.mqtt.c/bin -p
    mkdir ${OUTPUT_PATH}/paho.mqtt.c/include -p

    cp ${BASE}/source/paho.mqtt.c/build/output/lib*    ${OUTPUT_PATH}/paho.mqtt.c/lib -d -v
    cp ${BASE}/source/paho.mqtt.c/build/output/samples ${OUTPUT_PATH}/paho.mqtt.c/bin -r -v
    cp ${BASE}/source/paho.mqtt.c/build/output/test    ${OUTPUT_PATH}/paho.mqtt.c/bin -r -v
    cp ${BASE}/source/paho.mqtt.c/src/*.h       ${OUTPUT_PATH}/paho.mqtt.c/include    -v

}

make_mosquitto () {
    cd ${BASE}/source/mosquitto

    sed -r -i "/WITH_DOCS:=/ s/.*/WITH_DOCS:=no/"                       config.mk
    sed -r -i "/WITH_SRV:=/ s/.*/WITH_SRV:=no/"                         config.mk
    sed -r -i "/WITH_UUID:=/ s/.*/WITH_UUID:=no/"                       config.mk
    sed -r -i "/WITH_WEBSOCKETS:=/ s/.*/WITH_WEBSOCKETS:=no/"           config.mk
    sed -r -i "/WITH_STRIP:=/ s/.*/WITH_STRIP:=yes/"                    config.mk
    # 不添加 ssl 支持
    sed -r -i "/WITH_TLS:=/ s/.*/WITH_TLS:=no/"                         config.mk
    sed -r -i "/WITH_TLS_PSK:=/ s/.*/WITH_TLS_PSK:=no/"                 config.mk

    # 指定工具链
    sed -i "N;2aCC=${BUILD_HOST}gcc"                                     config.mk
    sed -i "N;2aCXX=${BUILD_HOST}g++"                                    config.mk
    sed -i "N;2aCPP=${BUILD_HOST}g++"                                    config.mk
    sed -i "N;2aCC=${BUILD_HOST}gcc"                                     config.mk
    sed -i "N;2aAR=${BUILD_HOST}ar"                                      config.mk
    sed -i "N;2aLD=${BUILD_HOST}ld"                                      config.mk
    sed -r -i "/STRIP\?=/ s/.*/STRIP\?=${BUILD_HOST}strip/"     config.mk
    # 设置输出路径
    echo "DESTDIR=${OUTPUT_PATH}/mosquitto"                 >>  config.mk
    echo "prefix=\\" >>  config.mk

    make  && make install

    # 添加 ssl 支持 下编译的 mosquitto
   #sed -r -i "/WITH_TLS:=/ s/.*/WITH_TLS:=yes/"                        config.mk
   #sed -r -i "/WITH_TLS_PSK:=/ s/.*/WITH_TLS_PSK:=yes/"                config.mk
   #make \
   #    CFLAGS+="-I ${OUTPUT_PATH}/${OPENSSL}/include" \
   #    LDFLAGS+="-L${OUTPUT_PATH}/${OPENSSL}/lib -lssl -lcrypto"

   #make \
   #    CFLAGS+="-I ${OUTPUT_PATH}/${OPENSSL}/include" \
   #    LDFLAGS+="-L${OUTPUT_PATH}/${OPENSSL}/lib -lssl -lcrypto" \
   #    install
}

make_dirs
download_package
tar_package
make_ssl
make_paho_mqtt_c
make_mosquitto
exit $?

目标板准备

将install 下运行库拷贝到板子上,添加到路径即可。

测试

使用以下的测试程序,服务器根据自己的需求进行指定

有关的例程在 ${paho_mqtt_c}/src/samples 可以找到。

/*
#    Copyright By Schips, All Rights Reserved
#    https://gitee.com/schips/
#
#    File Name:  main.cpp
#    Created  :  Sun 09 Feb 2020 10:14:44 AM CST
*/

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <MQTTClient.h>   /* MQTT头文件的位置  */
#include <sys/time.h>
#include <termios.h>


#define ADDRESS     "tcp://localhost:1883"   /* 指定服务器的ip地址 这里使用本机的 mosquitto 进行测试使用  */
#define CLIENTID    "ExampleClientPub"
#define TOPIC       "MQTT Examples"
#define PAYLOAD     "Hello World!"
#define QOS         1
#define TIMEOUT     10000L

using namespace std;
int main(int argc, char* argv[])
{
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;

    MQTTClient_message *receivemsg = NULL ;
    char* topicName_rev = NULL;
    int   topicLen_rev;

    int rc;
    int i;

    MQTTClient_create(&client, ADDRESS, CLIENTID,
    MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 60;
    conn_opts.cleansession = 1;

    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(-1);
    }
    pubmsg.payload = (void *)PAYLOAD;
    pubmsg.payloadlen = strlen(PAYLOAD);
    pubmsg.qos = QOS;
    pubmsg.retained = 0;
    MQTTClient_subscribe(client, "test", 1);                        /* 订阅一个客户端 的一个话题*/
    usleep(10000);
    MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token); /* 发送消息 */
    printf("Waiting for up to %d seconds for publication of %s\n"
            "on topic %s for client with ClientID: %s\n",
            (int)(TIMEOUT/1000), PAYLOAD, TOPIC, CLIENTID);
    rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
    printf("Message with delivery token %d delivered\n", token);
    for(;;)
    {
               if(MQTTClient_isConnected(client) == true)   /* 检测连接状态 */
               {
                   printf("alive \n");
               }else{
                   printf(" no alive \n");
                   break;
               }
            rc = MQTTClient_receive(client,&topicName_rev, &topicLen_rev, &receivemsg,5000); /* 接收消息 */
               if(rc == MQTTCLIENT_SUCCESS)
               {
                printf("Message REv  %d delivered\n", rc);
                printf("topicName: %s  topicName_LEN: %d \n", topicName_rev,topicLen_rev);
                if(topicName_rev != NULL)                          /* 滤掉心跳包 */
                {
                    printf("topicName: ");
                    for(i=0;i<topicLen_rev;i++)
                    {
                        printf(" %c ", topicName_rev[i]);
                    }
                    printf("\n");
                    printf("Data: %s  len:%d msgid: %d \n",(char *)receivemsg->payload,receivemsg->payloadlen,receivemsg->msgid);
                     if(strcmp((char *)receivemsg->payload,"ESC") == 0)
                    {
                        printf("ESC \n");
                        break;
                    }
                }
            }

            usleep(10000);
            usleep(100000);
    }
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return rc;
}

编译命令

arm-linux-g++ -I ./paho.mqtt.c/src -L ./paho.mqtt.c/build/output/ -lpaho-mqtt3c main.cpp  -o client

#include #include #include #include "mosquitto.h"

int g_port = -1;

char *g_host[128] = {0};

int g_connected = 0;

struct mosquitto *g_mosq = NULL;

void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)

{

printf("my_subscribe_callback \n");

}

void my_connect_callback(struct mosquitto *mosq, void *obj, int rc)

{

printf("my_connect_callback. rc: %d \n", rc);

mosquitto_subscribe(mosq, NULL, "topic_01", 2);

g_connected = 1;

}

void my_disconnect_callback(struct mosquitto *mosq, void *obj, int result)

{

printf("my_disconnect_callback \n");

g_mosq = NULL;

g_connected = 0;

}

void my_publish_callback(struct mosquitto *mosq, void *obj, int mid)

{

printf("my_publish_callback. mid = %d \n", mid);

}

void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)

{

printf("my_message_callback \n");

printf("topic = %s, paylod = %s \n ", message->topic, message->payload);

}

void my_connect()

{

int rc;

mosquitto_lib_init();

g_mosq = mosquitto_new("client_name", true, NULL);

mosquitto_message_callback_set(g_mosq, my_message_callback);

mosquitto_connect_callback_set(g_mosq, my_connect_callback);

mosquitto_disconnect_callback_set(g_mosq, my_disconnect_callback);

mosquitto_subscribe_callback_set(g_mosq, my_subscribe_callback);

mosquitto_publish_callback_set(g_mosq, my_publish_callback);

rc = mosquitto_connect(g_mosq, g_host, g_port, 60);

}

void my_pubish(const char *topic, const char *message, int msg_len)

{

printf("mqtt_publish. topic = %s, message = %s \n", topic, message);

mosquitto_publish(g_mosq, NULL, topic, msg_len, message, 2, false);

}

int main(int argc, char *argv[])

{

int rc;

if (3 != argc) {

printf("Usage: ./mqtt_client mqtt_host mqtt_port \n");

exit(-1);

}

sprintf(g_host, "%s", argv[1]);
g_port = atoi(argv[2]);
my_connect();

while(1)
{
        rc = mosquitto_loop(g_mosq, -1, 1);
        if (rc) {
                printf("mqtt_portal: mosquitto_loop rc = %d \n", rc);
                sleep(3);
                mosquitto_reconnect(g_mosq);
        }
}

mosquitto_destroy(g_mosq);
mosquitto_lib_cleanup();
return 0;

}