更新时间:2024-05-29 09:40:44下载pdf
MCU SDK 是根据涂鸦 IoT 开发平台定义的产品功能,自动生成的 MCU 代码,能够协助您快速完成 MCU 程序的开发。本文为您介绍移植 MCU SDK 的流程以及注意事项。
MCU SDK 是根据涂鸦 IoT 开发平台定义的产品功能,自动生成的 MCU 代码。为了减少您使用涂鸦 Cat.1 IoT 通用串口协议的对接成本,MCU SDK 已搭建通讯及协议解析架构。将 MCU SDK 添加至工程并配置相关信息后,就可以快速完成 MCU 程序开发。
涂鸦 SDK 包对 MCU 的要求如下。资源不足的用户可自行对接串口协议,SDK 包中的函数可以作为使用参考。
文件 | 说明 |
---|---|
mcu_api.c |
包含可供调用的函数。 |
mcu_api.h |
mcu_api.c 中的函数声明。 |
protocol.c |
包含协议数据体的内容处理函数。您可以根据项目需求,在相应函数内添加代码,获取 Cat.1 模组向 MCU 发送的数据。 |
protocol.h |
protocol.h 包含以下信息:
|
system.c |
串口通讯协议解析的具体实现。 |
system.h |
system.h 包含以下信息:
|
cellular.h |
包含 Cat.1 相关宏定义。 |
protocol.h
宏定义。protocol.c
文件及函数调用。在原项目工程中,完成 MCU 相关外设初始化,包括串口、外部中断(按键)、定时器(指示灯闪烁)等。
将 MCU SDK 文件夹中的 .c
和 .h
文件添加至相应头文件引用路径。
定义 PID:PRODUCT_KEY
为产品 PID 宏定义。PID 即产品 ID,为每个产品的唯一标识,可在涂鸦 IoT 开发平台的 产品管理页面 获取。
如果 PRODUCT_KEY
和产品 PID 不一致,请在 硬件开发 > 下载资料 下载最新 SDK 开发包后重试。
#define PRODUCT_KEY "ax23eawjo4np****"
定义版本号:MCU_VER
为软件版本,默认为 1.0.0
。若 MCU 需要 OTA 功能,需要添加新的 MCU 版本号。
#define MCU_VER "1.0.0"
定义功耗模式:MODULE_POWER
为功耗定义,默认为 0
。若 MCU 需要 Cat.1 模组进入低功耗模式,则定义为 1
。
定义模组类型:CELLULAR_MODULE_TYPE
为模组类型定义,若 Cat.1 模组选型为 LZ211,且需要 GNSS 功能时,则定义为1
,默认为 0
,表示当前模组类型为其他模组。
开启 OTA 升级功能。如果需要支持 OTA 固件升级,请定义 SUPPORT_MCU_FIRM_UPDATE
,默认关闭。
//#define SUPPORT_MCU_FIRM_UPDATE //开启 MCU 固件升级功能(默认关闭)
定义单次发送固件包的大小。
#ifdef SUPPORT_MCU_FIRM_UPDATE
#define PACKAGE_SIZE 0 //包大小为 256 字节
//#define PACKAGE_SIZE 1 //包大小为 512 字节
//#define PACKAGE_SIZE 2 //包大小为 1024 字节
#endif
OTA 升级流程,参考 OTA 升级说明。
串口接收缓存:大小受到串口数据处理被调用的频率影响。如果 MCU 对串口数据的处理较快,串口接收缓存大小可适当减小。
串口发送缓存:大小要大于数据最长的 DP 数据长度。
串口数据处理缓存:大小需要大于数据最长的 DP 数据长度,即大于最大数据量的大小。还要根据是否需要 OTA 功能和是否需要天气服务、天气服务类型数量和天数来调整大小。
/******************************************************************************
3:定义收发缓存:
如当前使用 MCU 的 RAM 不够,可修改为 24
******************************************************************************/
#ifndef SUPPORT_MCU_FIRM_UPDATE
#define CELLULAR_UART_RECV_BUF_LMT 16 //串口数据接收缓存区大小,如 MCU 的 RAM 不够,可缩小
#define CELLULAR_DATA_PROCESS_LMT 24 //串口数据处理缓存区大小,根据用户 DP 数据大小量定,必须大于 24
#ifdef SUPPORT_WIFI_LOCATION
#define CELLULAR_DATA_PROCESS_LMT 700
#endif
#else
#define CELLULAR_UART_RECV_BUF_LMT 128 //串口数据接收缓存区大小,如 MCU 的 RAM 不够,可缩小
//请在此处选择合适的串口数据处理缓存大小(根据上面 MCU 固件升级包选择的大小和是否开启天气服务,选择开启多大的缓存)
#define CELLULAR_DATA_PROCESS_LMT 300 //串口数据处理缓存大小,如需 MCU 固件升级,若单包大小选择 256
//则缓存必须大于 260,若开启天气服务,则需要更大
//#define CELLULAR_DATA_PROCESS_LMT 600 //串口数据处理缓存大小,如需 MCU 固件升级,若单包大小选择 512
//则缓存必须大于 520,若开启天气服务,则需要更大
//#define CELLULAR_DATA_PROCESS_LMT 1200 //串口数据处理缓存大小,如需 MCU 固件升级,若单包大小选择 1024
//则缓存必须大于 1030,若开启天气服务,则需要更大
#endif
#define CELLULAR_UART_SEND_BUF_LMT 48 //根据用户 DP 数据大小量定,必须大于 48
#ifdef SUPPORT_WIFI_LOCATION
#define CELLULAR_UART_SEND_BUF_LMT 256
#endif
/******************************************************************************
如果重置按键和 LED 接在 MCU 端模组,即选择模组和 MCU 配合处理工作模式(常用),保持CELLULAR_CONTROL_SELF_MODE
宏定义处于被注释状态。
//#define CELLULAR_CONTROL_SELF_MODE //蜂窝自处理按键及 LED 指示灯,如为 MCU 外界按键/LED 指示灯,请关闭该宏
如果状态指示灯和按键是接在 Cat.1 模组上的,即选择模组自处理工作模式,开启 CELLULAR_CONTROL_SELF_MODE
宏定义,然后根据实际的硬件连接,将按键所连接的 GPIO 脚位填入CELLULAR_RESERT_KEY
宏定义。
模组自处理模式下,状态指示灯需要接到 NET_MODE
的 PIN 脚。
#define CELLULAR_STATE_KEY 0 //蜂窝模组状态指示灯固定为 NET_MODE 灯,所以该宏配置随意
#define CELLULAR_RESERT_KEY 21 //蜂窝模组重置按键,请根据实际 GPIO 管脚设置
在 MCU 串口及其他外设初始化后调用 mcu_api.c
文件中的 cellular_protocol_init()
函数。
将 MCU 串口单字节发送函数填入 protocol.c
文件中 uart_transmit_output
函数内,并删除 #error
。示例如下:
/**
* @brief 串口发送数据
* @param[in] {value} 串口要发送的 1 字节数据
* @return Null
*/
void uart_transmit_output(u8 value)
{
#error "请将 MCU 串口发送函数填入该函数,并删除该行"
/*
//Example:
extern void Uart_PutChar(u8 value);
Uart_PutChar(value); //串口发送函数
*/
}
在串口接收中断服务函数里面调用 mcu_api.c
文件内的 uart_receive_input
函数,并将接收到的字符作为参数传入。示例如下:
void USART3_IRQHandler(void)
{
unsigned char Res=0;
if((USART3->SR&UART_FLAG_RXNE) != 0)
{
Res=USART3->DR;
uart_receive_input(Res);
}
}
单片机进入 while(1)
循环后调用 mcu_api.c
文件内的 cellular_uart_service()
函数。main.c
中示例代码结构如下:
#include "celluluar.h"
...
void main(void)
{
cellular_protocol_init();
...
while(1)
{
cellular_uart_service();
...
}
...
}
MCU 必须在 while(1)
中直接调用 mcu_api.c
内的 cellular_uart_service()
函数。程序正常初始化完成后,建议不进行关中断,如果必须关中断,关中断时间必须短。关中断会引起串口数据包丢失,请勿在中断内调用上报函数。
详情请参考 Wi-Fi 通用对接中的 MCU SDK 移植。
仅 MCU 与模组配合处理模式工作模式需要设置配网功能及指示灯函数。
移植协议成功后,如果需要设备激活,还需要添加配网指令以及指示灯功能。配合处理模式的 MCU、激活触发方式和指示方式,可以根据实际情况自行决定,通常为按键触发和 LED 快闪/慢闪指示。
激活指令可以通过函数实现:mcu_reset_cellular()
。通常在按键触发激活后,在按键处理函数中调用。
调用后复位 Cat.1 模组,复位后之前的激活信息全部清除,模组连上运营商网络后,会自动进入待激活状态。
通常在 while(1)
调用 mcu_get_cellular_connect_status()
函数获取 Cat.1 模组联网状态。根据 Cat.1 状态,写入相应闪灯的模式。
设备联网状态 | 描述 | 状态值 | LED 显示 |
---|---|---|---|
状态 1 | SIM 卡未插入 | 0 | 常亮 |
状态 2 | 运营商网搜索中 | 1 | 闪烁间隔 300ms |
状态 3 | 运营商网注册成功 | 2 | 闪烁间隔 1000ms |
状态 4 | 设备获取到 IP 地址 | 3 | 闪烁间隔 2000ms |
状态 5 | 设备连接云端 | 4 | 闪烁间隔 3000ms |
状态 6 | SIM 卡拒绝注册 | 5 | 闪烁间隔 100ms |
状态 7 | 设备待激活 | 6 | 闪烁间隔 2000ms |
调用函数 mcu_get_cellular_connect_status()
获取连接状态,函数架构如下:
void main(void)
{
...
while(1)
{
...
switch(mcu_get_cellular_connect_status())
{
case NO_SIM:
//设备未检测到 SIM 卡,即 LED 常亮
break;
case SEARCH_NETWORK:
//设备查询运营商网络,即 LED 闪烁间隔 300 毫秒
break;
case LOGINED_DISCONNECTED:
//设备运营商注册刚成功,未连接网络 即 LED 闪烁间隔 1 秒
case CONNECTED_GET_IP:
//获取到 IP 地址,即 LED 闪烁间隔 2 秒
break;
case CONNECTED_CLOUD:
//云端连接成功,即 LED 闪烁间隔 3 秒
break;
case DEVICE_WAIT_ACTIVE:
//设备待激活,即 LED 闪烁间隔 2 秒
break;
default:break;
}
...
}
}
Cat.1 产测功能默认开启。为了保证最终量产效率及品质,建议开启该功能。
#define CELLULAR_TEST_ENABLE //开启 Cat.1 产测功能(检查 SIM 卡是否正常、是否经过授权,RF 是否经过校准,及信号强度)
产测支持 4 个测试项检测:
开启产测支持。请定义 SET_FEATURE_TEST_ENABLE
,默认关闭。
//#define SET_FEATURE_TEST_ENABLE //开启模组自检功能
使用过程中,MCU 调用 mcu_api.c
中 mcu_set_feature_test()
函数,让 Cat.1 模组自检。
/**
* @brief 开始蜂窝设备自检功能
* @param Null
* @return Null
* @note MCU 需要自行调用该功能
*/
void mcu_set_feature_test(void)
{
cellular_uart_write_frame(GET_FEATURE_TEST_CMD, MCU_TX_VER, 0);
}
MCU 收到自检功能的应答后,需要根据收到应答增加处理功能。protocol.c
文件中有 get_feature_test_result()
的实现。
#ifdef SET_FEATURE_TEST_ENABLE
/**
* @brief 获取模组的自检状态
* @param[in] {sim_st} SIM 卡状态。1:succeed,0:fail
* @param[in] {auth} 是否经过涂鸦授权产测。1:succeed,0:fail
* @param[in] {rf} RF 射频是否经过校准。1:校准完成,0:未校准
* @param[in] {signal} 获取当前设备的信号强度 0~31
* @return Null
* @note MCU needs to implement this function by itself
*/
void get_feature_test_result(u8 sim_st,u8 auth,u8 rf,u8 signal)
{
#error "Complete the RSSI fetch processing code yourself and delete the line"
}
#endif
请在初始化完成后再进行数据交互,避免不可预测的问题。
帧格式请参考 Cat.1 IoT 通用串口协议。
在 MCU 和模组上电后,需要进行初始化配置。初始化通信包括但不限于以下内容:
心跳包是 MCU 和模组之间定时通信的数据,可作为验证 MCU 或者模组是否正常工作的依据。建议将 MCU 和模组上电后的第一次通信的帧作为心跳包。只有在心跳包发送和回复都正常的情况下,才可以进行通信。
心跳包正常交互后,模组需要查询 MCU 固件的以下信息。
用来设置模组工作方式,选择 MCU 与模组配合处理方式或者模组自处理方式。可根据 Cat.1 网络状态指示灯和模组重置按键的硬件连接位置选择。
Cat.1 模组初始化数据交互流程如下图所示:
涂鸦 App 和设备之间能够实现 DP 下发和上报:
Cat.1 模组的网络状态为状态 4:设备连上路由和涂鸦 IoT。此时 DP 可以正常上报。
MCU 与 Cat.1 模组在进行 OTA 数据交互时,数据包交互的数据量比较多,会出现一些异常情况。本章节介绍 Cat.1 模组的处理逻辑。
文件下载的逻辑和 OTA 实质上一样。可以将本章节中的 OTA 替换成文件下载,作为文件下载逻辑的介绍。
OTA 启动
OTA 数据传输
Cat.1 模组在发送一包 OTA 数据之后,如果 5s 内没有收到 MCU 的回复,会进行重发。若重发 3 次之后 MCU 依旧没有回复,就认为 OTA 失败。
OTA 传输完成
支持以下两种方式判断 OTA 传输是否完成。
帧长度为 0x0004
,即 OTA 数据包长度为 0。
包偏移等于 Cat.1 模组发送升级启动帧时的固件包总长度。
建议同时使用以上两种方式检测 OTA 传输情况。
蜂窝类接口定义在 protocol.c
中的的宏定义 CELLULAR_SERVICE_ENABLE
。
#define CELLULAR_SERVICE_ENABLE //开启蜂窝服务
Cat.1 模组有 2 种蜂窝工作模式。模组启动默认为全功能模式。
获取蜂窝工作模式
MCU 调用 mcu_api.c
里的 mcu_get_cellular_work_mode()
向 Cat.1 模组发送获取当前蜂窝工作模式。
/**
* @brief 获取 Cat.1 的工作模式
* @param Null
* @return Null
* @note MCU 需要自行实现该功能
*/
void mcu_get_cellular_work_mode(void)
{
u16 length = 0;
length = set_cellular_uart_byte(length, GET_CELLULAR_WORK_MODE);
cellular_uart_write_frame(GET_CELLULAR_CMD, MCU_TX_VER, length);
}
MCU 通过 protocol.c
里的 get_cellular_work_mode_result()
函数获取到蜂窝工作模式。该函数中有 #error
,MCU 需要去掉,另外需要根据自身需求实现具体功能。
/**
* @brief Get the cellular work mode
* @param[in] {result} work mode
* @return Null
* @note MCU needs to implement this function by itself
*/
void get_cellular_work_mode_result(u8 result)
{
#error "Complete the work mode fetch processing code yourself and delete the line"
switch(result) {
case 1:
//Full funtion mode
break;
case 4:
//Airplane mode
break;
default:break;
}
}
设置蜂窝工作模式
MCU 调用mcu_api.c
里的 mcu_set_cellular_mode()
向 Cat.1 模组发送设置当前蜂窝工作模式。
/**
* @brief MCU 设置蜂窝通讯工作模式
* @param[in] {mode} 进入的模式
* @ref 1:全功能模式
* @ref 4:飞行模式
* @return Null
* @note 1:MCU 主动调用
* 2:成功后,可判断 set_cellularmode_flag 是否为 TRUE,TRUE 表示为设置工作模式成功
* 3:如果为模组自处理模式,MCU 无须调用该函数
*/
void mcu_set_cellular_mode(u8 mode)
{
u8 length = 0;
set_cellularmode_flag = SET_CELLULARCONFIG_ERROR;
length = set_cellular_uart_byte(length, mode);
cellular_uart_write_frame(SET_CELLULAR_WORK_MODE, MCU_TX_VER, length);
}
MCU 通过 mcu_api.c
里的 mcu_get_cellular_mode_flag()
函数获取到蜂窝工作模式是否成功。
模组成功连上路由之后,定义 WEATHER_ENABLE
即可开通天气服务功能。模组连接上云端后会立即下发天气数据,此后每隔 30 分钟下发 1 次。
开启天气服务功能。定义 WEATHER_ENABLE
。
//#define WEATHER_ENABLE //打开天气功能
选择支持的天气类型。您可以在 protocol.c
文件的 weather_choose
数组中,选择支持天气数据的参数。
#ifdef WEATHER_ENABLE
/**
* @var weather_choose
* @brief 天气数据参数选择数组
* @note 用户可以自定义需要的参数,注释或者取消注释即可,注意更改
*/
const i8 *weather_choose[WEATHER_CHOOSE_CNT] = {
"temp",
"humidity",
"condition",
"pm25",
/*"pressure",
"realFeel",
"uvi",
"tips",
"windDir",
"windLevel",
"windSpeed",
"sunRise",
"sunSet",
"aqi",
"so2 ",
"rank",
"pm10",
"o3",
"no2",
"co",
"conditionNum",*/
};
#endif
设置支持的天气的个数。将支持的参数个数在 WEATHER_CHOOSE_CNT
中定义。
#define WEATHER_CHOOSE_CNT 4 //选择的需要天气服务类型的数目
设置预报天数。新版固件的模组将支持最多 7 天的天气预报功能,预报的天数可以在 WEATHER_FORECAST_DAYS_NUM
宏定义中设置。
1
:表示只获取当天的数据。0
或大于 7
:模组将回复错误信息,天气服务打开失败。#define WEATHER_FORECAST_DAYS_NUM 1 //设置天气预报的天数
模组发送的天气数据中带有天数的信息,此时 0
表示当天。
当前的定位功能有三种类型。
GNSS 设备定位
根据 模组功能列表 的定位服务来决定是否需要外接 GPS 模组。GNSS 开启后,功耗会升高,平均电流增加 30mA。
Wi-Fi 定位
Wi-Fi 定位一般作为室内定位使用。
LBS 定位
LBS 定位为基站定位。
如果需要支持定位功能,请定义 GNSS_SERIVCE_ENABLE
,默认关闭。
//#define GNSS_SERIVCE_ENABLE //开启地理定位服务(默认关闭)
GNSS 定位默认为不开启,MCU 调用 mcu_api.c
中的 mcu_control_gnss()
函数开启或者关闭。
当所选模组为 LZ211 时,需要注意由于 LZ211 模组内置 GPS,当使用 GNSS 服务时,需要通过mcu_control_gnss_power
接口给 GPS 供电。在获取电源状态为加载固件完成时,再调用mcu_control_gnss()
打开 GPS 定位功能。
实现代码示例:
/**
* @brief 控制 GNSS 服务
* @param [in] {enable} true:启动,false:关闭
* @param [in] {mode} GNSS 定位跟踪类型
* @return Null
* @note MCU 需要自行实现该功能
*/
void mcu_control_gnss(bool_t enable,GNSS_ATTACH_MODE_e mode)
{
u16 length = 0;
u8 cmd[3] = {0};
cmd[0] = SET_CELLULAR_CTRL_GNSS;
cmd[1] = enable;
cmd[2] = mode;
length = set_cellular_uart_buffer(length, cmd,sizeof(cmd));
cellular_uart_write_frame(SET_CELLULAR_CMD, MCU_TX_VER, length);
}
在定位控制中,主要考虑enbale
参数和 mode
参数。其中 mode
参数定义 GNSS 设备采用何种定位系统。
定位系统的定义在 system.h
中。
//GNSS 的定位跟踪模式
typedef enum{
GNSS_ATTACH_MODE_GPS_BD, //GPS+BEIDOU 定位模式
GNSS_ATTACH_MODE_GPS_GL, //GPS+GLONASS 定位模式
GNSS_ATTACH_MODE_GPS, //GPS 定位模式
GNSS_ATTACH_MODE_BD, //北斗定位模式
GNSS_ATTACH_MODE_GL, //GLONASS 定位模式
GNSS_ATTACH_MODE_GPS_GA, //GPS+伽利略定位模式
}GNSS_ATTACH_MODE_e;
系统默认为 GNSS_ATTACH_MODE_GPS_BD
模式。当前 GUC300/LZ211 不支持GNSS_ATTACH_MODE_GL
和 GNSS_ATTACH_MODE_GPS_GA
。
控制的返回处理在 protocl.c
的 get_set_gnss_result()
函数中。用户需要自行处理。
/**
* @brief 获取设置 GNSS 开启或关闭的结果
* @param[in] {gnss_status} 设置 GNSS 状态返回值
* @param[in] {gnss_mode} GNSS 定位模式状态返回
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_set_gnss_result(u8 gnss_status, u8 gnss_mode)
{
#error "请自行实现设置 GNSS 定位功能处理代码,完成后请删除该行"
if (!gnss_status) {
//设置失败
}
else {
if (!gnss_mode) {
//设置失败
}
else {
//设置成功
}
}
}
MCU 调用 mcu_api.c
里的 mcu_reset_gnss()
函数,发送复位命令给 Cat.1 模组,复位 GNSS 设备。
一般 MCU 开机或者复位,建议调用此命令,复位 GNSS 设备。该复位是 GNSS 硬件复位。另外,复位功能仅适用于 LZ201 这种可以外挂 GNSS 的模组,LZ211 不适用。
/**
* @brief 复位 GNSS 设备
* @param [in] {pin} 蜂窝设备连接到 GNSS 设备 RST 的 PIN 脚
* @return [in] {level} true:高电平复位,false:低电平复位
* @note MCU 需要自行实现该功能
*/
void mcu_reset_gnss(u8 pin,bool_t level)
{
u16 length = 0;
u8 cmd[3] = {0};
cmd[0] = SET_CELLULAR_RESET_GNSS;
cmd[1] = pin;
cmd[2] = level;
length = set_cellular_uart_buffer(length, cmd,sizeof(cmd));
cellular_uart_write_frame(SET_CELLULAR_CMD, MCU_TX_VER, length);
}
复位结果获取查看 protocl.c
中的 reset_gnss_result()
函数,用户需要自行实现。
实现代码示例:
/**
* @brief 复位 GNSS 设置
* @param[in] {result} 设置状态返回值
* @param[in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
static void reset_gnss_result(u8 result)
{
#error "请自行实现设置蜂窝设备的定位功能处理代码,完成后请删除该行"
if (!result) {
//设置失败
}
else {
//设置成功
}
}
MCU 调用mcu_api.c
里的 mcu_get_gnss_location()
函数,发送获取 GNSS 定位信息命令给 Cat.1 模组。
为了确保能成功获取定位信息,建议在获取定位信息前,先获取 GNSS 设备信号强度。由于 GNSS 搜星需要一定的时间,冷启动关机时间超过 2 小时,搜星时间在 2 分钟左右。
在 protocol.c
中 get_gnss_location_result()
函数等待获取定位信息结果。
/**
* @brief 获取设备 GNSS 定位信息
* @param[in] {location} gnss 定位信息,字符串类型:如 120.661,32.221 (经度、纬度)
* @param[in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_gnss_location_result(u8 location[], u16 data_len)
{
#error "请自行实现获取 GNSS 定位信息处理代码,完成后请删除该行"
u8 ret = location[0];
if (!ret) {
//获取失败
}
else {
//获取成功
}
}
函数 get_gnss_location_result()
中,在获取成功中,返回的定位信息为字符串类型,内容为经纬度信息。MCU 可以直接把字符串中的信息,通过 DP 上报。
MCU 自己调用 mcu_api.c
中的 mcu_get_gnss_rssi()
函数,发送获取信号强度命令。
获取信号强度的解析函数在 protocol.c
中的 get_gnss_rssi_result()
函数。
/**
* @brief 获取设备 GNSS 当前信号强度
* @param[in] {rssi} gnss 设备的当前信号强度
* @param[in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_gnss_rssi_result(u8 rssi[], u16 data_len)
{
#error "请自行实现获取 GNSS 定位信息处理代码,完成后请删除该行"
u8 ret = rssi[0];
u8 rssi = rssi[1];
if (!ret) {
//获取失败
}
else {
//获取成功
}
}
信号强度范围 0~100,一般只要信号超过 30 即可。GNSS 设备 3D 定位没有成功情况下,返回失败。
MCU 自己调用 mcu_api.c
中的 mcu_get_gnss_speed()
函数,发送获取信号强度命令。
获取信号强度的解析函数在 protocol.c
中的 get_gnss_rssi_result()
函数。
/**
* @brief 获取设备 GNSS 当前速度
* @param[in] {speed} GNSS 设备的当前速度,单位 100m/H
* @param[in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_gnss_speed_result(u8 speed[], u16 data_len)
{
#error "请自行实现获取 GNSS 定位信息处理代码,完成后请删除该行"
u8 ret = speed[0];
u16 u16speed = speed[1] << 8 | speed[2];
if (!ret) {
//获取失败
}
else {
//获取成功
}
}
获取到速度后,可以直接通过 DP 上报。
MCU 自己调用 mcu_api.c
中的 mcu_set_gnss_lat_lg_location()
函数,发送查询纬经度信息的命令。
获取纬经度信息的函数在 protocol.c
中的 get_gnss_location_lat_lg_result()
函数。
/**
* @brief 获取设备 GNSS 纬经度定位信息
* @param[in] {location} GNSS 定位信息,字符串类型:如 32.221,120.661(纬度、经度)
* @param[in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_gnss_location_lat_lg_result(u8 location[], u16 data_len)
{
#error "请自行实现获取 GNSS 定位信息处理代码,完成后请删除该行"
u8 ret = location[0];
if (!ret) {
//获取失败
}
else {
//获取成功
}
}
MCU 自己调用 mcu_api.c
中的 mcu_set_cellular_auto_rpt_gps(u16 period, u8 dpid)
函数,设置 GNSS 定位信息上报的周期,周期参数的含义参考协议文档。
在 protocol.c
中的 get_set_cellular_auto_rpt_gps_result()
函数查看返回的结果。
/**
* @brief 获取设置 GPS 定位周期性上报的返回结果
* @param[in] {result} 设置状态返回值
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_set_cellular_auto_rpt_gps_result(u8 result)
{
#error "请自行实现获取设置 GPS 定位周期性上报功能返回结果的处理代码,完成后请删除该行"
if (!result) {
//设置失败
}
else {
//设置成功
}
}
如果需要支持该功能,请定义 CELLULAR_MODULE_TYPE
为 1
,默认为 0
。
#define CELLULAR_MODULE_TYPE 0//蜂窝模组类型配置,0:lz201,1:lz211
MCU 调用 mcu_api.c
中的 mcu_control_gnss_power()
函数,控制 GNSS 设备主电源开启和关闭。
设置 GNSS 设备主电源开启或关闭的解析函数在 protocol.c
中的 get_set_gnss_power_result()
函数。
/**
* @brief 设置 GNSS 设备主电源开启或关闭
* @param[in] {result} 设置主电源开启或关闭的返回值
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_set_gnss_power_result(u8 result)
{
#error "请自行实现设置蜂窝设备的 GNSS 主电源控制功能处理代码,完成后请删除该行"
if (!result) {
//设置失败
}
else {
//设置成功
}
}
如果需要支持该功能,请定义 CELLULAR_MODULE_TYPE
为 1
,默认为0
。
#define CELLULAR_MODULE_TYPE 0//蜂窝模组类型配置,0:lz201,1:lz211
MCU 调用 mcu_api.c
中的 mcu_get_gnss_power_status()
函数,获取 GNSS 设备电源状态。
获取 GNSS 设备主电源状态的解析函数在 protocol.c
中的 get_gnss_power_status_result()
函数。
/**
* @brief 获取 GNSS 设备主电源状态
* @param[in] {result} 获取电源状态的结果
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_gnss_power_status_result(u8 result)
{
#error "请自行实现获取蜂窝设备的主电源状态功能处理代码,完成后请删除该行"
switch(result) {
case 0:
//电源关闭
break;
case 1:
//固件加载中
break;
case 2:
//电源开启,固件加载完成
break;
default:break;
}
}
调用 mcu_api.c
中的 mcu_control_wifi_location()
函数,启动或者关闭 Wi-Fi 定位功能。
启动后,Cat.1 模组会每间隔 10 秒扫描一次 AP 信息,扫描一次 AP 持续 1 秒。Wi-Fi 定位启动后,蓝牙会被关闭。
获取执行结果的接口在 protocol.c
中。具体处理结果用户自行实现。
/**
* @brief 打开或者关闭 Wi-Fi 定位功能
* @param[in] {result} 设置状态返回值
* @param[in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
static void set_cellular_wifi_result(u8 result)
{
#error "请自行实现设置蜂窝设备的 Wi-Fi 定位功能处理代码,完成后请删除该行"
if (!result) {
//设置失败
}
else {
//设置成功
}
}
调用 mcu_api.c
中的 mcu_get_wifi_location()
函数,启动或者关闭 Wi-Fi 定位功能。
获取执行结果的接口在 protocol.c
中 get_cellular_wifi_location_result()
。具体处理结果用户自行实现。
固件 1.0.7 版本中获取的定位信息格式为 apn_num+"["1900cee08d77",-66],["3a00c0e08c77",-62]..."
,由于 DP 只支持 apn_num+"xxx,xx"
的格式,所以需要转换信息格式。
/**
* @brief 获取设备蜂窝设备 Wi-Fi 定位信息
* @param[in] {location} GNSS 定位信息,字符串类型:如 0x02["b27e525dc87d",-64],["957e5b5d087d",-64]
* @param[in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
static void get_cellular_wifi_location_result(u8 ap_info[], u16 data_len)
{
//#error "请自行实现获取蜂窝设备的 Wi-Fi 定位信息处理代码,完成后请删除该行"
/**/
...
if (ap_count%MAX_AP_COUNT) {
loop_count ++;
}
if (data_len < 2 | | data_len >= CELLULAR_DATA_PROCESS_LMT) {
return;
}
if (!ap_count) {
//获取失败
}
else {
//获取成功
if (strchr((char*)ap_info+1,'[')) { //如果是 1 格式
remove_chacator(ap_info+1,wifi_info,'[','"',']');
}
else { //格式:112233445566,-XX(-x)
memcpy(wifi_info,ap_info+1,data_len -1);
}
if (strlen(wifi_info) < 200) {
//mcu_dp_string_update 直接上传给涂鸦 IoT 云。客户自行调用
}
else {
ptemp = (char*)wifi_info;
for (j = 0; j < loop_count; j ++) {
memset(ap_info,0,data_len);
psrc = ap_info;
if (j < loop_count-1) {
send_ap_count = MAX_AP_COUNT;
}
else {
send_ap_count = ap_count%MAX_AP_COUNT;
}
for ( i = 0; i < send_ap_count; i ++) {
sscanf(ptemp,"%[^,],%[^,]",mac,rssi);
if (strlen(rssi) && strlen(mac)) {
sprintf(psrc+strlen(psrc),"%s,",mac);
sprintf(psrc+strlen(psrc),"%s",rssi);
ptemp += strlen(mac);
ptemp += 1; //‘,’
ptemp += strlen(rssi);
if (strchr(ptemp,',')) {
ptemp += 1;
}
if (i < send_ap_count -1) {
sprintf(psrc+strlen(psrc),"%s",",");
}
}
memset(mac,0,sizeof(mac));
memset(rssi,0,sizeof(rssi));
}
//mcu_dp_string_update(dip,psrc,strlen(psrc));
}
}
调用 mcu_api.c
中的 mcu_set_cellular_auto_rpt_wifi(u16 period, u8 dpid)
函数,设置 Wi-Fi 定位信息上报的周期,周期参数含义参考协议文档。
获取执行结果的接口在 protocol.c
中。具体处理结果用户自行实现。
/**
* @brief 获取设置 Wi-Fi 定位周期性上报的返回结果
* @param[in] {result} 设置状态返回值
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_set_cellular_auto_rpt_wifi_result(u8 result)
{
#error "请自行实现获取设置 Wi-Fi 定位周期性上报功能返回结果的处理代码,完成后请删除该行"
if (!result) {
//设置失败
}
else {
//设置成功
}
}
调用 mcu_api.c
中的 mcu_get_lbs_location()
函数,获取 LBS 定位信息。
获取执行结果的接口在 protocol.c
中 get_cellular_lbs_location_result()
。具体处理结果用户自行实现。
/**
* @brief 获取 LBS 定位信息
* @param [in] {location} {lbs_info} LBS 定位信息,字符串类型:如"46011,e615,04bafc0a"(运营商编码+位置区域码+基站编码)
* @param [in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_cellular_lbs_location_result(u8 ap_info[], u16 data_len)
{
#error "请自行实现获取蜂窝设备的 LBS 定位功能处理代码,完成后请删除该行"
u8 ret = lbs_info[0];
if (!ret) {
//获取失败
}
else {
//获取成功
}
}
调用 mcu_api.c
中的 mcu_set_cellular_auto_rpt_lbs(u16 period, u8 dpid)
函数,设置 LBS 定位信息上报的周期。周期参数含义参考协议文档。
获取执行结果的接口在 protocol.c
中 get_set_cellular_auto_rpt_lbs_result(u8 result)
。具体处理结果用户自行实现。
/**
* @brief 获取设置 LBS 定位周期性上报的返回结果
* @param[in] {result} 设置状态返回值
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_set_cellular_auto_rpt_lbs_result(u8 result)
{
#error "请自行实现获取设置 LBS 定位周期性上报功能返回结果的处理代码,完成后请删除该行"
if (!result) {
//设置失败
}
else {
//设置成功
}
}后请删除该行"
}
调用 mcu_api.c
中的 mcu_get_battery()
函数,获取电池电量。
获取执行结果的接口在 protocol.c
中 get_cellular_vbat_vol_result()
。具体处理结果用户自行实现。
/**
* @brief 获取电池电量
* @param [in] {battery} 电池电量
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_cellular_vbat_vol_result(u8 battery)
{
#error "请自行实现获取蜂窝设备的电池电量功能处理代码,完成后请删除该行"
}
调用 mcu_api.c
中的 mcu_get_charging_status()
函数,获取蜂窝模组充电状态。
获取执行结果的接口在 protocol.c
中 get_vbat_charging_status_result()
。具体处理结果用户自行实现。
/**
* @brief 获取充电状态
* @param [in] {status} 充电状态
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_vbat_charging_status_result(u8 status)
{
#error "请自行实现获取蜂窝设备的充电状态功能处理代码,完成后请删除该行"
if (status == 1) {
//开始充电
}
else if (status == 2) {
//充电结束
}
else if (status == 3) {
//电量低
}
else if (status == 4) {
//电量超低
}
else if (status == 5) {
//电池拔出
}
else if (status == 6) {
//充电器故障
}
else if (status == 7) {
//充电故障
}
else {
return;
}
}
调用 mcu_api.c
中的 mcu_control_set_volume()
函数,控制蜂窝模组音量设置。
获取执行结果的接口在 protocol.c
中 get_set_cellular_volume_result()
。具体处理结果用户自行实现。
/**
* @brief 获取音量设置结果
* @param [in] {cmd} 1:本地音量,2:通话音量
* @param [in] {result} 音量设置结果
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_set_cellular_volume_result(u8 cmd, u8 result)
{
#error "请自行实现获取蜂窝设备的音量设置功能处理代码,完成后请删除该行"
switch (cmd) {
case 1:{
if (!result) {
//获取成功
}
else {
//获取失败
}
}
break;
case 2:{
if (!result) {
//获取成功
}
else {
//获取失败
}
}
break
default:break;
}
}
调用 mcu_api.c
中的 mcu_control_audio_play()
函数,控制蜂窝模组 SD 卡中音频播放。
获取执行结果的接口在 protocol.c
中 get_contrl_cellulat_audio_play_result()
。具体处理结果用户自行实现。
/**
* @brief 获取音频播放结果
* @param [in] {play_info} 蜂窝模组返回的播放信息
* @param [in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_contrl_cellulat_audio_play_result(u8 play_info[], u16 data_len)
{
#error "请自行实现获取蜂窝设备的音频播放功能处理代码,完成后请删除该行"
if (data_len < 4) {
return;
}
u8 result = play_info[3];
if (!result) {
//获取成功
}
else {
//获取失败
}
}
调用 mcu_api.c
中的 mcu_get_audio_play_status()
函数,获取本地音频播放状态。
获取执行结果的接口在 protocol.c
中 get_voice_play_status_result()
。具体处理结果用户自行实现。
/**
* @brief 获取音频播放状态
* @param [in] {cmd} 0:空闲,1:播放中,2:播放中止,3:播放完成
* @param [in] {result} 播放设置结果
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_voice_play_status_result(u8 cmd, u8 result)
{
#error "请自行实现获取蜂窝设备的音频播放状态功能处理代码,完成后请删除该行"
switch (cmd) {
case 0:{
if (!result) {
//获取成功
}
else {
//获取失败
}
}
break;
case 1:{
if (!result) {
//获取成功
}
else {
//获取失败
}
}
break;
case 2:{
if (!result) {
//获取成功
}
else {
//获取失败
}
}
break
case 3:{
if (!result) {
//获取成功
}
else {
//获取失败
}
}
break
default:break;
}
}
调用 mcu_api.c
中的 mcu_contrl_cellular_mem_audio()
函数,对内存音频播放进行控制。
获取执行结果的接口在 protocol.c
中 get_cellular_contrl_mem_audio_result()
。具体处理结果用户自行实现。
/**
* @brief 获取内存音频指令结果
* @param[in] {data} 数据
* @param[in] {len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_cellular_contrl_mem_audio_result(u8 *data,u16 len)
{
u8 cmd = data[0];
if (cmd != 5) {
u8 result = data[1];
if(!result) {
//success
} else {
//failure
}
}
else {
u8 audio_sum = data[1];
}
}
调用 mcu_api.c
中的 mcu_contrl_cellular_tts_audio()
函数,对在线 TTS 服务进行请求及查询。
获取执行结果的接口在 protocol.c
中 get_cellular_ctrl_tts_result()
。具体处理结果用户自行实现。
#ifdef SUPPORT_ONLINE_TTS
/**
* @brief 在线 TTS 控制指令结果
* @param[in] {result} 指令结果
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_cellular_ctrl_tts_result(char result)
{
if (result == 0){
//success
}
else{
//failure
}
}
#endif
模组收到短信后,发送给 MCU。当 MCU 收到模组的短信后,需要调用 mcu_api.c
中的 mcu_control_recv_sms_rsp()
函数,控制接收短信是否成功。
调用的接口在 protocol.c
中 get_contrl_sms_result()
。具体处理结果用户自行实现。
/**
* @brief 获取短信业务返回结果
* @param [in] {sms_info} 短信业务返回信息
* @param [in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_contrl_sms_result(u8 sms_info[], u16 data_len)
{
#error "请自行实现设置蜂窝设备的短信功能处理代码,完成后请删除该行"
if (data_len < 2) {
return;
}
u8 result = 0;
u8 cmd = sms_info[0];
switch (cmd) {
case 0:
//接收到短信
break;
case 1:{
//发送短信
result = sms_info[1];
if (!result) {
//发送失败
}
else {
//发送成功
}
}
break;
default:break;
}
}
调用 mcu_api.c
中的 mcu_control_send_sms()
函数,控制蜂窝模组发送短信。
获取执行结果的接口在 protocol.c
中 get_contrl_sms_result()
。具体处理结果用户自行实现。
具体代码框架,请参考 控制接收短信是否成功。
电话呼入
模组收到电话提醒后,发送给 MCU。当 MCU 收到模组的电话提醒后,需要调用 mcu_api.c
中的
mcu_control_phone_callin_rsp()
函数,通知蜂窝模组,收到来电。
调用的接口在 protocol.c
中 get_contrl_phone_result()
。具体处理结果用户自行实现。
/**
* @brief 获取电话业务返回结果
* @param [in] {phone_info} 电话业务返回信息
* @param [in] {data_len} 数据长度
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_contrl_phone_result(u8 phone_info[], u16 data_len)
{
#error "请自行实现设置蜂窝设备的电话业务功能处理代码,完成后请删除该行"
if (data_len < 2) {
return;
}
u8 result = 0;
u8 cmd = phone_info[0];
switch (cmd) {
case 0:
//电话呼入提醒
mcu_control_phone_callin_rsp();
break;
case 1:{
//电话呼出结果
result = phone_info[1];
if (!result) {
//呼出失败
}
else {
//呼出成功
}
}
break;
case 2:{
//电话接听结果
result = phone_info[1];
if (!result) {
//接听成功
}
else {
//接听失败
}
}
break;
case 3:{
//电话挂断结果
result = phone_info[1];
if (!result) {
//挂断成功
}
else {
//挂断失败
}
}
break;
case 4:{
//电话状态查询结果
result = phone_info[1];
if (result == 0) {
//拨号中
}
else if(result == 1) {
//空闲
}
else if(result == 2) {
//通话失败
}
else if(result == 3) {
//通话中
}
else {
break;
}
}
break;
default:break;
}
}
电话呼出
调用 mcu_api.c
中的 mcu_control_phone_callout()
函数,控制蜂窝模组电话呼出。
获取执行结果的接口在 protocol.c
中 get_contrl_phone_result()
。具体处理结果用户自行实现。
具体代码框架,请参考 电话呼入。
电话接听
调用 mcu_api.c
中的 mcu_control_phone_anwser()
函数,控制蜂窝模组电话接听。
获取执行结果的接口在 protocol.c
中 get_contrl_phone_result()
。具体处理结果用户自行实现。
具体代码框架,请参考 电话呼入。
电话挂断
调用 mcu_api.c
中的 mcu_control_phone_hungup()
函数,控制蜂窝模组电话挂断。
获取执行结果的接口在 protocol.c
中 get_contrl_phone_result()
。具体处理结果用户自行实现。
具体代码框架,请参考 电话呼入。
电话状态查询
调用 mcu_api.c
中的 mcu_get_phone_status()
函数,获取蜂窝模组电话状态。
获取执行结果的接口在 protocol.c
中 get_contrl_phone_result()
。具体处理结果用户自行实现。
具体代码框架,请参考 电话呼入。
如果需要支持门锁功能,请定义 LOCK_SERIVCE_ENABLE
,默认关闭。
调用 mcu_api.c
中的 mcu_get_unix_time_zone()
函数,获取当前 Unix 时区信息。
执行结果可以在 get_unix_time_zone(u8 time[])
函数中获取,具体处理结果用户自行实现。
/**
* @brief 获取到的 Unix 时间
* @param[in] {time} 获取到的 Unix 时间数据
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_unix_time_zone(u8 time[])
{
#error "请自行完成相关代码,并删除该行"
/*
time[0] 为是否获取时间成功标志,0 表示失败,1 表示成功
time[1]-time[4] 为 Unix 时间戳
time[5] 为是否获取时区成功标志,0 表示失败,1 表示成功
time[6] 0 表示东区,1 表示西区
time[7] 为时区
time[8] 是否有夏令时,1 表示有夏令时,0 表示没有夏令时
time[9]-time[12] 进入夏令时时间戳
time[13]-time[16] 退出夏令时时间戳
*/
if(time[0] == 1) {
//正确接收到蜂窝模块返回的 unix 时区数据
}else {
//获取 unix 时间出错
}
}
调用 mcu_api.c
中的 mcu_set_pswd_base(u8 pswd_num,u8 start)
函数,设置密码进制,具体详情可以参考协议文档。
执行结果可以在 get_set_psw_base_result(u8 result)
函数中获取,具体处理结果用户自行实现。
/**
* @brief 获取密码进制服务设置结果
* @param[in] {result} 获取到的设置密码进制服务返回的状态
* @return Null
* @note MCU 需要自行实现该功能
*/
void get_set_psw_base_result(u8 result)
{
#error "请自行完成相关代码,并删除该行"
if(result == 0) {
//设置成功
is_setpswd_base = TRUE;
}else {
//设置失败
is_setpswd_base = FALSE;
}
}
调用 mcu_api.c
中的 mcu_get_schedule_temp_pass()
函数,获取云端临时密码,具体详情可以参考协议文档。
执行结果可以在 get_schedule_temp_pass_handle(const u8 data[])
函数中获取,具体处理结果用户自行实现。
/**
* @brief MCU 请求临时密码(带 schedule 列表)返回
* @param[in] {data} 返回数据
* @return Null
* @note Null
*/
void get_schedule_temp_pass_handle(const u8 data[])
{
u8 i = 0;
u8 pass_len = 0;
u8 result = data[0];
u8 pass_num = data[1];
if(get_setpassword_base_flag()){
pass_len = data[3];
}else{
pass_len = data[2];
}
u8 offset = 4;
if (result == 1) {
for (i=0;i<pass_num;i++) {
schedule_temp_pass_data(result, data[offset], data[offset+1], data[offset+2], data+offset+3,
data+offset+9, data+offset+15, pass_len, data[offset+15+pass_len],data+offset+15+pass_len+1);
offset += 15 + pass_len + 1 + 6*data[offset+15+pass_len];
}
}else {
schedule_temp_pass_data(result, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
}
如果需要支持获取离线动态密码功能,请定义 OFFLINE_DYN_PW_ENABLE
,默认关闭。
调用 mcu_api.c
中的 mcu_set_offline_dynamic_pswd(u8 green_time[],u8 pw[],u8 pw_len)
函数,获取动态离线密码,具体详情可以参考协议文档。
执行结果可以在 get_offline_dynamic_pswd_result(u8 result_data[])
函数中获取,具体处理结果用户自行实现。
/**
* @brief 离线动态密码结果
* @param[in] {result_data} 结果数据
* @return Null
* @note MCU 需要先自行调用 mcu_set_offline_dynamic_pswd 函数后,在此函数对接收的结果进行处理
*/
void get_offline_dynamic_pswd_result(u8 result_data[])
{
#error "请自行完成离线动态密码结果处理代码,并删除该行"
u8 result; //密码正确性
u8 type; //密码类型
u8 decode_len; //解密后数据长度
u8 decode[DECODE_MAX_LEN]; //解密数据
result = result_data[0];
if(0 == result) {
//正确
}else {
//错误
return; //错误时,无后续数据
}
type = result_data[1];
switch(type) {
case 0:
//限时开门密码
break;
case 1:
//单次开门密码
break;
case 2:
//清除密码
break;
default:break;
}
decode_len = result_data[2];
my_memcpy(decode,&result_data[3],decode_len);
//可添加解密数据处理
}
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