无操作系统设备接入

Link SDK支持跨平台跨系统运行,默认支持Linux(POSIX接口)设备,其它环境的设备都需要移植。无操作系统的设备资源较少,移植难度较大。本文介绍无操作系统的单片机设备,使用C Link SDK接入物联网平台的流程和示例。

前提条件

步骤一:接口移植

重要

无操作系统设备可以理解为只有一个任务或线程,该任务的栈大小需要足够支撑SDK运行。

实现Link SDK运行依赖的底层接口后,设备才可以正常运行,下文介绍不同接口实现方案。

接口类型

接口列表

实现方案

内存管理

core_sysdep_malloc

  • 可以使用libc库的内存管理,对应malloc/free函数。

    重要

    单片机需要配置堆大小(heap)。

  • 也可以使用第三方更适合单片机的内存管理库。例如:libmemory

core_sysdep_free

系统时间

core_sysdep_time

建议使用单片机的系统嘀嗒实现,获取系统运行时间,单位:ms。

系统睡眠

core_sysdep_sleep

裸单片机环境没有睡眠接口,也不会存在多任务并发处理,所以不用让出MCU的运行权限,可以使用轮询的方式模拟睡眠。

随机值

core_sysdep_rand

以系统时间为随机因子,使用libc库的rand函数生成随机值。

互斥锁

core_sysdep_mutex_init

  1. core_sysdep_mutex_init实现结果为空,返回非NULL任意指针。

  2. 其他接口实现结果为空,无返回值。

core_sysdep_mutex_lock

core_sysdep_mutex_unlock

core_sysdep_mutex_deinit

网络连接

core_sysdep_network_***

阻塞式接口,有、无操作系统实现相同。

步骤二:应用开发

对于有操作系统的设备,应用开发建议多任务处理,因为接口会阻塞,单任务处理会影响其他的业务处理。但对于无操作系统设备,只有一个任务,解决Link SDK接口阻塞的方案是降低阻塞的时间,尽量不影响业务的处理流程。

image

MQTT模块涉及到阻塞的接口

接口类

接口名

说明

MQTT建连

aiot_mqtt_connect

执行建连会阻塞,通过设置建连和接收超时处理,示例值:2s。

MQTT接收

aiot_mqtt_recv

执行MQTT的消息接收、回调执行、心跳超时重连会阻塞,通过设置网络接收超时处理,示例值:1s。

MQTT处理

aiot_mqtt_process

执行MQTT的心跳、QoS 1消息会阻塞。

配置超时时间

    /* 配置MQTT超时时间 */
    int32_t recv_timeout = 1000, connect_timeout = 2000;
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&recv_timeout);
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_CONNECT_TIMEOUT_MS, (void *)&connect_timeout);

示例

环境说明

image

环境配置

说明

操作系统

无。

硬件设备

STM32L476 + 移远EC200S模组。

接口连接

STM32串口1连接模组串口,串口波特率为115200。

开发环境

MDK-Arm

参数配置

堆大小为80 KB、栈大小为1.5 KB。

移植实现示例

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "os_net_interface.h"
#include "stm32l4xx_hal.h"

/**
 * @brief 获取当前的时间戳,SDK用于差值计算
 */
uint64_t __time(void) {
    return (uint64_t)HAL_GetTick();
}
/**
 * @brief 睡眠指定的毫秒数
 */
void __sleep(uint64_t time_ms) {
	  uint64_t start = __time();
	  while(__time() - start < time_ms) {
			;
		}
}
/**
 * @brief 随机数生成方法
 */
void __rand(uint8_t *output, uint32_t output_len) {
    uint32_t idx = 0, bytes = 0, rand_num = 0;

    srand(__time());
    for (idx = 0; idx < output_len;) {
        if (output_len - idx < 4) {
            bytes = output_len - idx;
        } else {
            bytes = 4;
        }
        rand_num = rand();
        while (bytes-- > 0) {
            output[idx++] = (uint8_t)(rand_num >> bytes * 8);
        }
    }
}
/**
 * @brief 创建互斥锁
 */
void*  __mutex_init(void) {
    return (void *)0xFFFFFFFF;
}
/**
 * @brief 申请互斥锁
 */
void __mutex_lock(void *mutex) {
    
}
/**
 * @brief 释放互斥锁
 */
void __mutex_unlock(void *mutex) {

}
/**
 * @brief 销毁互斥锁
 */
void __mutex_deinit(void **mutex) {
    if (mutex == NULL || *mutex == NULL) {
        return;
    }
    *mutex = NULL;
}

