Connect devices that do not have an operating system
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
You have obtained the C Link SDK.
You are familiar with the C Link SDK porting interfaces.
You have obtained the device certificate.
Step 1: Port the interfaces
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 |
|
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 |
Mutex lock | core_sysdep_mutex_init |
|
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.
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
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 | |
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
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.
To adapt the interfaces for different environments, you can modify the
./LinkSDK/portfiles/aiot-port/project/stm32_noneos/Core/os_none_impl.cfile.For more information about how to modify the file, see Step 1: Port the interfaces.
You can open
./LinkSDK/portfiles/aiot-port/project/stm32_noneos/Core/mqtt_at_basic_demo.cand 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.
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.***.***" OKEstablish 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_CONNECTReport 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 22Receive 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.