This topic describes how to use the embedded C software development kit (SDK) from Alibaba Cloud Model Studio for real-time multi-modal interaction. It focuses on device management and real-time multi-modal interaction using the License pattern. This topic covers SDK download and installation, the fully managed and semi-managed modes of the License pattern, and provides code examples for key server-side and device-side interfaces.
Prerequisites
Activate a real-time multi-modal interaction application in Alibaba Cloud Model Studio and obtain the Workspace ID, APP ID, and API key.
To create an application, see Application Creation.
Configure the application. For more information, see Application Configuration.
License Model > Product Billing
Hardware compatibility list
No. | Hardware platform information | SDK package download link | ||
Vendor | Chip platform | Published version | ||
1 | Anyka | V500 | v1.2.1 | |
2 | Aiq | AiW626X | v1.2.1 | |
3 | ASR | ASR1606 | v1.2.1 | |
4 | Broadcom | BL616CL | v1.2.1 | |
BK7252 | v1.2.1 | |||
BK7258 | v1.2.1 | |||
5 | CX-WIC | LM600 | v1.2.1 | |
LM620 | v1.2.1 | |||
6 | HiSilicon | AV100 | v1.2.1 | |
AV200 | v1.2.1 | |||
Hi3516CV610 | v1.2.1 | |||
6 | Jieli | AC7911 | v1.2.1 | |
AC792X | v1.2.1 | |||
JL7014 | v1.2.1 | |||
7 | Ingenic | G32S10M | v1.2.1 | |
T23 | v1.2.1 | |||
T41 | v1.2.1 | |||
8 | Espressif | ESP32 | v1.2.1 | |
ESP32S3 | v1.2.1 | |||
9 | Allwinner | F133 | v1.2.1 | |
R128 | v1.2.1 | |||
V821 | v1.2.1 | |||
V853 | v1.2.1 | |||
XR872 | v1.2.1 | |||
10 | Rockchip | RK3506 | v1.2.1 | |
RK3588 | v1.2.1 | |||
RV1103 | v1.2.1 | |||
11 | Realtek | RTL8711 | v1.2.1 | |
RTL8721 | v1.2.1 | |||
12 | Xiaomi | VELA_V7A | v1.2.1 | |
VELA_V8A | v1.2.1 | |||
13 | XINMAI | XMW718 | v1.2.1 | |
14 | SigmaStar | SSC305DE | v1.2.1 | |
SSC309QL | v1.2.1 | |||
15 | Xingyi | XY4100LC | v1.2.1 | |
16 | YIXIN | EC718PM | v1.2.1 | |
17 | China Mobile | ML307N | v1.2.1 | |
18 | UNISOC | UIS8910 | v1.2.1 | |
UMS9117 | v1.2.1 | |||
W217 | v1.2.1 | |||
1. Access mode description
Semi-managed mode
This mode is designed for customers who require post-sales operations or device upgrades. In this mode, customers use their own cloud services to manage and authenticate devices, and a bidirectional communication channel exists between the customer's cloud service and the device.
After a DeviceName is registered, it cannot be registered again to obtain a new device certificate.

Server-side development:
Complete the server-side API integration. For more information, see the Cloud API Reference section. The Model Studio device metering and management service provides two APIs: device registration and token acquisition.
Device-side development
Adapt the chip. For more information, see the Chip Platform HAL Integration section. If you use a chip or module recommended by Alibaba Cloud, you can skip this step.
After you integrate the SDK, complete the business logic development. For more information, see the Device-side business logic integration section. The device is registered based on the one-type-one-secret principle and is pre-provisioned with the AppId and AppSecret (License key) that are generated when the application is created.
Fully managed mode
This mode is designed for customers who sell hardware as a one-time transaction and do not require post-sales operations. These customers do not have their own cloud services for device management. Alibaba Cloud handles all device management and authentication.
After a DeviceName is registered, it cannot be registered again to obtain a new device certificate.

Server-side development:
None
Device-side development
Adapt the chip. For more information, see the Chip Platform HAL Layer Integration section. If you use a chip or module recommended by Alibaba Cloud, you can skip this step.
After you integrate the SDK, complete the business logic development. For more information, see Device-side business logic integration. Register the device using the one-model-one-secret method and pre-provision it with the AppId and AppSecret (License key) that are generated when you create the application.
What is one-model-one-secret?
This means that all devices of the same product model are pre-flashed with the same product information, which includes the AppId and AppSecret (License key). When a device registers with the metering and management service, the service authenticates the device information, which includes the AppId, AppSecret (License key), and DeviceName. After successful authentication, the service issues a unique device certificate, which is a trituple that consists of the AppId, DeviceName, and DeviceSecret. The device must use this certificate for all subsequent communication and authentication.
How do I view the AppId and AppSecret (License key)?
You can copy the application ID (AppID) directly from the My Applications page. Next to the Configure Application button, click the button with three dots (...) to view the AppSecret (License key). This option is available only after you purchase a license.

