![5fc7db8e2dcd0e40081d43d62987f5fd.png](https://i-blog.csdnimg.cn/blog_migrate/c72572af38db2fe8669b98131e083e8e.jpeg)
这次准备换个硬件来测试mbed-os的蜂窝组件,之前文章用的硬件板载的是EC20,直接用官方cellular组件中现成target配置为ppp就可以跑。
所以为了学习的目的找到手上一款比较适合的硬件来测试,这个板载的是中兴的CDMA模组可以说指令和3GPP基本上很难对上,所以挑战也不小,因为官方的cellular组件是需要模组AT指令兼容3GPP TS 27.007规范才能适配的。
![dbc88b576ef5f046349c2d92e75be348.png](https://i-blog.csdnimg.cn/blog_migrate/d59b2b0352ba773acda4ec5ef4d6e37b.jpeg)
mbed-os中关于AT这部分有两个可选一个是platrom中的ATCmdParser 而另外一个是features中的Cellular组件。如果是比较简单的AT设备可以使用ATCmdParser这个实现相对简单,但是设计者抽象的比较好,非常好用。基本上是设置指令超时时间/发送指令/接收预期并解析/获取结果or超时退出。
Cellular相对是比较复杂的里面代码较多结构更具有层次化,本文暂不做分析。关于蜂窝模组的支持mbed-os做的是比较好的,之前也在用rt-thread中的at_client组件,觉得at_client中关于接收解析处理非常具有技巧性,直接抽象为对line的解析,所以不管指令的应答如何复杂处理起来都不在话下。
![4faa01859cc13309d2c90396f1d4713f.png](https://i-blog.csdnimg.cn/blog_migrate/90e817dd16812e6f0d3dd5713aee441d.png)
mbed-os中关于AT接收应答的处理支持的方式更多,也更灵活,唯一缺点就是没有samplecode只能看头文件中的函数描述,而且这个函数不是很友好,比较费劲。不过这些都不是理由因为他足够好,比如说蜂窝模组入网的状态机非常的科学支持各种尝试机制以及对低功耗的支持退避机制,这里面吸收了大量的LTE蜂窝模组概念比如PSM/eDRX等都有相关的接口用于支持。当然cellular组件支持AT交互使用模组内部的stack来交互数据也能够很好的支持ppp拨号采用外部的lwip协议栈,都是很灵活的直接配置相关的json配置文件即可。
![200e03bd831dda6c3c933a2e7e3ecbaa.png](https://i-blog.csdnimg.cn/blog_migrate/91a68e92871620fbd6ee856565a39f8a.png)
![c779a3642903678594d51e12ab277dd8.png](https://i-blog.csdnimg.cn/blog_migrate/273cfa3dbf7c7fcb20d610fe2ce8d5ca.png)
另外比较吸睛的地方,创建新的target如果你的模组是3GPP指令兼容的那么直接用GENERIC_AT3GPP这个现成的类就行了。如果是不友好的蜂窝模组,比如我使用的MC8618模组,相当不友好折腾了一天才把底层搞好,能够正常运行官方的代码 mbed-os-example-cellular。其次是关于模组特性配置的属性表,我认为设计的比较好,充分体现了mbed设计者的架构能力,这么小的组件设计的不留遗憾。
static const intptr_t cellular_properties[AT_CellularDevice::PROPERTY_MAX] = {
AT_CellularNetwork::RegistrationModeDisable, // C_EREG
AT_CellularNetwork::RegistrationModeDisable, // C_GREG
AT_CellularNetwork::RegistrationModeLAC, // C_REG
0, // AT_CGSN_WITH_TYPE
0, // AT_CGDATA
0, // AT_CGAUTH
1, // AT_CNMI
0, // AT_CSMP
1, // AT_CMGF
1, // AT_CSDH
1, // PROPERTY_IPV4_STACK
0, // PROPERTY_IPV6_STACK
0, // PROPERTY_IPV4V6_STACK
0, // PROPERTY_NON_IP_PDP_TYPE
0, // PROPERTY_AT_CGEREP
0, // PROPERTY_AT_COPS_FALLBACK_AUTO
6, // PROPERTY_SOCKET_COUNT
1, // PROPERTY_IP_TCP
1, // PROPERTY_IP_UDP
25, // PROPERTY_AT_SEND_DELAY
};
一个小细节是关于AT指令发送的,这个和模组设计有关系,mebd中已经对两种做了很好的兼容。比如某些带字符串的指令,有的模组需要双引号有的模组则不能带双引号。看cellular中底层函数关于字符串写入的接口,完美兼容管你是那种垃圾模组都可以。
// AT+ZIPSETUP=0,"www.zhihu.com",80
// AT+ZIPSETUP=0,www.zhihu.com,80
void ATHandler::write_string(const char *param, bool useQuotations)
{
// do common checks before sending subparameter
if (check_cmd_send() == false) {
return;
}
// we are writing string, surround it with quotes
if (useQuotations && write(""", 1) != 1) {
return;
}
(void)write(param, strlen(param));
if (useQuotations) {
// we are writing string, surround it with quotes
(void)write(""", 1);
}
}
关于cellular组件的介绍的可以mbed参考官方文档,限于篇幅也不再展开了。
关于MC8618创建socket的应答处理有些麻烦,在对cellular中相关的api进行测试最终形成一个比较简洁的版本接口供参考。
void ZTE_MC8618_CellularStack::handle_open_socket_response(int &modem_connect_id, int &err)
{
tr_info("ZTE_MC8618_CellularStack:%s:%u: START", __FUNCTION__, __LINE__);
_at.resp_start("OK");
_at.resp_stop();
char resp[64];
int socket_id = -1;
for (uint8_t i=0; i<5; i++) {
_at.set_at_timeout(20000);
_at.resp_start("+");
if (_at.info_resp()) {
resp[0] = '0';
_at.read_string(resp, sizeof(resp));
tr_warn("ZIPSETUP resp [%s]", resp);
if (strstr(resp, "ZTCPESTABLISHED:") != NULL) {
sscanf(resp, "ZTCPESTABLISHED:%drn", &socket_id);
modem_connect_id = socket_id;
tr_info("modem_connect socket_id [%d]n", modem_connect_id);
break;
}
} else {
tr_warn("ZIPSETUP no resp [%d]", i);
}
}
_at.set_at_timeout(1000);
_at.resp_stop();
_at.get_last_error();
err = 0;
tr_info("ZTE_MC8618_CellularStack:%s:%u: END [%d]", __FUNCTION__, __LINE__, err);
}
这条指令属于应答需要等待很久,其次是里面有很多未知的URC通知,实际测试发现有3种以上的URC通知,而且顺序是乱的且URC也不一定会通知。关于创建一个URC处理也是很方便的,接口清晰。
ZTE_MC8618_CellularStack::ZTE_MC8618_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type, AT_CellularDevice &device) :
AT_CellularStack(atHandler, cid, stack_type, device)
{
_at.set_urc_handler("+ZIPRECV:", Callback<void()>(this, &ZTE_MC8618_CellularStack::urc_ziprecv));
_at.set_urc_handler("+ZIND:", Callback<void()>(this, &ZTE_MC8618_CellularStack::urc_zind));
_at.set_urc_handler("+ZTCPCLOSED:0", Callback<void()>(this, &ZTE_MC8618_CellularStack::socket_closed_0));
_at.set_urc_handler("+ZTCPCLOSED:1", Callback<void()>(this, &ZTE_MC8618_CellularStack::socket_closed_1));
}
编译好之后用官方的代码测试一个socket收发,通过这个cellular测试我发现mbed-os的trace能力非常强大而且灵活易用,我记得我一开始接触mbed-os的时候完全不知情,还手动将rt-thread的日志输出的相关代码移植到第一个mbed-os工程上了,有兴趣的可以看之前的文字。
关于trace的相关分析后续会写一些文字,先放官方的代码,效果就是本次测试输出的结果也是带颜色的日志。
static rtos::Mutex trace_mutex;
static void trace_wait()
{
trace_mutex.lock();
}
static void trace_release()
{
trace_mutex.unlock();
}
static char time_st[50];
static char* trace_time(size_t ss)
{
snprintf(time_st, 49, "[%08llums]", Kernel::get_ms_count());
return time_st;
}
static void trace_open()
{
mbed_trace_init();
mbed_trace_prefix_function_set( &trace_time );
mbed_trace_mutex_wait_function_set(trace_wait);
mbed_trace_mutex_release_function_set(trace_release);
mbed_cellular_trace::mutex_wait_function_set(trace_wait);
mbed_cellular_trace::mutex_release_function_set(trace_release);
}
static void trace_close()
{
mbed_cellular_trace::mutex_wait_function_set(NULL);
mbed_cellular_trace::mutex_release_function_set(NULL);
mbed_trace_free();
}
蜂窝组件测试日志输出:
mbed-os-example-cellular
Built: Feb 22 2020, 13:56:45
cellular power up
cellular power done [1]
Establishing connection
trace_open
socket type tcp
[00002000ms][INFO][CELL]: cellular context get default instance.....
[00002000ms][INFO][CELL]: ZTE_MC8618 is the default cellular device instance
[00002000ms][INFO][CELL]: New CellularContext (0x20005d80)
[00002007ms][INFO][CELL]: CellularContext connect
[00002012ms][INFO][CELL]: Start connecting (timeout 1000 ms)
[00003086ms][INFO][CELL]: Modem ready
[00003086ms][INFO][CELL]: Setup SIM (timeout 1000 ms)
[00003237ms][INFO][CELL]: RSSI unknown
[00003268ms][INFO][CELL]: Network registration (timeout 180000 ms)
[00003268ms][INFO][CELL]: Continue after 1 seconds
[00004274ms][INFO][CELL]: RSSI unknown
[00004305ms][INFO][CELL]: Network registration (timeout 180000 ms)
[00004305ms][INFO][CELL]: Continue after 2 seconds
[00006311ms][INFO][CELL]: RSSI -51 dBm
[00006373ms][INFO][CELL]: RSSI -51 dBm
[00006373ms][INFO][CELL]: Attaching network (timeout 60000 ms)
[00006406ms][INFO][CELL]: Network attach
[00015169ms][WARN][CELL]: ZPPPOPEN resp [ZNEWIP:10.187.53.167]
[00015171ms][WARN][CELL]: ZPPPOPEN resp [ZPPPSTATUS: OPENED]
[00016172ms][INFO][CELL]: AT_CellularNetwork:set_attach:327: END [1]
[00016172ms][INFO][CELL]: Continue after 1 seconds
[00017178ms][INFO][CELL]: RSSI -51 dBm
[00017178ms][INFO][CELL]: Attaching network (timeout 60000 ms)
[00017211ms][INFO][CELL]: ZTE_MC8618_CellularStack:socket_stack_init:152: START
[00017308ms][INFO][CELL]: Cellular local IP: 10.187.53.167
Connection Established.
[00017308ms][INFO][CELL]: Socket 0 open
[00017308ms][INFO][CELL]: ZTE_MC8618_CellularStack:socket_connect:250: START [0] PROTO [0]
[00017333ms][INFO][CELL]: ZTE_MC8618_CellularStack:handle_open_socket_response:204: START
[00018233ms][WARN][CELL]: ZIPSETUP resp [ZTCPESTABLISHED:0]
[00018233ms][INFO][CELL]: modem_connect socket_id [0]
[00019234ms][INFO][CELL]: ZTE_MC8618_CellularStack:handle_open_socket_response:232: END [0]
TCP: connected with echo.mbedcloudtesting.com server
[00019234ms][INFO][CELL]: ZTE_MC8618_CellularStack:socket_sendto_impl:389:[5-5]
[00019234ms][WARN][CELL]: socket_sendto_impl socket_id [0]
[00019266ms][WARN][CELL]: response [+ZIPSEND: 5]
[00019266ms][INFO][CELL]: ZTE_MC8618_CellularStack:socket_sendto_impl:427:[ERROR: 0]
[00019266ms][INFO][CELL]: Socket 0 sent 5 bytes to 52.215.34.155 port 7
TCP: Sent 5 Bytes to echo.mbedcloudtesting.com
[00020270ms][INFO][CELL]: ZTE_MC8618_CellularStack:socket_recvfrom_impl:433:[5]
[00021055ms][WARN][CELL]: ZTE_MC8618_CellularStack:urc_ziprecv:112: START
[00022056ms][INFO][CELL]: ZTE_MC8618_CellularStack:socket_recvfrom_impl:453:[5]
[00022056ms][INFO][CELL]: Socket 0 recv 5 bytes
[00022056ms][INFO][CELL]: ZTE_MC8618_CellularStack:socket_close_impl:192:
[00022081ms][INFO][CELL]: Socket 0 closed
Received from echo server 5 Bytes
[00022081ms][INFO][CELL]: CellularContext disconnect()
[00022081ms][INFO][CELL]: cb: CellularContext disconnected
[00022081ms][INFO][CELL]: CellularContext disconnected
Success. Exiting
最后贴上我的mbed_app.json配置其中
{
"config": {
"sock-type" : "TCP",
"cdma_x1" : true,
"echo-server-hostname": {
"help" : "Echo server host name.",
"value" : ""echo.mbedcloudtesting.com""
},
"echo-server-port": {
"help" : "Echo server port number.",
"value" : 7
},
"trace-level": {
"help" : "Options are TRACE_LEVEL_ERROR,TRACE_LEVEL_WARN,TRACE_LEVEL_INFO,TRACE_LEVEL_DEBUG",
"macro_name" : "MBED_TRACE_MAX_LEVEL",
"value" : "TRACE_LEVEL_INFO"
}
},
"target_overrides": {
"*": {
"target.network-default-interface-type" : "CELLULAR",
"mbed-trace.enable" : true,
"lwip.ipv4-enabled" : true,
"ppp.ipv4-enabled" : false,
"lwip.ipv6-enabled" : false,
"ppp.ipv6-enabled" : false,
"lwip.ethernet-enabled" : false,
"lwip.ppp-enabled" : false,
"lwip.tcp-enabled" : true,
"platform.stdio-convert-newlines" : true,
"platform.stdio-baud-rate" : 115200,
"platform.default-serial-baud-rate" : 115200,
"platform.stdio-buffered-serial" : true,
"drivers.uart-serial-rxbuf-size" : 1024,
"spif-driver.SPI_MOSI" : "SPIF_MOSI",
"spif-driver.SPI_MISO" : "SPIF_MISO",
"spif-driver.SPI_CLK" : "SPIF_SCLK",
"spif-driver.SPI_CS" : "SPIF_NESS",
"cellular.debug-at" : false,
"cellular.use-apn-lookup" : false,
"cellular.use-unsupport-3gpp-cdma" : true,
"nsapi.default-cellular-sim-pin" : null,
"nsapi.default-cellular-plmn" : null,
"nsapi.default-cellular-apn" : null,
"nsapi.default-cellular-username" : null,
"nsapi.default-cellular-password" : null,
"ZTE_MC8618.provide-default" : true,
"ZTE_MC8618.tx" : "MC8618_TX",
"ZTE_MC8618.rx" : "MC8618_RX"
}
}
}
其中这个配置比较有意思 "cellular.use-unsupport-3gpp-cdma":true, 是为了修改cellular/framework中的官方代码做了一个宏定义区分是标准3GPP模组还非标模组。
一个例子 AT_CellularNetwork 中关于attach操作的,标准模组CGATT=1即可,你看这个垃圾CDMA要执行这么多。最后说一句手里还有很多这种垃圾板子有需要的可私信,低价处理给感兴趣的人。
nsapi_error_t AT_CellularNetwork::set_attach()
{
_at.lock();
AttachStatus status;
get_attach(status);
if (status == Detached) {
tr_info("Network attach");
#if MBED_CONF_CELLULAR_USE_UNSUPPORT_3GPP_CDMA == 1
_at.at_cmd_discard("+ZPNUM=#777", "");
_at.at_cmd_discard("+ZPIDPWD=card,card", "");
/* AT+ZPPPOPEN */
char resp[64];
_at.set_at_timeout(1000);
_at.cmd_start_stop("+ZPPPOPEN", "");
_at.resp_start("OK");
_at.resp_stop();
for (uint8_t i=0; i<5; i++) {
_at.set_at_timeout(20000);
_at.resp_start("+");
if (_at.info_resp()) {
resp[0] = '0';
_at.read_string(resp, sizeof(resp));
tr_warn("ZPPPOPEN resp [%s]", resp);
if (strstr(resp, "OPENED") != NULL) {
status = Attached;
break;
}
} else {
tr_warn("ZPPPOPEN no resp [%d]", i);
}
}
_at.set_at_timeout(1000);
_at.resp_stop();
tr_info("AT_CellularNetwork:%s:%u: END [%d]", __FUNCTION__, __LINE__, status);
#else
_at.at_cmd_discard("+CGATT", "=1");
#endif
}
return _at.unlock_return_error();
}