【附源码】IMX6U嵌入式Linux开发板连接阿里云--MQTT协议

演示

IMX6U嵌入式Linux开发板连接阿里云

阿里云创建设备&&获取LinkSDK

如果还不知道怎么在阿里云创建设备和获取连接阿里云的LinkSDK的话,先看这篇文章,再到这里。看这篇文章的时候,麻烦将下方文章打开对照着看,因为一些重复的内容,就不重新再粘贴了。

一篇文章将带你从0到1让Linux系统连接阿里云--MQTT协议【傻瓜式教程】-CSDN博客

代码实现

将LinkSDK整个目录移植到自己的项目中,以下工程是我移植后的样子。Hal是开发板上的硬件驱动,net是阿里云相关的应用程序。

Makefile 如下:需要将SDK相关的路径和文件包含进去。

复制代码
# 定义交叉编译工具链
#CC = $(CROSS_COMPILE)gcc

# 定义源文件和目标文件
PROG_FILE := app_main.c
PROG_OBJS := $(patsubst %.c,%.o,$(PROG_FILE))
PROG = app_main

# SDK 相关路径和文件
SDK_ROOT = $(shell pwd)/LinkSDK
SDK_DIR = $(SDK_ROOT)/core $(SDK_ROOT)/core/sysdep $(SDK_ROOT)/core/utils $(SDK_ROOT)/portfiles/aiot_port $(SDK_ROOT)/external $(SDK_ROOT)/external/mbedtls/library $(SDK_ROOT)/components/data-model
SDK_INC = -I$(SDK_ROOT)/external/mbedtls/include $(foreach dir, $(SDK_DIR), -I$(dir))
SDK_FILES = $(foreach dir, $(SDK_DIR), $(wildcard $(dir)/*.c))
SDK_OBJS = $(patsubst %.c,%.o,$(SDK_FILES))
SDK_LIBS = $(SDK_ROOT)/output/lib/libaiot.a -lpthread

# 添加 Hal 文件夹
HAL_DIR = Hal
HAL_INC = -I$(HAL_DIR)
HAL_FILES = $(wildcard $(HAL_DIR)/*.c)
HAL_OBJS = $(patsubst %.c,%.o,$(HAL_FILES))

# 添加 net 文件夹
NET_DIR = net
NET_INC = -I$(NET_DIR)
NET_FILES = $(wildcard $(NET_DIR)/*.c)
NET_OBJS = $(patsubst %.c,%.o,$(NET_FILES))

# 添加当前目录的头文件路径
INC_DIR = -I.

# 添加标准库路径
STD_INC = -I$(OECORE_TARGET_SYSROOT)/usr/include

CFLAGS += $(SDK_INC) $(HAL_INC) $(INC_DIR) $(STD_INC) $(NET_INC)

# 生成主程序
main: $(PROG_OBJS) $(SDK_OBJS) $(HAL_OBJS) $(NET_OBJS)
	$(CC) $(CFLAGS) -o $(PROG) $(PROG_OBJS) $(SDK_OBJS) $(HAL_OBJS) $(SDK_LIBS) $(NET_OBJS)

# 清理编译生成的文件
clean:
	rm -f *.o $(PROG) $(SDK_OBJS) $(HAL_OBJS) $(NET_OBJS)

移植好LinkSDK后,在其目录下的demos中将mqtt_basic_demo.c复制一份出来。我将其修改成mqtt_aliiyun.c放在net目录下。

将三元组表修改成自己设备对应的,这一块不清楚的可以在开始提到的文章中了解到。需要订阅和发布信息时,需要在sdk_test中将sub_topic 和 pub_topic改成自己的设备名字,云平台接收到信息可以在mqtt_payload_handle函数中进行处理,其他的内容自己看代码吧,其中有详细的注释。

cpp 复制代码
/*
 * 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
 *
 * + 一个线程用于保活长连接
 * + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
 *
 * 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
 *
 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "mqtt_aliyun.h"


/* TODO: 替换为自己设备的三元组 */
char *product_key       = "k1h2*******";
char *device_name       = "Lin******";
char *device_secret     = "00708c34c4e4b75*******";
char  *mqtt_host = "iot-******wea0.mqtt.iothub.aliyuncs.com";
const uint16_t port = 8883;
//const uint16_t port = 1883;

/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;

/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;

static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;


int mqtt_payload_handle(char *payload, int payload_len)
{
    if(payload_len > 0)
    {
        payload[payload_len] = '\0';
    }

    if(!strcmp(payload, "led_on"))
    {
        set_led_mode("on");
        return LED_ON;
    } 
    else if(!strcmp(payload, "led_off"))
    {
        set_led_mode("off");
        return LED_OFF;
    }
    else if(!strcmp(payload, "beep_on"))
    {
        set_beep_mode("on");
        return BEEP_ON;
    }
    else if(!strcmp(payload, "beep_off"))
    {
        set_beep_mode("off");
        return BEEP_OFF;
    }

    return 0;
}

/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
 *
 * 例如: [1577589489.033][LK-0317] mqtt_basic_demo&gb80sFmX7yX
 *
 * 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h
 *
 */

/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
    printf("%s", message);
    return 0;
}

/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
    switch (event->type) {
        /* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
        case AIOT_MQTTEVT_CONNECT: {
            printf("AIOT_MQTTEVT_CONNECT\n");
            /* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
        }
        break;

        /* SDK因为网络状况被动断连后, 自动发起重连已成功 */
        case AIOT_MQTTEVT_RECONNECT: {
            printf("AIOT_MQTTEVT_RECONNECT\n");
            /* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
        }
        break;

        /* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
        case AIOT_MQTTEVT_DISCONNECT: {
            char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
                          ("heartbeat disconnect");
            printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
            /* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
        }
        break;

        default: {

        }
    }
}

/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
    switch (packet->type) {
        case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
            printf("heartbeat response\n");
            /* TODO: 处理服务器对心跳的回应, 一般不处理 */
        }
        break;

        case AIOT_MQTTRECV_SUB_ACK: {
            printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
                   -packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
            /* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
        }
        break;

        case AIOT_MQTTRECV_PUB: {
            //printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
            //printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
            /* TODO: 处理服务器下发的业务报文 */
            printf("payload = %.*s\n",  packet->data.pub.payload_len, packet->data.pub.payload);
            mqtt_payload_handle(packet->data.pub.payload, packet->data.pub.payload_len);
        }
        break;

        case AIOT_MQTTRECV_PUB_ACK: {
            printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
            /* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
        }
        break;

        default: {

        }
    }
}

/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
    int32_t res = STATE_SUCCESS;

    while (g_mqtt_process_thread_running) {
        res = aiot_mqtt_process(args);
        if (res == STATE_USER_INPUT_EXEC_DISABLED) {
            break;
        }
        sleep(1);
    }
    return NULL;
}

/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
    int32_t res = STATE_SUCCESS;

    while (g_mqtt_recv_thread_running) {
        res = aiot_mqtt_recv(args);
        if (res < STATE_SUCCESS) {
            if (res == STATE_USER_INPUT_EXEC_DISABLED) {
                break;
            }
            sleep(1);
        }
    }
    return NULL;
}

int sdk_test(void)
{
    int32_t     res = STATE_SUCCESS;
    void       *mqtt_handle = NULL;
    aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */

    /* 配置SDK的底层依赖 */
    aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
    /* 配置SDK的日志输出 */
    aiot_state_set_logcb(demo_state_logcb);

    /* 创建SDK的安全凭据, 用于建立TLS连接 */
    memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
    cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;  /* 使用RSA证书校验MQTT服务端 */
    cred.max_tls_fragment = 16384; /* 最大的分片长度为16K, 其它可选值还有4K, 2K, 1K, 0.5K */
    cred.sni_enabled = 1;                               /* TLS建连时, 支持Server Name Indicator */
    cred.x509_server_cert = ali_ca_cert;                 /* 用来验证MQTT服务端的RSA根证书 */
    cred.x509_server_cert_len = strlen(ali_ca_cert);     /* 用来验证MQTT服务端的RSA根证书长度 */

    /* 创建1个MQTT客户端实例并内部初始化默认参数 */
    mqtt_handle = aiot_mqtt_init();
    if (mqtt_handle == NULL) {
        printf("aiot_mqtt_init failed\n");
        return -1;
    }

    /* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
    /*
    {
        memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
        cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
    }
    */

    /* 配置MQTT服务器地址 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
    /* 配置MQTT服务器端口 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
    /* 配置设备productKey */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
    /* 配置设备deviceName */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
    /* 配置设备deviceSecret */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
    /* 配置网络连接的安全凭据, 上面已经创建好了 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
    /* 配置MQTT默认消息接收回调函数 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
    /* 配置MQTT事件回调函数 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);

    /* 与服务器建立MQTT连接 */
    res = aiot_mqtt_connect(mqtt_handle);
    if (res < STATE_SUCCESS) {
        /* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
        aiot_mqtt_deinit(&mqtt_handle);
        printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
        printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
        return -1;
    }

    /* MQTT 订阅topic功能示例, 请根据自己的业务需求进行使用 */
    {
        char *sub_topic = "/k1h2******7/Linux_ATK/user/get";

        res = aiot_mqtt_sub(mqtt_handle, sub_topic, NULL, 1, NULL);
        if (res < 0) {
            printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
            return -1;
        }
    } 

    /* MQTT 发布消息功能示例, 请根据自己的业务需求进行使用 */
    {
        char *pub_topic = "/k1h2h*******/Linux_ATK/user/update";
        char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}";

        res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0);
        if (res < 0) {
            printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
            return -1;
        }
    }

    /* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
    g_mqtt_process_thread_running = 1;
    res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
    if (res < 0) {
        printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
        return -1;
    }

    /* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
    g_mqtt_recv_thread_running = 1;
    res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
    if (res < 0) {
        printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
        return -1;
    }

    /* 主循环进入休眠 */
    while (1) {
        sleep(1);
    }

    /* 断开MQTT连接, 一般不会运行到这里 */
    g_mqtt_process_thread_running = 0;
    g_mqtt_recv_thread_running = 0;
    sleep(1);
    pthread_join(g_mqtt_process_thread, NULL);
    pthread_join(g_mqtt_recv_thread, NULL);

    res = aiot_mqtt_disconnect(mqtt_handle);
    if (res < STATE_SUCCESS) {
        aiot_mqtt_deinit(&mqtt_handle);
        printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
        return -1;
    }

    /* 销毁MQTT实例, 一般不会运行到这里 */
    res = aiot_mqtt_deinit(&mqtt_handle);
    if (res < STATE_SUCCESS) {
        printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
        return -1;
    }

    return 0;
}

完整工程资料

链接:https://pan.baidu.com/s/1g2bRUTL0rzlW7YaFBSyrig?pwd=pg62

提取码:pg62

--来自百度网盘超级会员V5的分享

相关推荐
AOwhisky19 小时前
Kubernetes 学习笔记:集群管理、命名空间与 Pod 基础
linux·运维·笔记·学习·云原生·kubernetes
小龙在慢慢变强..20 小时前
目录结构(FHS 标准)
linux·运维·服务器
2035去旅行20 小时前
嵌入式开发,如何选择C标准库
linux·arm开发
刘延林.20 小时前
win11系统下通过 WSL2 安装Ubuntu 24.04 使用RTX 5080 GPU
linux·运维·ubuntu
CodeOfCC21 小时前
Linux 嵌入式arm64安装openclaw
linux·运维·服务器
宵时待雨1 天前
linux笔记归纳3:linux开发工具
linux·运维·笔记
magrich1 天前
安装NoMachine并解决无外接显示器桌面黑屏
linux·运维·服务器
fish_xk1 天前
Linus基础指令
linux·服务器
宁波阿成1 天前
在ubuntu22.04源码级安装sub2api
linux·运维·ubuntu·ai·api·token·中转站
charlie1145141911 天前
嵌入式Linux驱动开发(7) 从虚拟设备到真实硬件 —— LED驱动硬件基础
linux·开发语言·驱动开发·内核·c