2. Server-side API development guide
Import dependency packages
Domain name: bailianmodelonchip.cn-beijing.aliyuncs.com
Region: cn-beijing
Version: Use the latest version.
Query address: https://mvnrepository.com/artifact/com.aliyun/bailianmodelonchip20240816
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>bailianmodelonchip20240816</artifactId>
<version>${version}</version>
</dependency>
Example:
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>bailianmodelonchip20240816</artifactId>
<version>1.4.0</version>
</dependency>Maven repository address: https://repo1.maven.org/maven2
Endpoint: bailianmodelonchip.cn-beijing.aliyuncs.com
RegionId: cn-beijing
Python version
pip install alibabacloud_bailianmodelonchip20240816==1.4.0alibabacloud_bailianmodelonchip202408162.1 Device registration interface: deviceRegister
When the device connects to the network for the first time, it must call this interface through the customer's cloud service to register with the Model Studio device metering and management service and obtain the device trituple information. Authentication is performed using the Alibaba Cloud POP gateway and requires the integration of the POP SDK.
Input parameter: DeviceRegisterRequest object
Parameter | Type | Required | Description |
nonce | String | Yes | A 13-byte random number (26 characters) that is extracted from data generated by the device-side SDK. For more information, see Device Registration API. |
appId | String | Yes | The product identifier. This ID is generated after you create an application in the Model Studio console. |
workspaceId | String | No | The workspace ID. |
requestTime | String | Yes | The request timestamp in milliseconds (ms). This value is extracted from data generated by the device-side SDK. For more information, see the Device Registration API. |
signature | String | Yes | Encrypted JSON information extracted from the data generated by the device-side SDK. For more information, see Device Registration API. |
Response parameter: DeviceRegisterResponseBody object
Parameter | Type | Required | Description |
code | String | Yes | The error code. |
httpStatusCode | Integer | Yes | The HTTP status code. 200: Success Other failures |
message | String | Yes | The error message. |
requestId | String | Yes | The request ID. |
success | Boolean | Yes | The success flag. |
data | DeviceRegisterResponseBodyData | Yes | The registration result. Pass this through directly to the device. |
DeviceRegisterResponseBodyData
Parameter | Type | Description |
nonce | String | A 13-byte random number (26 characters). |
responseTime | String | The response timestamp in milliseconds. |
appId | String | The product identifier. |
workspaceId | String | The workspace ID. |
deviceName | String | The unique device identifier. |
signature | String | The device trituple information that can be parsed by the device-side SDK. For more information, see Device Registration API. |
Java example
package pop;
import com.alibaba.fastjson.JSON;
import com.aliyun.bailianmodelonchip20240816.Client;
import com.aliyun.bailianmodelonchip20240816.models.*;
import com.aliyun.teaopenapi.models.Config;
public class DeviceTest {
public static void main(String[] args) {
Config config = new Config();
config.setAccessKeyId("your-ak");
config.setAccessKeySecret("your-as");
config.setEndpoint("bailianmodelonchip.cn-beijing.aliyuncs.com");
try {
Client client = new Client(config);
deviceRegister(client);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void deviceRegister(Client client) throws Exception {
DeviceRegisterRequest request = new DeviceRegisterRequest();
request.setNonce("Your Nonce");
request.setRequestTime("1748312026868");
request.setAppId("Your AppId");
request.setSignature("Your Signature");
DeviceRegisterResponse response = client.deviceRegister(request);
DeviceRegisterResponseBody.DeviceRegisterResponseBodyData data = response.getBody().getData();
// Pass the data to the device in JSON format. The vendor must implement this.
System.out.println("Pass-through result: " + JSON.toJSONString(data));
}
}Python example
import sys
from typing import List
from Tea.exceptions import UnretryableException, TeaException
from alibabacloud_bailianmodelonchip20240816 import models as bailian_model_on_chip_20240816_models
from alibabacloud_bailianmodelonchip20240816.client import Client as BailianModelOnChip20240816Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_tea_util import models as util_models
class PopDeviceRegister:
def __init__(self):
pass
@staticmethod
def create_client() -> BailianModelOnChip20240816Client:
config = open_api_models.Config(
access_key_id='your-ak',
access_key_secret='your-as'
)
config.endpoint = f'bailianmodelonchip.cn-beijing.aliyuncs.com'
return BailianModelOnChip20240816Client(config)
@staticmethod
def main(
args: List[str],
) -> None:
client = PopDeviceRegister.create_client()
device_register_request = bailian_model_on_chip_20240816_models.DeviceRegisterRequest(
nonce='Your Nonce',
request_time='1750313647162',
app_id='Your AppId',
signature='Your Signature'
)
headers = {}
try:
# Print the API return value when you run the code.
res = client.device_register_with_options(device_register_request, headers, util_models.RuntimeOptions())
print('body', res.body)
if res.body.success:
print('data', res.body.data)
except UnretryableException as e:
# Network exception
print(e)
except TeaException as e:
# Business exception
print(e)
except Exception as e:
# Other exceptions
print(e)
if __name__ == '__main__':
PopDeviceRegister.main(sys.argv[1:])You can obtain your-ak and your-as from the Alibaba Cloud console. For more information, see AccessKey pair.
Endpoint: bailianmodelonchip.cn-beijing.aliyuncs.com
The DeviceRegisterResponseBodyData must be converted to the JSON format using `JSON.toJSONString` and then sent to the device. The vendor is responsible for implementing this logic.
2.2 Interface to get the access token for service interaction: getToken
After a device is registered, it must dynamically obtain an access token to use the multi-modal interaction capabilities. You can use this interface to retrieve the token from the Model Studio device metering and management service. Authentication is performed using the Alibaba Cloud POP gateway and requires the integration of the POP SDK.
Input parameter:
GetTokenRequest
Parameter | Type | Required | Description |
nonce | String | Yes | A 13-byte random number (26 characters), extracted from the data generated by the device-side SDK. |
appId | String | Yes | The application identifier. This ID is generated after you create an application in the Model Studio console. |
deviceName | String | Yes | The unique device identifier uploaded from the device, extracted from the data generated by the device-side SDK. |
requestTime | String | Yes | The request timestamp in milliseconds, extracted from the data generated by the device-side SDK. |
signature | String | Yes | Encrypted JSON information extracted from data generated by the device-side SDK. For more information, see the Get Interaction Token API. |
tokenType | String | Yes | The type of token requested. Currently, only the MMI type is supported. MMI: Multi-modal interaction token. |
tokenKey | String | Yes | The key used to exchange for a token. You must pass different values based on the token type. Currently, only the MMI type is supported. MMI: The Alibaba Cloud Model Studio's API key. |
Response parameter
GetTokenResponseBody
Parameter | Type | Required | Description |
code | String | Yes | The error code. |
httpStatusCode | Integer | Yes | The HTTP status code. 200: Success Other values: Failure |
message | String | Yes | The error message. |
requestId | String | Yes | The request ID. |
success | Boolean | Yes | The success flag. |
data | GetTokenResponseBodyData | Yes | The result can be directly passed through to the device for parsing. For more information, see Obtain an interaction token. |
GetTokenResponseBodyData
Parameter | Type | Description |
nonce | String | A 13-byte random number (26 characters). |
responseTime | String | The response timestamp in milliseconds. |
appId | String | The application identifier. |
deviceName | String | The unique device identifier. |
signature | String | The encrypted token information. The device-side SDK can parse this. |
Java example
package pop;
import com.alibaba.fastjson.JSON;
import com.aliyun.bailianmodelonchip20240816.Client;
import com.aliyun.bailianmodelonchip20240816.models.*;
import com.aliyun.teaopenapi.models.Config;
public class TokenTest {
public static void main(String[] args) {
Config config = new Config();
config.setAccessKeyId("your-ak");
config.setAccessKeySecret("your-as");
config.setEndpoint("bailianmodelonchip.cn-beijing.aliyuncs.com");
try {
Client client = new Client(config);
getToken(client);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getToken(Client client) throws Exception {
GetTokenRequest request = new GetTokenRequest();
request.setNonce("Your Nonce");
request.setRequestTime("1748570866553");
request.setAppId("Your AppId");
request.setDeviceName("Your DeviceName");
request.setTokenType("MMI");
request.setTokenKey("Your TokenKey");
request.setSignature("Your Signature");
GetTokenResponse response = client.getToken(request);
GetTokenResponseBody.GetTokenResponseBodyData data = response.getBody().getData();
// Pass the data to the device in JSON format. The vendor must implement this.
System.out.println("Execution result: " + JSON.toJSONString(data));
}
}Python example
import sys
from typing import List
from Tea.exceptions import UnretryableException, TeaException
from alibabacloud_bailianmodelonchip20240816 import models as bailian_model_on_chip_20240816_models
from alibabacloud_bailianmodelonchip20240816.client import Client as BailianModelOnChip20240816Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_tea_util import models as util_models
class PopGetToken:
def __init__(self):
pass
@staticmethod
def create_client() -> BailianModelOnChip20240816Client:
config = open_api_models.Config(
access_key_id='your-ak',
access_key_secret='your-as'
)
config.endpoint = f'bailianmodelonchip.cn-beijing.aliyuncs.com'
return BailianModelOnChip20240816Client(config)
@staticmethod
def main(
args: List[str],
) -> None:
client = PopGetToken.create_client()
get_token_request = bailian_model_on_chip_20240816_models.GetTokenRequest(
nonce='Your Nonce',
request_time='1750313823168',
app_id='Your AppId',
device_name='Your DeviceName',
token_type='MMI',
token_key='Your Token Key',
signature='Your Signature'
)
headers = {}
try:
# Print the API return value when you run the code.
res = client.get_token_with_options(get_token_request, headers, util_models.RuntimeOptions())
print('body', res.body)
if res.body.success:
print('data', res.body.data)
except UnretryableException as e:
# Network exception
print(e)
except TeaException as e:
# Business exception
print(e)
except Exception as e:
# Other exceptions
print(e)
if __name__ == '__main__':
PopGetToken.main(sys.argv[1:])You can obtain your-ak and your-as from the Alibaba Cloud console. For more information, see AccessKey pair.
Endpoint: bailianmodelonchip.cn-beijing.aliyuncs.com
The GetTokenResponseBodyData must be converted to the JSON format using `JSON.toJSONString` and then sent to the device. The vendor is responsible for implementing this logic. The tokenKey parameter must be the Model Studio API key. For more information about how to obtain the API key, see the Model Studio documentation.
2.3 Server-side error codes
Error code | Problem Description | Solution |
100007 | The product does not have a quota for activating devices. | Quota Purchase |
100008 | The device is already registered. | Check whether the unique device identifier is already registered, or change the unique device identifier. |
100009 | The device is not registered. | Register the device. |
100010 | The request timestamp must be within the last five minutes. | Update the request time and resubmit. |
100011 | The signature is empty. | Check the request signature data. |
100012 | The unique device identifier cannot exceed 32 characters. | Check the length of the unique device identifier. |
100013 | The device name is invalid. | The device name contains invalid characters. |
100023 | The device is disabled. | Alibaba Cloud Model Studio - Device Management: Enable a device |
100025 | The device is already activated and cannot be registered again. | On the Model Studio platform, go to the Device Management page, reset the device, and then register it. |
100032 | The access mode does not match. | Check the application access mode and modify the SDK call to use either License mode or pay-as-you-go mode. |
100033 | The validity period for quota activation has passed for the device. | Purchase a new quota. |
200014 | The quota has been exhausted. | Purchase a new quota. |
200021 | The device has reached the maximum daily call limit. | Purchase a top-up package. |
200022 | The quota has expired. | Purchase a new quota. |
300002 | Failed to retrieve a token from the Model Studio gateway. | Check the application configuration. |
500001 | The encrypted data does not match the request parameter values. | Check the encrypted parameters and request parameter values. |
500002 | Decryption failed. | Check the encrypted data. |
3. Device-side interface guide
3.1. Get the SDK
Different chip platforms require compilation with their corresponding toolchains. Model Studio supports the chips in the following list, and you can download the SDK for them directly.
If you are using a new chip platform, contact Alibaba Cloud sales for technical support.
SDK folder structure
├── ReleaseNote.md
├── include
│ ├── c_agent
│ │ └── ...
│ ├── c_mmi_cmd
│ │ └── ...
│ ├── c_visual
│ │ └── ...
│ ├── c_utils
│ │ ├── c_utils.h
│ │ └── ...
│ ├── c_mmi.h
│ ├── lib_c_license.h
│ ├── lib_c_sdk.h
│ ├── qwen_test.h
│ └── ...
├── libs
│ ├── libc_license.a
│ ├── libc_no_license.a
│ ├── libc_mmi_cmd.a
│ ├── libhal_dummy.a
│ ├── libqwen_sdk.a
│ ├── libqwen_test.a
│ └── ...
└── third_party
├── cJSON
│ ├── cJSON.h
│ └── libcjson.a
└── tinycrypt
├── include
│ └── ...
└── libtinycrypt.aThe `include` folder contains the header files required to use the SDK. Add this folder to your project's header file directory.
`libqwen_sdk.a` contains the core code of the SDK and must be loaded.
`libc_license.a` contains code related to License mode. It must be loaded if you use License mode for access. Do not load this library if you use pay-as-you-go mode.
`libhal_dummy.a` contains dummy HAL code used to check the compilation environment before the SDK is adapted. It is recommended to delete this library after completing the HAL porting.
`libqwen_test.a` contains test code. This library must be loaded during automated testing and removed for production.
`libtinycrypt.a` contains encryption and decryption dependency interfaces. This library must be loaded if the platform has not integrated this third-party library.
`libcjson.a` contains SDK-related dependency interfaces. This library must be loaded if the platform has not integrated this third-party library.
3.2. Vendor completes HAL layer development
The SDK abstracts a Hardware Abstraction Layer (HAL). Vendors must complete the corresponding development on their own chip platforms.
3.2.1. Memory
/**
* util_malloc - Allocates a block of memory of a specified size.
* @size: The size of the memory to allocate, in bytes.
*
* This function calls the standard library function malloc to allocate memory.
* It may include additional error checking or memory management strategies to improve
* program stability and performance.
*
* @return Returns a pointer to the allocated memory. Returns NULL if memory allocation fails.
*/
void * util_malloc(int32_t size);
/**
* Frees dynamically allocated memory.
*
* This function is intended to release memory space previously obtained through dynamic allocation
* to avoid memory leaks. It accepts a pointer to a dynamically allocated memory region and sets it
* to NULL to prevent dangling pointers.
*
* @param ptr A pointer to the dynamically allocated memory region. If NULL, the function does nothing.
* After the memory is freed, this pointer will be set to NULL.
*/
void util_free(void *ptr);The memory module is a dependency for the SDK provided by Alibaba Cloud. Vendors must implement it on their own hardware platforms.
3.2.2. Random number module
/**
* Initializes the random number generator.
*
* @param seed The seed value used to initialize the random number generator.
*
* @return Returns the initialization result. 0 indicates success, non-zero indicates failure.
*
* This function initializes the random number generator to ensure that the subsequent
* generated random number sequence has good randomness. The choice of seed value has a
* significant impact on the generated random number sequence. The same seed value will
* produce the same random number sequence.
*/
int32_t util_random_init(uint32_t seed);
/**
* Generates a random number.
*
* @return Returns the generated random number.
*
* Before calling this function, ensure that the random number generator has been
* successfully initialized with the util_random_init function. The random number
* generated by this function is based on the seed provided during initialization.
*/
uint32_t util_random(void);The random number module is a dependency for the SDK provided by Alibaba Cloud. Vendors must implement it on their own hardware platforms.
3.2.3. Storage module
/**
* @brief Erases the storage.
*
* This function is used to erase all data in the storage. Before calling this function, ensure that
* no information in the storage is needed, as the erase operation will delete all data and is irreversible.
*
* @return int32_t Returns the result of the erase operation. 0 indicates success; a non-zero value indicates an error.
*/
int32_t util_storage_erase(void);
/**
* @brief Stores data to the storage.
*
* This function stores the specified data into the storage. Before calling this function, ensure the
* correctness and integrity of the data, as the storage operation will overwrite existing data in the storage.
*
* @param data A pointer to the data to be stored. The data type is uint8_t (unsigned 8-bit integer).
* @param size The size of the data to be stored, in bytes. The data type is uint32_t (unsigned 32-bit integer).
* @return int32_t Returns the result of the storage operation. 0 indicates success; a non-zero value indicates an error.
*/
int32_t util_storage_storage(uint8_t *data, uint32_t size);
/**
* @brief Loads data from the storage.
*
* This function loads data of a specified size from the storage. Before calling this function, ensure that
* the memory area pointed to by the data pointer is large enough to accommodate the data loaded from the storage.
*
* @param data A pointer to the buffer used to store the data loaded from the storage. The data type is uint8_t (unsigned 8-bit integer).
* @param size The size of the data to be loaded, in bytes. The data type is uint32_t (unsigned 32-bit integer).
* @return int32_t Returns the result of the load operation. 0 indicates success; a non-zero value indicates an error.
*/
int32_t util_storage_load(uint8_t *data, uint32_t size);The storage module is a dependency for the SDK provided by Alibaba Cloud. Vendors must implement it on their own hardware platforms.
Store data in a separate partition to ensure that the data is not cleared even after a factory reset.
3.2.4. Time module
/**
* Gets the current timestamp in milliseconds.
*
* This function is used to get the current timestamp, accurate to the millisecond.
* This timestamp is typically used for calculating time differences, recording event
* occurrence times, and other similar scenarios.
*
* @return The current timestamp in milliseconds.
*/
int64_t util_now_ms(void);
/**
* Millisecond-level sleep function.
*
* This function pauses the current thread for a specified number of milliseconds.
* It is used to control program execution pace, wait for events to occur, etc.
*
* @param ms The number of milliseconds to pause.
*/
void util_msleep(uint32_t ms);
/**
* Gets the current timestamp.
*
* This function is used to get the current timestamp, which is the number of milliseconds
* since 00:00:00 UTC on January 1, 1970. It takes no input parameters and returns an
* int64_t value representing the current timestamp.
*
* @return int64_t The current timestamp in milliseconds.
*/
int64_t util_get_timestamp(void);
/**
* Checks if the timestamp function has been initialized.
*
* This function is used to check if the timestamp-related functions have been initialized.
* If it returns true (non-zero), the timestamp function is available. If it returns false (zero),
* you may need to perform initialization or avoid using the timestamp function.
*
* @return Returns non-zero if the timestamp function is initialized, otherwise returns zero.
*/
uint8_t util_timestamp_inited(void);The time module is a dependency for the SDK provided by Alibaba Cloud. Vendors must implement it on their own hardware platforms.
3.2.5. Mutex module
/* Mutex struct definition */
typedef struct _util_mutex_t {
void *mutex_handle; /* Mutex handle, specific implementation depends on the platform */
} util_mutex_t;
/*****************************************************
* Function: util_mutex_create
* Description: Creates a mutex object.
* Parameter: None.
* Return: util_mutex_t * --- Returns a pointer to the mutex struct.
****************************************************/
util_mutex_t * util_mutex_create(void);
/*****************************************************
* Function: util_mutex_delete
* Description: Deletes the specified mutex object.
* Parameter:
* mutex --- A pointer to the mutex struct.
* Return: None.
****************************************************/
void util_mutex_delete(util_mutex_t *mutex);
/*****************************************************
* Function: util_mutex_lock
* Description: Locks the specified mutex with a timeout mechanism.
* Parameter:
* mutex --- A pointer to the mutex struct.
* timeout --- The lock wait timeout in milliseconds (ms). Can be set to MUTEX_WAIT_FOREVER for an infinite wait.
* Return: int32_t --- Returns the operation result (util_result_t).
****************************************************/
int32_t util_mutex_lock(util_mutex_t *mutex, int32_t timeout);
/*****************************************************
* Function: util_mutex_unlock
* Description: Unlocks the specified mutex.
* Parameter:
* mutex --- A pointer to the mutex struct.
* Return: int32_t --- Returns the operation result (util_result_t).
****************************************************/
int32_t util_mutex_unlock(util_mutex_t *mutex);
The mutex module is a dependency for the SDK provided by Alibaba Cloud. Vendors must implement it on their own hardware platforms.
3.2.6. HAL layer porting acceptance criteria
After you implement the modules, load `libqwen_test.a` and call the `qwen_sdk_test` interface directly in the main program. This tests the modules, and you can view the test results in the output log.
Provide the output log to Alibaba Cloud for confirmation.
Example of a successful test output log:

3.3. Device-side business logic integration
This SDK is compatible with both License mode and pay-as-you-go mode.
When `libc_license.a` is loaded, the SDK is in License mode.
When `libc_license.a` is not loaded, the SDK is in pay-as-you-go mode.
When you load `libc_license.a`, you must add a compilation option to force all symbols in the library to be loaded.
For ARM platform compilation parameters, perform the following configuration:
# Linker flags
LDFLAGS += -L./YourLibPath \
-Wl,--whole-archive \
-lc_license \
-Wl,--no-whole-archive \When using pay-as-you-go mode, you must write the API key to the device before connecting to the multi-modal gateway.
When you use License mode (fully managed mode), you must write the API key to the device. You are responsible for the security of the API key storage and the application.
With the pay-as-you-go model, you are responsible for the security of the application and any API key written to a device.

For the corresponding modules, refer to the code examples that are marked from Code Example 1 to Code Example 6 in the diagram. You can find the corresponding code examples in the detailed interface descriptions.
3.3.1. Initialization interface
/**
* @brief Initializes the MMI SDK.
*
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_sdk_init(void);
/**
* @brief Configures MMI module parameters.
*
* This function is used to initialize the configuration parameters of the MMI module,
* including event callback, work mode, text mode, voice settings, audio stream mode,
* and buffer size.
*
* @param config A pointer to the mmi_user_config_t struct, which contains the configuration parameters.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_config(mmi_user_config_t *config);This SDK is compatible with pay-as-you-go mode. When `libc_license.a` is not loaded, the SDK operates in pay-as-you-go mode.
When you load `libc_license.a`, you must add a compilation option to force all symbols in the library to be loaded.
For ARM platform compilation parameters, perform the following configuration:
# Linker flags
LDFLAGS += -L./YourLibPath \
-Wl,--whole-archive \
-lc_license \
-Wl,--no-whole-archive \Code example 1
#include "c_mmi.h"
mmi_user_config_t mmi_config = C_MMI_CONFIG_DEFAULT();
// Initialize the SDK
c_mmi_sdk_init();
// You must configure evt_cb, otherwise the SDK will not run correctly.
mmi_config.evt_cb = _mmi_event_callback; // Register the event callback function, see details below.
// Configure the work mode
mmi_config.work_mode = C_MMI_MODE_PUSH2TALK;
mmi_config.text_mode = C_MMI_TEXT_MODE_BOTH;
// Configure the upstream and downstream audio data format
mmi_config.upstream_mode = C_MMI_STREAM_MODE_PCM;
mmi_config.downstream_mode = C_MMI_STREAM_MODE_MP3;
// Configure the buffer size
mmi_config.recorder_rb_size = 8 * 1024;
mmi_config.player_rb_size = 8 * 1024;
c_mmi_config(&mmi_config);
// Set the voice ID. This must be called after c_mmi_config.
c_mmi_set_voice_id("longxiaochun_v2");3.3.2. Product information configuration interface
/**
* @brief Checks if the device is registered.
*
* @return uint8_t Device registration status.
* - 0: Device is not registered.
* - 1: Device is registered.
*/
uint8_t c_license_device_is_registered(void);
/**
* @brief Resets the configuration.
*
* This function clears all saved device configuration information.
* After calling this function, the configuration will be restored to the default state,
* and you will need to set the relevant parameters again.
*
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_storage_reset(void);
/**
* @brief Saves the configuration.
*
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_storage_save(void);
/**
* @brief Sets the AppId.
*
* This function is used to set the AppId. After setting, you need to call c_mmi_storage_save to save it.
*
* @param app_id_str The AppId, issued by Alibaba Cloud, in string format.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_storage_set_app_id_str(char *app_id_str);
/**
* @brief Sets the ApiKey.
*
* This function is used to set the ApiKey. After setting, you need to call c_mmi_storage_save to save it.
*
* @param api_key The API key obtained from the Model Studio platform.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_storage_set_api_key(char *api_key);
/**
* @brief Sets the WorkSpaceId.
*
* This function is used to set the WorkSpaceId. After setting, you need to call c_mmi_storage_save to save it.
*
* @param ws_id The WorkSpaceId, obtained from the Model Studio platform.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_storage_set_ws_id(char *ws_id);
/**
* @brief Sets the device name (DeviceName).
*
* This function is used to set the device name. After setting, you need to call c_mmi_storage_save to save it.
*
* @param dn The device name, which can be set by the user. The length must not exceed 32 characters.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_storage_set_device_name(char *device_name);
/**
* @brief Sets the AppSecret.
* This function is used to set the AppSecret. After setting, you need to call c_mmi_storage_save to save it.
*
* @param app_secret The AppSecret, issued by Alibaba Cloud, in string format.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_license_set_app_secret_str(char *app_secret);
/**
* @brief Initializes MMI.
*
* This function is used to initialize MMI. It only needs to be called in pay-as-you-go mode.
*
* @param workspace The WorkSpaceId, obtained from the Model Studio platform.
* @param app_id The AppId, issued by Alibaba Cloud, in string format.
* @param api_key The API key obtained from the Model Studio platform.
*
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_mmi_init(char *workspace, char *app_id, char *api_key);Code example 2.1: Pay-as-you-go mode configuration
#include "c_mmi.h"
// Pre-configure the AppId
c_mmi_storage_set_app_id_str("Your AppId");
// Pre-configure the WorkspaceId
c_mmi_storage_set_ws_id("Your WorkspaceId");
// Pre-configure the ApiKey
c_mmi_storage_set_api_key("Your ApiKey");
// Pre-configure the DeviceName
c_mmi_set_device_name("Your DeviceName");
// Save the configuration information
c_mmi_storage_save();
// Initialize MMI
c_mmi_init("Your WorkspaceId", "Your AppId", "Your ApiKey");Code example 2.2: License mode configuration
#include "c_mmi.h"
#include "lib_c_license.h"
if (c_license_device_is_registered() == 0) {
c_mmi_storage_reset();
// Pre-configure the AppId
c_mmi_storage_set_app_id_str("Your AppId");
// Pre-configure the AppSecret
c_license_set_app_secret_str("Your AppSecret");
// Pre-configure the ApiKey. This is required for fully managed mode, but not for semi-managed mode.
c_mmi_storage_set_api_key("Your ApiKey");
// Pre-configure the DeviceName
c_mmi_set_device_name("Your DeviceName");
// Save the configuration information
c_mmi_storage_save();
}
// Pre-configure the ApiKey. This is required for fully managed mode, but not for semi-managed mode.
// The API key needs to be reconfigured every time the device starts up.
c_mmi_storage_set_api_key("Your ApiKey");3.3.3. Device registration interface (This process is not required for pay-as-you-go mode)
/**
* @brief Generates the registration string.
*
* @param buffer The buffer to store the generated registration string.
* @param buffer_size The size of the buffer.
* @param time_ms_str The timestamp string in milliseconds.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_license_gen_register_str(char *buffer, uint32_t buffer_size, char *time_ms_str);
/**
* @brief Parses the device registration response from the server.
*
* @param rsp_str A pointer to the device registration response string.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_license_analyze_register_rsp(char *rsp_str);
Code example 3
#include "lib_c_license.h"
#define REQ_BUFFER_SIZE (1024)
char req[REQ_BUFFER_SIZE];
char time_ms_str[14];
if (c_license_device_is_registered() == 0) {
snprintf(time_ms_str, sizeof(time_ms_str), "%" PRId64, util_get_timestamp());
// Generate the registration information string 'req' based on the timestamp.
c_license_gen_register_str(req, sizeof(req), time_ms_str);
// Get the device registration information. The customer must implement the HTTP interface.
char *rsp = dummy_http_request(req);
// Parse and complete the registration.
int32_t err = c_license_analyze_register_rsp(rsp);
if (err) {
return err;
}
// After successful registration, call the storage interface to save the registration information.
c_mmi_storage_save();
}About the `dummy_http_request` interface in the code example:
In semi-managed mode, this `dummy_http_request` must access the HTTP interface provided by the customer's existing device management server to complete the device registration.
In fully managed mode, this `dummy_http_request` must access the device management service interface provided by Alibaba Cloud to complete the device registration.
When you use fully managed mode, the access information is as follows:
Host: bailian.multimodalagent.aliyuncs.com
Register API: /api/device/v1/register
Example of a request data packet generated by the device-side SDK
{
"appId": "<YOUR APP ID>",
"deviceName": "<YOUR DEVICE NAME>",
"nonce": "<YOUR NONCE>",
"requestTime": "1753326620619",
"sdkVersion": "0.3.2",
"signature": "<YOUR SIGNATURE>"
}
Example of a data packet passed from the server to the device. The following data is the `data` field in the HTTP response message.
{
"nonce": "<YOUR NONCE>",
"responseTime": "1753326621269",
"appId": "<YOUR APP ID>",
"deviceName": "<YOUR DEVICE NAME>",
"signature": "<YOUR SIGNATURE>"
}
The customer must implement `http_request` to receive the data that is returned from the customer's cloud.
When you pass the data to `c_device_analyze_register_rsp`, the format must be the same as the `rsp` example data.
3.3.4. Get interaction token interface (This process is not required for pay-as-you-go mode)
/**
* @brief Checks if the token has expired.
*
* @return uint8_t Returns 1 if the token is valid, 0 if the token has expired.
*/
uint8_t c_license_is_token_expire(void);
/**
* @brief Generates the request data to get a token.
*
* This function generates a request string to get a token based on the provided parameters.
* This is used to request an access token from the server.
*
* @param buffer The buffer to store the generated request string.
* @param buffer_size The size of the buffer.
* @param time_ms_str The timestamp string in milliseconds.
* @param api_key The API key string. Set api_key to NULL in semi-managed mode.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_license_gen_get_token_str(char *buffer, uint32_t buffer_size, char *time_ms_str, char *api_key);
/**
* @brief Parses the login response data.
*
* Parses the login response string and triggers events:
* - C_MMI_EVENT_USER_CONFIG: User configuration initialization.
* - C_MMI_EVENT_DATA_INIT: Data initialization complete.
*
* @param rsp_str The string containing the login response.
* @return int32_t Returns the operation result. 0 indicates success, non-zero indicates failure.
*/
int32_t c_license_analyze_get_token_rsp(char *rsp_str);
Code example 4
#include "lib_c_license.h"
#define REQ_BUFFER_SIZE (1024)
char req[REQ_BUFFER_SIZE];
char time_ms_str[14];
char api_key[36] = { 0 };
snprintf(time_ms_str, sizeof(time_ms_str), "%" PRId64, util_get_timestamp());
c_mmi_storage_get_api_key(api_key);
// Generate the registration information string 'req' based on the timestamp issued by the server.
// An api_key is required for fully managed mode.
c_license_gen_get_token_str(req, sizeof(req), time_ms_str, api_key);
// For semi-managed mode, you can pass NULL.
// c_license_gen_get_token_str(req, sizeof(req), time_ms_str, NULL);
// Get the device information returned from the server. The customer must implement this interface.
char *rsp = dummy_http_request(req);
// Parse the interaction token.
int32_t err = c_license_analyze_get_token_rsp(rsp);
if (err) {
return err;
}About the `dummy_http_request` interface in the code example:
In semi-managed mode, this `dummy_http_request` must access the HTTP interface provided by the customer's existing device management server to retrieve the token.
In fully managed mode, the dummy_http_request accesses the Alibaba Cloud device management service API to complete device registration.
When you use fully managed mode, the access information is as follows:
Host: bailian.multimodalagent.aliyuncs.com
GetToken API: /api/token/v1/getToken
The data returned by the `getToken` interface can be over 300 bytes. It is recommended to reserve a receiving buffer of at least 512 bytes.
Example of a request data packet generated by the device-side SDK
{
"appId":"<YOUR APP ID>",
"deviceName":"<YOUR DEVICE NAME>",
"payMode":"LICENSE",
"requestTime":"1753327457730",
"sdkVersion":"0.3.2",
"tokenType":"MMI",
"signature": "<YOUR SIGNATURE>"
}After the server adds the `tokenKey` field, it sends the request to the Alibaba Cloud POP interface.
{
"appId":"<YOUR APP ID>",
"deviceName":"<YOUR DEVICE NAME>",
"payMode":"LICENSE",
"requestTime":"1753327457730",
"sdkVersion":"0.3.2",
"tokenType":"MMI",
"signature": "<YOUR SIGNATURE>",
"tokenKey": "<YOUR TOKEN KEY>"
}After the device sends the SDK-generated data to the cloud, the cloud must add the corresponding `tokenKey` field before it can access the Alibaba Cloud interface to retrieve the token. The device must not store the TokenKey to prevent leakage.
Example of a data packet sent from the server to the device
{
"nonce": "<YOUR NONCE>",
"responseTime": "1753327458081",
"appId": "<YOUR APP ID>",
"deviceName": "<YOUR DEVICE NAME>",
"requestIp": "<YOUR IP>",
"signature": "<YOUR SIGNATURE>"
}The customer must implement `http_request` to receive data returned to their cloud.
When passing data to `c_mmi_analyze_rsp`, the data must be in the same format as the `rsp` example.
3.3.5. Establish a service connection
/**
* @brief Gets the WSS server hostname string.
*
* This function returns the WSS server hostname string.
*
* @return char* Returns a pointer to the WSS server hostname string.
*/
char *c_mmi_get_wss_host(void);
/**
* @brief Gets the WSS server port string.
*
* This function returns the WSS server port string.
*
* @return char* Returns a pointer to the WSS server port string.
*/
char *c_mmi_get_wss_port(void);
/**
* @brief Gets the WSS service API path string.
*
* This function returns the WSS service API path string.
*
* @return char* Returns a pointer to the WSS service API path string.
*/
char *c_mmi_get_wss_api(void);
/**
* @brief Gets the WSS request header information string.
*
* This function returns the WSS request header information string.
*
* @return char*
* Returns a pointer to the WSS request header information string.
*/
char *c_mmi_get_wss_header(void);
Code Example 5
#include "c_mmi.h"
char *wss_host = c_mmi_get_wss_host();
char *wss_port = c_mmi_get_wss_port();
char *wss_api = c_mmi_get_wss_api();
char *wss_header = c_mmi_get_wss_header();
// Establish a WSS connection. The customer must implement the dummy_wss_connect interface.
WSS_HANDLE *wss = dummy_wss_connect(wss_host, wss_port, wss_api, wss_header);The WebSocket communication between this SDK and the cloud requires a Transport Layer Security (TLS) tunnel. You must configure the following:
The TLS version must be 1.2 or later.
Server Name Indication (SNI) must be enabled.
The Certificate Authority (CA) certificate (GlobalSign Root CA - R46) must be configured.
You must use the three specified interfaces to obtain the port, API, and header required to establish the WebSocket connection.
You must implement interfaces for WebSocket functions, such as `wss_connect`, `wss_register_recv_func`, `wss_register_send_func`, and `wss_send`. You can reuse existing solutions from the module vendor or chip manufacturer.
Example of an upgrade request message to establish a WebSocket connection
GET <WSS API> HTTP/1.1
Host: <WSS HOST>
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: FhVlQeR4S1N06+1/SU79XA==
Sec-WebSocket-Version: 13
<WSS HEADER>
Example response message for establishing a WebSocket connection
HTTP/1.1 101 Switching Protocols
upgrade: websocket
connection: upgrade
sec-websocket-accept: sqchBdVDX8kKBgi90/PFl5+/4VI=
date: Thu, 24 Jul 2025 08:25:24 GMT
server: istio-envoy
3.3.7. Event callback
enum {
C_MMI_EVENT_USER_CONFIG, // User configuration for the SDK, such as audio buffer size, work mode, and voice, should be implemented in this event callback.
C_MMI_EVENT_DATA_INIT, // This event is triggered when the SDK has finished initialization. You can start establishing the service connection in this event callback.
C_MMI_EVENT_SPEECH_READY, // This event is triggered after a WSS connection is correctly established. In push and tap modes, speech start can only be called after this event.
C_MMI_EVENT_SPEECH_PREPARE, // This event is triggered when the SDK is ready to start a new round of conversation.
C_MMI_EVENT_SPEECH_START, // This event is triggered when the SDK starts audio uplink.
C_MMI_EVENT_SPEECH_RESTART, // This event is triggered when the SDK restarts audio uplink.
C_MMI_EVENT_DATA_DEINIT, // This event is triggered after the SDK is unregistered.
C_MMI_EVENT_ASR_START, // This event is triggered when ASR starts returning data.
C_MMI_EVENT_ASR_INCOMPLETE, // This event returns incomplete ASR text data (full text).
C_MMI_EVENT_ASR_COMPLETE, // This event returns the complete ASR text data (full text).
C_MMI_EVENT_ASR_END, // This event is triggered when ASR ends.
C_MMI_EVENT_LLM_INCOMPLETE, // This event returns incomplete LLM text data (full text).
C_MMI_EVENT_LLM_COMPLETE, // This event returns the complete LLM text data (full text).
C_MMI_EVENT_TTS_START, // This event is triggered when audio downlink starts.
C_MMI_EVENT_TTS_END, // This event is triggered when audio downlink is complete.
C_MMI_EVENT_HEARTBEAT, // This event is triggered when the SDK receives a heartbeat reply from the cloud.
};
/**
* @brief MMI event callback function type.
*
* A callback function triggered when the MMI module's state changes or an event occurs.
*
* @param event The event type, which is one of the C_MMI_EVENT_xxx macros.
* @param param The event parameter, which points to different data structures depending on the event type.
* @return int32_t Returns 0 for successful processing, non-zero for failure.
*/
typedef int32_t(*c_mmi_event_callback)(uint32_t event, void *param);This callback interface is registered by configuring `c_mmi_config` during the initialization phase.
3.3.8. Multi-modal data interaction
/**
* @brief Writes data to the recorder buffer.
*
* @param data A pointer to the data to be written.
* @param size The length of the data in bytes.
* @return uint32_t The number of bytes actually written.
*/
uint32_t c_mmi_put_recorder_data(uint8_t *data, uint32_t size);
/**
* @brief Reads data from the player buffer.
*
* @param data The buffer to store the read data.
* @param size The available buffer size in bytes.
* @return uint32_t The number of bytes actually read.
*/
uint32_t c_mmi_get_player_data(uint8_t *data, uint32_t size);
/**
* @brief Gets the data to be sent via WebSocket.
*
* This function is used to get data of a specified type, preparing it to be sent via WebSocket.
* It fills the provided data buffer with data of the corresponding type based on the input type parameter.
*
* @param opcode Returns the WebSocket data type, such as WS_DATA_TYPE_TEXT or WS_DATA_TYPE_BINARY.
* @param data A pointer to a uint8_t array that will store the retrieved data.
* @param size The maximum capacity of the data array, in bytes.
* @return Returns the number of bytes actually filled into the data array.
*/
uint32_t c_mmi_get_send_data(uint8_t *opcode, uint8_t *data, uint32_t size);
/**
* @brief Analyzes the received WebSocket data.
*
* This function analyzes the received data packet based on the provided data type and content.
* Its main purpose is to parse the data content for further processing or use.
*
* @param opcode The WebSocket data type, such as WS_DATA_TYPE_TEXT or WS_DATA_TYPE_BINARY.
* @param data A pointer to the received data. The content will be parsed based on the type parameter.
* @param size The length of the data in bytes, used to determine the data's scope.
* @return Returns the number of bytes actually parsed.
*/
uint32_t c_mmi_analyze_recv_data(uint8_t opcode, uint8_t *data, uint32_t size);
Code Example 6
int dummy_wss_task_recv(void)
{
int opcode;
char payload_data[8 * 1024];
int recv_size;
while(1){
// Receive downstream data from the server via WebSocket.
dummy_wss_recv(&opcode, payload_data, &recv_size);
if(recv_size)
// Send the received downstream data to the SDK for parsing.
c_mmi_analyze_recv_data(opcode, payload_data, recv_size);
else
util_msleep(10);
}
return 0;
}
int dummy_wss_task_send(void)
{
uint8_t opcode;
uint8_t payload_data[8 * 1024];
uint32_t size;
while(1){
// Get the packaged payload data from the SDK.
size = c_mmi_get_send_data(&opcode, payload_data, sizeof(payload_data));
if (size == 0) {
util_msleep(10);
} else {
// Pass the payload data to the WebSocket send function, and package the frame header for sending.
dummy_wss_send(opcode, data, size);
}
}
return 0;
}
int _mmi_event_callback(uint32_t event, void *param)
{
char *text;
text = param;
switch (event) {
case C_MMI_EVENT_USER_CONFIG:
// Start a new conversation.
c_mmi_reset_dialog_id();
break;
case C_MMI_EVENT_DATA_INIT:
// MMI data is ready, start the network connection.
dummy_wss_init();
break;
case C_MMI_EVENT_DATA_DEINIT:
UTIL_LOG_W("will disconnect");
break;
case C_MMI_EVENT_SPEECH_START:
UTIL_LOG_D("enable recorder when send speech");
dummy_player_stop();
dummy_recorder_start();
break;
case C_MMI_EVENT_ASR_START:
UTIL_LOG_I("event [C_MMI_EVENT_ASR_START]");
break;
case C_MMI_EVENT_ASR_INCOMPLETE:
UTIL_LOG_D("ASR [%s]", text);
break;
case C_MMI_EVENT_ASR_COMPLETE:
if (text) {
UTIL_LOG_D("ASR C [%s]", text);
} else {
UTIL_LOG_D("ASR C [NULL]");
}
break;
case C_MMI_EVENT_ASR_END:
UTIL_LOG_D("disable record when ASR complete");
dummy_recorder_stop();
break;
case C_MMI_EVENT_LLM_INCOMPLETE:
UTIL_LOG_D("LLM [%s]", text);
break;
case C_MMI_EVENT_LLM_COMPLETE:
UTIL_LOG_D("LLM C [%s]", text);
break;
case C_MMI_EVENT_TTS_START:
UTIL_LOG_I("enable player when dialog start");
dummy_player_start();
break;
case C_MMI_EVENT_TTS_END:
break;
default:
break;
}
return UTIL_SUCCESS;
}Within the `c_mmi_analyze_recv_data` interface, the SDK triggers various event callbacks, which in turn call `_mmi_event_callback` for you to handle.