aiot_os_al_t  g_aiot_freertos_api = {
    .malloc = malloc,
    .free = free,
    .time = __time,
    .sleep = __sleep,
    .rand = __rand,
    .mutex_init = __mutex_init,
    .mutex_lock = __mutex_lock,
    .mutex_unlock = __mutex_unlock,
    .mutex_deinit = __mutex_deinit,
};

示例工程

  1. 下载示例工程

  2. 使用MDK-Arm,打开工程./LinkSDK/portfiles/aiot_port/project/stm32_noneos/MDK-ARM/L476.uvprojx

    MDK-Arm的使用说明,请参见MDK-Arm

  3. 如果需要适配不同环境接口,修改文件./LinkSDK/portfiles/aiot-port/project/stm32_noneos/Core/os_none_impl.c

    修改方法,请参见步骤一:接口移植

  4. 打开./LinkSDK/portfiles/aiot-port/project/stm32_noneos/Core/mqtt_at_basic_demo.c,配置设备认证信息。

    参数

    示例

    说明

    product_key

    a18wP******

    设备认证信息。开发准备时,您获取的设备证书。

    您也可以在物联网平台控制台的设备详情页面查看设备的认证信息。

    device_name

    LightSwitch

    device_secret

    uwMTmVA**********DY6cHxxB******

    mqtt_host

    a18wP******.iot-as-mqtt.cn-shanghai.aliyuncs.com

    设备的MQTT接入域名。

    新旧版公共实例和企业版实例的接入域名信息,请参见查看实例终端节点

  5. 编译工程文件,然后下载可执行文件到开发板运行。

    • 成功运行后,您可以在设备端查看日志。

      • 模组初始化:

        >>> AT
        >>> AT
        <<<
        RDY
        >>> AT
        <<< AT
        OK
        >>> AT+QIACT=1
        <<<
        OK
        >>> AT+QIACT?
        <<<
        +QIACT: 1,1,1,"10.13.***.***"
        OK
      • 建立连接:

        [22.400][LK-0313] MQTT user calls aiot_mqtt_connect api, connect
        [22.433][LK-0317] LightSwitch&a18wP******
        [22.444][LK-0318] B4C45425D73E24B2935D73C1E98B6079A630FBE03F61E2A2031CEE7867D4D0D7
        >>> AT+QIOPEN=1,1,"TCP","a18wP******.iot-as-mqtt.cn-shanghai.aliyuncs.com",443,0,1
        <<<
        OK
        <<<
        +QIOPEN: 1,0
        >>> AT+QISEND=1,286
        <<<
        > >>> <<<
        SEND OK
        <<<
        +QIURC: "recv",1,4
         id 0, len 4, res 20
        [22.744][LK-0313] MQTT connect success in 341 ms
        AIOT_MQTTEVT_CONNECT
      • 数据上报:

        [27.766][LK-0309] pub: /sys/a18wP******/LightSwitch/thing/event/property/post
        [LK-030A] > 7B 22 69 64 22 3A 22 31  22 2C 22 76 65 72 73 69 | {"id":"1","versi
        [LK-030A] > 6F 6E 22 3A 22 31 2E 30  22 2C 22 70 61 72 61 6D | on":"1.0","param
        [LK-030A] > 73 22 3A 7B 22 4C 69 67  68 74 53 77 69 74 63 68 | s":{"LightSwitch
        [LK-030A] > 22 3A 30 7D 7D                                   | ":0}}
        >>> AT+QISEND=1,120
        <<<
        > >>> 0v<<<
        SEND OK
        <<<
        +QIURC: "recv",1,178
        0?id 0, len 178, res 22
      • 数据接收:

        [27.933][LK-0309] pub: /sys/a18wP******/LightSwitch/thing/event/property/post_reply
        [LK-030A] < 7B 22 63 6F 64 65 22 3A  32 30 30 2C 22 64 61 74 | {"code":200,"dat
        [LK-030A] < 61 22 3A 7B 7D 2C 22 69  64 22 3A 22 31 22 2C 22 | a":{},"id":"1","
        [LK-030A] < 6D 65 73 73 61 67 65 22  3A 22 73 75 63 63 65 73 | message":"succes
        [LK-030A] < 73 22 2C 22 6D 65 74 68  6F 64 22 3A 22 74 68 69 | s","method":"thi
        [LK-030A] < 6E 67 2E 65 76 65 6E 74  2E 70 72 6F 70 65 72 74 | ng.event.propert
        [LK-030A] < 79 2E 70 6F 73 74 22 2C  22 76 65 72 73 69 6F 6E | y.post","version
        [LK-030A] < 22 3A 22 31 2E 30 22 7D                          | ":"1.0"}
        pub, qos: 0, topic: /sys/a18wP******/LightSwitch/thing/event/property/post_reply
    • 您也可以在物联网平台查看日志,具体操作,请参见云端运行日志