Connect devices that do not have an operating system

Updated at:
Copy as MD

The Link SDK supports cross-platform operations and by default, supports Linux devices that use POSIX-based APIs. For devices in other environments, you must port the SDK. Porting can be difficult for devices without an operating system because they have limited resources. This topic describes how to use the C Link software development kit (SDK) to connect a single-chip microcomputer that does not have an operating system to the IoT Platform.

Prerequisites

Step 1: Port the interfaces

Important

A device without an operating system can be considered to have only one task or thread. The stack size of this task must be large enough to support the SDK runtime.

The device can run properly only after you implement the underlying interfaces that the Link SDK depends on. The following table describes how to implement different interfaces.

Interface type

Interface list

Implementation method

Memory management

core_sysdep_malloc

  • Use the memory management of the libc library, which corresponds to the malloc and free functions.

    Important

    You must configure the heap size for the single-chip microcomputer.

  • Alternatively, use a third-party memory management library that is better suited for single-chip microcomputers, such as libmemory.

core_sysdep_free

System time

core_sysdep_time

Use the system tick of the single-chip microcomputer to get the system runtime in milliseconds (ms).

System sleep

core_sysdep_sleep

A bare-metal microcontroller environment has no sleep interface and does not handle concurrent processing. You do not need to yield MCU execution control. Use polling to simulate sleep.

Random value

core_sysdep_rand

Use the system time as a random seed and use the rand function of the libc library to generate a random value.

Mutex lock

core_sysdep_mutex_init

  1. The implementation of core_sysdep_mutex_init is empty and returns any non-NULL pointer.

  2. The implementations of the other interfaces are empty and have no return value.

core_sysdep_mutex_lock

core_sysdep_mutex_unlock

core_sysdep_mutex_deinit

Network connectivity

core_sysdep_network_***

These are blocking interfaces. The implementation is the same for environments with or without an operating system.

Step 2: Develop the application

For devices with an operating system, you can use multitasking in your application because the interfaces are blocking and a single task can affect other operations. Devices without an operating system have only one task. To handle blocking interfaces in the Link SDK, you can reduce the blocking time to minimize the impact on your business logic.

image

Blocking interfaces in the MQTT module

Interface class

Interface name

Description

MQTT connection

aiot_mqtt_connect

This interface blocks when establishing a connection. Set connection and receive timeouts to handle blocking. Example value: 2 s.

MQTT receive

aiot_mqtt_recv

This interface blocks during Message Queuing Telemetry Transport (MQTT) message receiving, callback execution, and reconnection after a heartbeat timeout. Set a network receive timeout to handle blocking. Example value: 1 s.

MQTT processing

aiot_mqtt_process

This interface blocks when processing MQTT heartbeats and Quality of Service (QoS) 1 messages.

Configure the timeout period

    /* Configure the MQTT timeout period. */
    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);

Example

Environment description

image

Environment configuration

Description

Operating system

None.

Hardware model

STM32L476 + Quectel EC200S module.

Interface connection

Connect UART1 of the STM32 to the UART of the module. The baud rate is 115200.

Development environment

MDK-Arm.

Parameter settings

Heap size: 80 KB. Stack size: 1.5 KB.

Porting implementation example

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

/**
 * @brief Obtains the current timestamp. The SDK uses it to calculate differences.
 */
uint64_t __time(void) {
    return (uint64_t)HAL_GetTick();
}
/**
 * @brief Sleeps for the specified number of milliseconds.
 */
void __sleep(uint64_t time_ms) {
	  uint64_t start = __time();
	  while(__time() - start < time_ms) {
			;
		}
}
/**
 * @brief Method for random number generation.
 */
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 Creates a mutex.
 */
void*  __mutex_init(void) {
    return (void *)0xFFFFFFFF;
}
/**
 * @brief Locks a mutex.
 */
void __mutex_lock(void *mutex) {
    
}
/**
 * @brief Unlocks a mutex.
 */
void __mutex_unlock(void *mutex) {

}
/**
 * @brief Destroys a mutex.
 */
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,
};

Example project

  1. Download the example project.

  2. You can use MDK-Arm to open the project at ./LinkSDK/portfiles/aiot_port/project/stm32_noneos/MDK-ARM/L476.uvprojx.

    For more information about how to use MDK-Arm, see MDK-Arm.

  3. To adapt the interfaces for different environments, you can modify the ./LinkSDK/portfiles/aiot-port/project/stm32_noneos/Core/os_none_impl.c file.

    For more information about how to modify the file, see Step 1: Port the interfaces.

  4. You can open ./LinkSDK/portfiles/aiot-port/project/stm32_noneos/Core/mqtt_at_basic_demo.c and configure the device certificate.

    Parameter

    Example

    Description

    product_key

    a18wP******

    The device certificate information. This is the device certificate that you obtained during preparation.

    You can also view the device certificate on the device details page in the IoT Platform console.

    device_name

    LightSwitch

    device_secret

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

    mqtt_host

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

    The MQTT domain name for device access.

    For the domain names of public instances and Enterprise instances, see View instance endpoints.

  5. You can compile the project files and then download the executable file to the development board to run it.

    • After the file runs successfully, you can view logs on the device.

      • Module initialization:

        >>> AT
        >>> AT
        <<<
        RDY
        >>> AT
        <<< AT
        OK
        >>> AT+QIACT=1
        <<<
        OK
        >>> AT+QIACT?
        <<<
        +QIACT: 1,1,1,"10.13.***.***"
        OK
      • Establish a connection:

        [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
      • Report data:

        [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
      • Receive data:

        [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
    • You can also view logs in the IoT Platform console. For more information, see IoT Platform logs.