【修改】更新 V2.0.0 版本,添加 AT Socket 多客户端支持功能

Signed-off-by: chenyong <1521761801@qq.com>
This commit is contained in:
chenyong 2019-05-27 13:41:17 +08:00
parent 8dc8f39f95
commit 84303edb8a
35 changed files with 10543 additions and 7506 deletions

142
README.md
View File

@ -2,19 +2,22 @@
## 1. 简介 ##
AT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文件和示例代码组成,目前支持的 AT 设备有ESP8266、M26、MC20、EC20、RW007、SIM800C 以及 SIM76XX 系列设备等,目前上述设备都完成对 `AT socket` 功能的移植,及设备通过 AT 命令实现标准 socket 编程接口,完成 socket 通讯的功能,具体功能介绍可参考 [《RT-Thread 编程指南》](https://github.com/RT-Thread/rtthread-manual-doc)AT 命令章节 。
AT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文件和示例代码组成,目前支持的 AT 设备有ESP8266、M26、MC20、RW007、SIM800C 以及 SIM76XX 系列设备等,目前上述设备都完成对 `AT socket` 功能的移植,及设备通过 AT 命令实现标准 socket 编程接口,完成 socket 通讯的功能,具体功能介绍可参考 [《RT-Thread 编程指南》](https://github.com/RT-Thread/rtthread-manual-doc)AT 命令章节 。
### 1.1. 文件结构 ###
### 1.1. 目录结构 ###
| 名称 | 说明 |
| ---- | ---- |
| at_socket_esp8266.c | ESP8266 模块针对 AT 组件的移植文件,实现 AT socket |
| at_socket_rw007.c | RW007 模块针对 AT 组件的移植文件,实现 AT socket |
| at_socket_sim800c.c | SIM800C 模块针对 AT 组件的移植文件,实现 AT socket |
| at_socket_sim76xx.c | SIM76XX 模块针对 AT 组件的移植文件,实现 AT socket |
| at_socket_m26.c | M26/MC20 模块针对 AT 组件的移植文件,实现 AT socket |
| at_socket_ec20.c | EC20 模块针对 AT 组件的移植文件,实现 AT socket |
| at_client_sample.c | ESP8266 模块 AT Client 功能示例文件 |
| src | AT device 实现源码目录 |
| inc | AT device 头文件目录 |
| sample | 不同设备示例文件目录 |
| class | 不同设备针对 AT 组件的移植适配目录 |
| class/esp8266 | ESP8266 设备针对 AT 组件的移植目录,实现 AT Socket 功能 |
| class/rw007 | RW007 设备针对 AT 组件的移植目录,实现 AT Socket 功能 |
| class/sim800c | SIM800C 设备针对 AT 组件的移植目录,实现 AT Socket 功能 |
| class/sim76xx | SIM76XX 设备针对 AT 组件的移植目录,实现 AT Socket 功能 |
| class/m26 | M26/MC20 设备针对 AT 组件的移植目录,实现 AT Socket 功能 |
| class/ec20 | EC20 设备针对 AT 组件的移植目录,实现 AT Socket 功能 |
### 1.2 许可证 ###
@ -29,42 +32,123 @@ at_device package 遵循 LGPLv2.1 许可,详见 `LICENSE` 文件。
## 2. 获取方式 ##
AT device 软件包是对 AT 组件和 AT socket 功能的移植,需开启 AT 组件库和 AT socket 功能来获取 AT device 软件包。
AT device 软件包是对 AT 组件和 AT socket 功能的移植,需开启 AT 组件库和 AT socket 功能来获取 AT device 软件包。
先要开启 AT 组件库和 AT socket 功能, 具体路径如下所示:
**版本号说明**
RT-Thread Components --->
Network stack --->
Socket abstraction layer --->
protocol stack implement --->
[ ] Support lwIP stack
[*] Support AT Commands stack
AT device 软件包目前已经发布多个版本,各个版本之间选项配置方式和其对应的系统版本有所不同,下面主要列出当前可使用的软件包版本信息:
开启 AT socket 功能之后,默认开启 AT device 软件包, 具体路径如下所示:
- **V1.2.0**RT-Thread 版本小于 V3.1.3AT 组件版本等于 V1.0.0
- **V1.3.0**RT-Thread 版本小于 V3.1.3AT 组件版本等于 V1.1.0
- **V1.4.0**RT-Thread 版本小于 V3.1.3或等于 V4.0.0 AT 组件版本等于 V1.2.0
- **V1.5.0**: RT-Thread 版本小于 V3.1.3 或等于 V4.0.0 AT 组件版本等于 V1.2.0
- **V1.6.0**: RT-Thread 版本等于 V3.1.3 或等于 V4.0.1 AT 组件版本等于 V1.2.0
- **laster**: RT-Thread 版本小于大于 V4.0.1 或者 大于 3.1.3 AT 组件版本大于 V1.2.0
> 上述 版本判断在 menuconfig 中自动完成at_device 软件包选择版本时会根据当前系统环境给出最佳版本支持,这里版本介绍作为运行环境参考。
针对不同的版本号,在 ENV 中的选项配置也有所不同,主要分为如下几部分:
**V1.X.X 版本配置选项介绍**
开启 AT device 软件包,该版本只支持**同时开启一种 AT 设备** 配置选项具体如下所示:
RT-Thread online packages --->
IoT - internet of things --->
-*- AT DEVICE: RT-Thread AT component porting or samples for different device
[ ] Enable at device init by thread
AT socket device modules (Not selected, please select) --->
Version (latest) --->
Version (V1.6.0) --->
- `Enable at device init by thread`: 配置开启设备网络初始化是否通过创建线程完成;
- `AT socket device modules`: AT 设备选择,目前支持 RW007、ESP8266、M26/MC20、EC20、SIM800C、SIM76XX 等设备;
- `Version`: 下载软件包版本;
- **V1.2.0**RT-Thread 版本小于 V3.1.3AT 组件版本等于 V1.0.0
- **V1.3.0**RT-Thread 版本小于 V3.1.3AT 组件版本等于 V1.1.0
- **V1.4.0**RT-Thread 版本小于 V3.1.3 或等于 V4.0.0 AT 组件版本等于 V1.2.0
- **V1.5.0**RT-Thread 版本小于 V3.1.3 或等于 V4.0.0 AT 组件版本等于 V1.2.0
- **V1.6.0**RT-Thread 版本等于 V3.1.3 或等于 V4.0.1 AT 组件版本等于 V1.2.0
- **laster**RT-Thread 版本小于大于 V4.0.1 或者 大于 3.1.3 AT 组件版本大于 V1.2.0
- **Enable at device init by thread**: 配置开启设备网络初始化是否通过创建线程完成;
- **AT socket device modules**: AT 设备选择,目前支持 RW007、ESP8266、M26/MC20、EC20、SIM800C、SIM76XX 等设备;
- **Version**: 下载软件包版本;
>上述 版本判断在 menuconfig 中自动完成at_device 软件包选择版本时会根据当前系统环境给出最佳版本支持,这里版本介绍作为运行环境参考。
**V2.X.X (laster) 版本配置选项介绍**
开启 AT device 软件包,该版本支持**同时开启多种 AT 设备**配置选项具体如下所示:
```c
RT-Thread online packages --->
IoT - internet of things --->
-*- AT DEVICE: RT-Thread AT component porting or samples for different device
[*] Quectel M26/MC20 --->
[*] Enable initialize by thread
[*] Enable sample
(-1) Power pin
(-1) Power status pin
(uart3) AT client device name
(512) The maximum length of receive line buffer
[ ] Quectel EC20 --->
[*] Espressif ESP8266 --->
[*] Enable initialize by thread
[*] Enable sample
(realthread) WIFI ssid
(12345678) WIFI password
(uart2) AT client device name
(512) The maximum length of receive line buffer
[ ] Realthread RW007 --->
[ ] SIMCom SIM800C --->
[ ] SIMCom SIM76XX --->
Version (latest) --->
```
- **Quectel M26/MC20**:开启 M20/MC202G 模块)设备支持;
- **Enable initialize by thread**:开启使用线程进行设备初始化功能(非阻塞模式初始化);
- **Enable sample**:开启示例代码,该示例代码中有对示例设备的注册;
- **Power pin**:配置该示例设备上电引脚;
- **Power status pin**:配置该示例设备上电状态引脚;
- **AT client device name**:配置该示例设备使用的串口设备名称;
- **The maximum length of receive line buffer**:配置该示例设备最大一行接收的数据长度;
- **Quectel EC20 **:开启 EC204G 模块)设备支持;
- **Espressif ESP8266**:开启 ESP8266 WIFI 模块)设备支持;
- **Enable initialize by thread**:开启使用线程进行设备初始化功能(非阻塞模式初始化);
- **Enable sample**:开启示例代码,该示例代码中有对示例设备的注册;
- **WIFI ssid**:配置该示例设备连接的 WIFI 用户名;
- **WIFI password**:配置该示例设备连接的 WIFI 密码;
- **AT client device name**:配置该示例设备使用的串口设备名称;
- **The maximum length of receive line buffer**:配置该示例设备最大一行接收的数据长度;
- **Realthread RW007**:开启 RW007 WIFI 模块)设备支持;
- **SIMCom SIM800C**:开启 SIM800C 2G 模块)设备支持;
- **SIMCom SIM76XX**:开启 SIM76XX 4G 模块)设备支持;
- **Version** 下载软件包版本;
上面配置选项以 2G 模块和 WIFI 模块选项为例,介绍了`V2.X.X` 版本 AT device 软件包配置方式,如下几点值得注意:
- `V2.X.X` 版本支持同时开启多个 AT 设备,可以在 FinSH 中通过 `ifocnfig` 命令查看开启的设备信息;
- `V2.X.X` 版本设备需要注册之后才可使用,目前在 samples 目录文件中完成设备注册,用户也可以在应用层自定义设备注册。
- `Power pin``Power status pin` 等引脚选项根据具体设备硬件连接情况配置,如果不使用硬件上电功能,可以配置为 `-1`
- 一个 AT 设备对应一个串口名称,及每个设备配置的`AT client device name` 应该都不相同。
**AT 组件相关配置选项介绍**
选中 AT device 软件包并开启相关设备支持之后,会默认选中 AT 组件的客户端功能,下面是 AT 组件配置选项,
```c
RT-Thread Components --->
Network --->
AT commands --->
[ ] Enable debug log output
[ ] Enable AT commands server
-*- Enable AT commands client
(1) The maximum number of supported clients
-*- Enable BSD Socket API support by AT commnads
[*] Enable CLI(Command-Line Interface) for AT commands
[ ] Enable print RAW format AT command communication data
(128) The maximum lenght of AT Commonds buffe
```
其中和 AT device 软件包相关的配置选项:
- **The maximum number of supported clients**:最大支持的同时开启的 AT 客户端数量AT device 软件包中选中多个设备需要配置该选项为对应数值);
- **Enable BSD Socket API support by AT commnads**:开启 AT Socket 功能支持,选中 AT device 软件包默认选中该选项;
- **The maximum lenght of AT Commonds buffe**:最大支持的发送命令数据的长度。
## 3. 注意事项 ##
- AT device 软件包适配的模块暂时不支持作为 TCP Server 完成服务器相关操作(如 accept 等);
- AT device 软件包默认设备类型为未选择,使用时需要指定使用设备型号;
- `laster` 版本支持多个选中多个 AT 设备接入实现 AT Socket 功能,`V1.X.X` 版本只支持单个 AT 设备接入。
- AT device 软件包目前多个版本主要用于适配 AT 组件和系统的改动,推荐使用最新版本 RT-Thread 系统,并在 menuconfig 选项中选择 `latest` 版本;
## 5. 联系方式

View File

@ -1,30 +1,63 @@
from building import *
cwd = GetCurrentDir()
path = [cwd + '/inc']
src = Glob('src/*.c')
src = []
# M26/MC20
if GetDepend(['AT_DEVICE_USING_M26']):
path += [cwd + '/class/m26']
src += Glob('class/m26/at_device_m26.c')
if GetDepend(['AT_USING_SOCKET']):
src += Glob('class/m26/at_socket_m26.c')
if GetDepend(['AT_DEVICE_M26_SAMPLE']):
src += Glob('samples/at_sample_m26.c')
if GetDepend(['AT_DEVICE_M26']):
src = Glob('at_socket_m26.c')
# EC20
if GetDepend(['AT_DEVICE_USING_EC20']):
path += [cwd + '/class/ec20']
src += Glob('class/ec20/at_device_ec20.c')
if GetDepend(['AT_USING_SOCKET']):
src += Glob('class/ec20/at_socket_ec20.c')
if GetDepend(['AT_DEVICE_EC20_SAMPLE']):
src += Glob('samples/at_sample_ec20.c')
if GetDepend(['AT_DEVICE_EC20']):
src = Glob('at_socket_ec20.c')
# ESP8266
if GetDepend(['AT_DEVICE_USING_ESP8266']):
path += [cwd + '/class/esp8266']
src += Glob('class/esp8266/at_device_esp8266.c')
if GetDepend(['AT_USING_SOCKET']):
src += Glob('class/esp8266/at_socket_esp8266.c')
if GetDepend(['AT_DEVICE_ESP8266_SAMPLE']):
src += Glob('samples/at_sample_esp8266.c')
# RW007
if GetDepend(['AT_DEVICE_USING_RW007']):
path += [cwd + '/class/rw007']
src += Glob('class/rw007/at_device_rw007.c')
if GetDepend(['AT_USING_SOCKET']):
src += Glob('class/rw007/at_socket_rw007.c')
if GetDepend(['AT_DEVICE_RW007_SAMPLE']):
src += Glob('samples/at_sample_rw007.c')
if GetDepend(['AT_DEVICE_ESP8266']):
src = Glob('at_socket_esp8266.c')
# SIM800C
if GetDepend(['AT_DEVICE_USING_SIM800C']):
path += [cwd + '/class/sim800c']
src += Glob('class/sim800c/at_device_sim800c.c')
if GetDepend(['AT_USING_SOCKET']):
src += Glob('class/sim800c/at_socket_sim800c.c')
if GetDepend(['AT_DEVICE_SIM800C_SAMPLE']):
src += Glob('samples/at_sample_sim800c.c')
if GetDepend(['AT_DEVICE_RW007']):
src = Glob('at_socket_rw007.c')
# SIM76XX
if GetDepend(['AT_DEVICE_USING_SIM76XX']):
path += [cwd + '/class/sim76xx']
src += Glob('class/sim76xx/at_device_sim76xx.c')
if GetDepend(['AT_USING_SOCKET']):
src += Glob('class/sim76xx/at_socket_sim76xx.c')
if GetDepend(['AT_DEVICE_SIM76XX_SAMPLE']):
src += Glob('samples/at_sample_sim76xx.c')
if GetDepend(['AT_DEVICE_SIM800C']):
src += Glob('at_socket_sim800c.c')
if GetDepend(['AT_DEVICE_SIM76XX']):
src += Glob('at_socket_sim76xx.c')
if GetDepend(['AT_DEVICE_NOT_SELECTED']):
src = Glob('*.c')
group = DefineGroup('at_device', src, depend = ['PKG_USING_AT_DEVICE','AT_USING_SOCKET'], CPPPATH = [cwd])
group = DefineGroup('at_device', src, depend = ['PKG_USING_AT_DEVICE'], CPPPATH = path)
Return('group')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,773 +0,0 @@
/*
* File : at_socket_rw007.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-20 chenyong first version
*/
#include <stdio.h>
#include <string.h>
#include <rtthread.h>
#include <sys/socket.h>
#include <at.h>
#include <at_socket.h>
#if !defined(RT_USING_NETDEV)
#error "This RT-Thread version is older, please check and updata laster RT-Thread!"
#else
#include <arpa/inet.h>
#include <netdev.h>
#endif /* RT_USING_NETDEV */
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.rw007"
#include <at_log.h>
#ifdef AT_DEVICE_RW007
#define RW007_NETDEV_NAME "rw007"
#define RW007_MODULE_SEND_MAX_SIZE 2048
#define RW007_WAIT_CONNECT_TIME 5000
#define RW007_THREAD_STACK_SIZE 1024
#define RW007_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2)
/* set real event by current socket and current state */
#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event))
/* AT socket event type */
#define RW007_EVENT_CONN_OK (1L << 0)
#define RW007_EVENT_SEND_OK (1L << 1)
#define RW007_EVENT_RECV_OK (1L << 2)
#define RW007_EVNET_CLOSE_OK (1L << 3)
#define RW007_EVENT_CONN_FAIL (1L << 4)
#define RW007_EVENT_SEND_FAIL (1L << 5)
static int cur_socket;
static int cur_send_bfsz;
static rt_event_t at_socket_event;
static rt_mutex_t at_event_lock;
static at_evt_cb_t at_evt_cb_set[] = {
[AT_SOCKET_EVT_RECV] = NULL,
[AT_SOCKET_EVT_CLOSED] = NULL,
};
static int at_socket_event_send(uint32_t event)
{
return (int) rt_event_send(at_socket_event, event);
}
static int at_socket_event_recv(uint32_t event, uint32_t timeout, rt_uint8_t option)
{
int result = 0;
rt_uint32_t recved;
result = rt_event_recv(at_socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved);
if (result != RT_EOK)
{
return -RT_ETIMEOUT;
}
return recved;
}
/**
* close socket by AT commands.
*
* @param current socket
*
* @return 0: close socket success
* -1: send AT commands error
* -2: wait socket event timeout
* -5: no memory
*/
static int rw007_socket_close(int socket)
{
at_response_t resp = RT_NULL;
int result = RT_EOK;
resp = at_create_resp(64, 0, rt_tick_from_millisecond(5000));
if (!resp)
{
LOG_E("No memory for response structure!");
return -RT_ENOMEM;
}
rt_mutex_take(at_event_lock, RT_WAITING_FOREVER);
if (at_exec_cmd(resp, "AT+CIPCLOSE=%d", socket) < 0)
{
result = -RT_ERROR;
goto __exit;
}
__exit:
rt_mutex_release(at_event_lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* create TCP/UDP client or server connect by AT commands.
*
* @param socket current socket
* @param ip server or client IP address
* @param port server or client port
* @param type connect socket type(tcp, udp)
* @param is_client connection is client
*
* @return 0: connect success
* -1: connect failed, send commands error or type error
* -2: wait socket event timeout
* -5: no memory
*/
static int rw007_socket_connect(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client)
{
at_response_t resp = RT_NULL;
int result = RT_EOK;
rt_bool_t retryed = RT_FALSE;
RT_ASSERT(ip);
RT_ASSERT(port >= 0);
resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000));
if (!resp)
{
LOG_E("No memory for response structure!");
return -RT_ENOMEM;
}
rt_mutex_take(at_event_lock, RT_WAITING_FOREVER);
__retry:
if (is_client)
{
switch (type)
{
case AT_SOCKET_TCP:
/* send AT commands to connect TCP server */
if (at_exec_cmd(resp, "AT+CIPSTART=%d,\"TCP\",\"%s\",%d,60", socket, ip, port) < 0)
{
result = -RT_ERROR;
}
break;
case AT_SOCKET_UDP:
if (at_exec_cmd(resp, "AT+CIPSTART=%d,\"UDP\",\"%s\",%d", socket, ip, port) < 0)
{
result = -RT_ERROR;
}
break;
default:
LOG_E("Not supported connect type : %d.", type);
result = -RT_ERROR;
goto __exit;
}
}
if (result != RT_EOK && !retryed)
{
LOG_D("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket);
if (rw007_socket_close(socket) < 0)
{
goto __exit;
}
retryed = RT_TRUE;
result = RT_EOK;
goto __retry;
}
__exit:
rt_mutex_release(at_event_lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* send data to server or client by AT commands.
*
* @param socket current socket
* @param buff send buffer
* @param bfsz send buffer size
* @param type connect socket type(tcp, udp)
*
* @return >=0: the size of send success
* -1: send AT commands error or send data error
* -2: waited socket event timeout
* -5: no memory
*/
static int rw007_socket_send(int socket, const char *buff, size_t bfsz, enum at_socket_type type)
{
int result = RT_EOK;
int event_result = 0;
at_response_t resp = RT_NULL;
size_t cur_pkt_size = 0, sent_size = 0;
RT_ASSERT(buff);
RT_ASSERT(bfsz > 0);
resp = at_create_resp(128, 2, rt_tick_from_millisecond(5000));
if (!resp)
{
LOG_E("No memory for response structure!");
return -RT_ENOMEM;
}
rt_mutex_take(at_event_lock, RT_WAITING_FOREVER);
/* set current socket for send URC event */
cur_socket = socket;
/* set AT client end sign to deal with '>' sign.*/
at_set_end_sign('>');
while (sent_size < bfsz)
{
if (bfsz - sent_size < RW007_MODULE_SEND_MAX_SIZE)
{
cur_pkt_size = bfsz - sent_size;
}
else
{
cur_pkt_size = RW007_MODULE_SEND_MAX_SIZE;
}
/* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */
if (at_exec_cmd(resp, "AT+CIPSEND=%d,%d", socket, cur_pkt_size) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* send the real data to server or client */
result = (int) at_client_send(buff + sent_size, cur_pkt_size);
if (result == 0)
{
result = -RT_ERROR;
goto __exit;
}
/* waiting result event from AT URC */
if (at_socket_event_recv(SET_EVENT(socket, 0), rt_tick_from_millisecond(5 * 1000), RT_EVENT_FLAG_OR) < 0)
{
LOG_E("socket (%d) send failed, wait connect result timeout.", socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
if ((event_result = at_socket_event_recv(RW007_EVENT_SEND_OK | RW007_EVENT_SEND_FAIL, rt_tick_from_millisecond(5 * 1000),
RT_EVENT_FLAG_OR)) < 0)
{
LOG_E("socket (%d) send failed, wait connect OK|FAIL timeout.", socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & RW007_EVENT_SEND_FAIL)
{
LOG_E("socket (%d) send failed, return failed.", socket);
result = -RT_ERROR;
goto __exit;
}
if (type == AT_SOCKET_TCP)
{
cur_pkt_size = cur_send_bfsz;
}
sent_size += cur_pkt_size;
}
__exit:
/* reset the end sign for data */
at_set_end_sign(0);
rt_mutex_release(at_event_lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* domain resolve by AT commands.
*
* @param name domain name
* @param ip parsed IP address, it's length must be 16
*
* @return 0: domain resolve success
* -2: wait socket event timeout
* -5: no memory
*/
static int rw007_domain_resolve(const char *name, char ip[16])
{
#define RESOLVE_RETRY 5
int i, result = RT_EOK;
char recv_ip[16] = { 0 };
at_response_t resp = RT_NULL;
RT_ASSERT(name);
RT_ASSERT(ip);
resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000));
if (!resp)
{
LOG_E("No memory for response structure!");
return -RT_ENOMEM;
}
rt_mutex_take(at_event_lock, RT_WAITING_FOREVER);
for(i = 0; i < RESOLVE_RETRY; i++)
{
if (at_exec_cmd(resp, "AT+CIPDOMAIN=\"%s\"", name) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* parse the third line of response data, get the IP address */
if(at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", recv_ip) < 0)
{
rt_thread_delay(rt_tick_from_millisecond(100));
/* resolve failed, maybe receive an URC CRLF */
continue;
}
if (strlen(recv_ip) < 8)
{
rt_thread_delay(rt_tick_from_millisecond(100));
/* resolve failed, maybe receive an URC CRLF */
continue;
}
else
{
strncpy(ip, recv_ip, 15);
ip[15] = '\0';
break;
}
}
__exit:
rt_mutex_release(at_event_lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* set AT socket event notice callback
*
* @param event notice event
* @param cb notice callback
*/
static void rw007_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb)
{
if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1]))
{
at_evt_cb_set[event] = cb;
}
}
static void urc_send_func(const char *data, rt_size_t size)
{
RT_ASSERT(data && size);
if (strstr(data, "SEND OK"))
{
at_socket_event_send(SET_EVENT(cur_socket, RW007_EVENT_SEND_OK));
}
else if (strstr(data, "SEND FAIL"))
{
at_socket_event_send(SET_EVENT(cur_socket, RW007_EVENT_SEND_FAIL));
}
}
static void urc_send_bfsz_func(const char *data, rt_size_t size)
{
int send_bfsz = 0;
RT_ASSERT(data && size);
sscanf(data, "Recv %d bytes", &send_bfsz);
cur_send_bfsz = send_bfsz;
}
static void urc_close_func(const char *data, rt_size_t size)
{
int socket = 0;
RT_ASSERT(data && size);
sscanf(data, "%d,CLOSED", &socket);
/* notice the socket is disconnect by remote */
if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED])
{
at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0);
}
}
static void urc_recv_func(const char *data, rt_size_t size)
{
int socket = 0;
rt_size_t bfsz = 0, temp_size = 0;
rt_int32_t timeout;
char *recv_buf = RT_NULL, temp[8];
RT_ASSERT(data && size);
/* get the current socket and receive buffer size by receive data */
sscanf(data, "+IPD,%d,%d:", &socket, (int *) &bfsz);
/* get receive timeout by receive buffer length */
timeout = bfsz;
if (socket < 0 || bfsz == 0)
return;
recv_buf = rt_calloc(1, bfsz);
if (!recv_buf)
{
LOG_E("no memory for URC receive buffer (%d)!", bfsz);
/* read and clean the coming data */
while (temp_size < bfsz)
{
if (bfsz - temp_size > sizeof(temp))
{
at_client_recv(temp, sizeof(temp), timeout);
}
else
{
at_client_recv(temp, bfsz - temp_size, timeout);
}
temp_size += sizeof(temp);
}
return;
}
/* sync receive data */
if (at_client_recv(recv_buf, bfsz, timeout) != bfsz)
{
LOG_E("receive size(%d) data failed!", bfsz);
rt_free(recv_buf);
return;
}
/* notice the receive buffer and buffer size */
if (at_evt_cb_set[AT_SOCKET_EVT_RECV])
{
at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz);
}
}
static void urc_busy_p_func(const char *data, rt_size_t size)
{
RT_ASSERT(data && size);
LOG_D("system is processing a commands and it cannot respond to the current commands.");
}
static void urc_busy_s_func(const char *data, rt_size_t size)
{
RT_ASSERT(data && size);
LOG_D("system is sending data and it cannot respond to the current commands.");
}
static void urc_func(const char *data, rt_size_t size)
{
RT_ASSERT(data && size);
if(strstr(data, "WIFI CONNECTED"))
{
LOG_I("RW007 WIFI is connected.");
}
else if(strstr(data, "WIFI DISCONNECT"))
{
LOG_I("RW007 WIFI is disconnect.");
}
}
static struct at_urc urc_table[] = {
{"SEND OK", "\r\n", urc_send_func},
{"SEND FAIL", "\r\n", urc_send_func},
{"Recv", "bytes\r\n", urc_send_bfsz_func},
{"", ",CLOSED\r\n", urc_close_func},
{"+IPD", ":", urc_recv_func},
{"busy p", "\r\n", urc_busy_p_func},
{"busy s", "\r\n", urc_busy_s_func},
{"WIFI CONNECTED", "\r\n", urc_func},
{"WIFI DISCONNECT", "\r\n", urc_func},
};
#define AT_SEND_CMD(resp, cmd) \
do \
{ \
if (at_exec_cmd(at_resp_set_info(resp, 256, 0, rt_tick_from_millisecond(5000)), cmd) < 0) \
{ \
LOG_E("RT AT send commands(%s) error!", cmd); \
result = -RT_ERROR; \
goto __exit; \
} \
} while(0); \
static void rw007_init_thread_entry(void *parameter)
{
at_response_t resp = RT_NULL;
rt_err_t result = RT_EOK;
rt_size_t i;
resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000));
if (!resp)
{
LOG_E("No memory for response structure!");
result = -RT_ENOMEM;
goto __exit;
}
rt_thread_delay(rt_tick_from_millisecond(5000));
/* reset module */
AT_SEND_CMD(resp, "AT+RST");
/* reset waiting delay */
rt_thread_delay(rt_tick_from_millisecond(1000));
/* disable echo */
AT_SEND_CMD(resp, "ATE0");
/* set current mode to Wi-Fi station */
AT_SEND_CMD(resp, "AT+CWMODE=1");
/* get module version */
AT_SEND_CMD(resp, "AT+GMR");
/* show module version */
for (i = 0; i < resp->line_counts - 1; i++)
{
LOG_D("%s", at_resp_get_line(resp, i + 1));
}
/* connect to WiFi AP */
if (at_exec_cmd(at_resp_set_info(resp, 128, 0, 20 * RT_TICK_PER_SECOND), "AT+CWJAP=\"%s\",\"%s\"",
AT_DEVICE_WIFI_SSID, AT_DEVICE_WIFI_PASSWORD) != RT_EOK)
{
LOG_E("AT network initialize failed, check ssid(%s) and password(%s).", AT_DEVICE_WIFI_SSID, AT_DEVICE_WIFI_PASSWORD);
result = -RT_ERROR;
goto __exit;
}
AT_SEND_CMD(resp, "AT+CIPMUX=1");
__exit:
if (resp)
{
at_delete_resp(resp);
}
if (!result)
{
LOG_I("AT network initialize success!");
}
else
{
LOG_E("AT network initialize failed (%d)!", result);
}
}
int rw007_net_init(void)
{
#ifdef PKG_AT_INIT_BY_THREAD
rt_thread_t tid;
tid = rt_thread_create("rw007_net_init", rw007_init_thread_entry, RT_NULL,RW007_THREAD_STACK_SIZE, RW007_THREAD_PRIORITY, 20);
if (tid)
{
rt_thread_startup(tid);
}
else
{
LOG_E("Create AT initialization thread fail!");
}
#else
rw007_init_thread_entry(RT_NULL);
#endif
return RT_EOK;
}
int rw007_ping(int argc, char **argv)
{
at_response_t resp = RT_NULL;
static int icmp_seq;
int req_time;
if (argc != 2)
{
rt_kprintf("Please input: at_ping <host address>\n");
return -RT_ERROR;
}
resp = at_create_resp(64, 0, rt_tick_from_millisecond(5000));
if (!resp)
{
rt_kprintf("No memory for response structure!\n");
return -RT_ENOMEM;
}
for(icmp_seq = 1; icmp_seq <= 4; icmp_seq++)
{
if (at_exec_cmd(resp, "AT+PING=\"%s\"", argv[1]) < 0)
{
rt_kprintf("ping: unknown remote server host\n");
at_delete_resp(resp);
return -RT_ERROR;
}
if(at_resp_parse_line_args_by_kw(resp, "+", "+%d", &req_time) < 0)
{
continue;
}
if (req_time)
{
rt_kprintf("32 bytes from %s icmp_seq=%d time=%d ms\n", argv[1], icmp_seq, req_time);
}
}
if (resp)
{
at_delete_resp(resp);
}
return RT_EOK;
}
#ifdef FINSH_USING_MSH
#include <finsh.h>
MSH_CMD_EXPORT_ALIAS(rw007_net_init, at_net_init, initialize AT network);
MSH_CMD_EXPORT_ALIAS(rw007_ping, at_ping, AT ping network host);
#endif
static const struct at_device_ops rw007_socket_ops = {
rw007_socket_connect,
rw007_socket_close,
rw007_socket_send,
rw007_domain_resolve,
rw007_socket_set_event_cb,
};
static int rw007_netdev_add(const char *netdev_name)
{
#define ETHERNET_MTU 1500
#define HWADDR_LEN 8
struct netdev *netdev = RT_NULL;
int result = 0;
RT_ASSERT(netdev_name);
netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev));
if (netdev == RT_NULL)
{
return RT_NULL;
}
/* TODO: improve netdev adaptation */
netdev->mtu = ETHERNET_MTU;
netdev->hwaddr_len = HWADDR_LEN;
netdev->ops = RT_NULL;
#ifdef SAL_USING_AT
extern int sal_at_netdev_set_pf_info(struct netdev *netdev);
/* set the network interface socket/netdb operations */
sal_at_netdev_set_pf_info(netdev);
#endif
result = netdev_register(netdev, netdev_name, RT_NULL);
/*TODO: improve netdev adaptation */
netdev_low_level_set_status(netdev, RT_TRUE);
netdev_low_level_set_link_status(netdev, RT_TRUE);
netdev_low_level_set_dhcp_status(netdev, RT_TRUE);
netdev->flags |= NETDEV_FLAG_INTERNET_UP;
return result;
}
static int at_socket_device_init(void)
{
/* create current AT socket event */
at_socket_event = rt_event_create("at_se", RT_IPC_FLAG_FIFO);
if (at_socket_event == RT_NULL)
{
LOG_E("RT AT client port initialize failed! at_sock_event create failed!");
return -RT_ENOMEM;
}
/* create current AT socket event lock */
at_event_lock = rt_mutex_create("at_se", RT_IPC_FLAG_FIFO);
if (at_event_lock == RT_NULL)
{
LOG_E("RT AT client port initialize failed! at_sock_lock create failed!");
rt_event_delete(at_socket_event);
return -RT_ENOMEM;
}
/* initialize AT client */
at_client_init(AT_DEVICE_NAME, AT_DEVICE_RECV_BUFF_LEN);
/* register URC data execution function */
at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
/* add network interface device to netdev list */
if (rw007_netdev_add(RW007_NETDEV_NAME) < 0)
{
LOG_E("RW007 network interface device(%d) add failed.", RW007_NETDEV_NAME);
return -RT_ENOMEM;
}
/* initialize rw007 network */
rw007_net_init();
/* set rw007 AT Socket options */
at_socket_device_register(&rw007_socket_ops);
return RT_EOK;
}
INIT_APP_EXPORT(at_socket_device_init);
#endif /* AT_DEVICE_RW007 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1040
class/ec20/at_device_ec20.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
/*
* File : at_device_ec20.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-16 chenyong first version
*/
#ifndef __AT_DEVICE_EC20_H__
#define __AT_DEVICE_EC20_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <at_device.h>
/* The maximum number of sockets supported by the ec20 device */
#define AT_DEVICE_EC20_SOCKETS_NUM 5
struct at_device_ec20
{
char *device_name;
char *client_name;
int power_pin;
int power_status_pin;
size_t recv_line_num;
struct at_device device;
void *socket_data;
void *user_data;
};
#ifdef AT_USING_SOCKET
/* ec20 device socket initialize */
int ec20_socket_init(struct at_device *device);
/* ec20 device class socket register */
int ec20_socket_class_register(struct at_device_class *class);
#endif /* AT_USING_SOCKET */
#ifdef __cplusplus
}
#endif
#endif /* __AT_DEVICE_EC20_H__ */

1007
class/ec20/at_socket_ec20.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,929 @@
/*
* File : at_socket_esp8266.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-20 chenyong first version
* 2019-05-09 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_esp8266.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.dev"
#include <at_log.h>
#ifdef AT_DEVICE_USING_ESP8266
#define ESP8266_WAIT_CONNECT_TIME 5000
#define ESP8266_THREAD_STACK_SIZE 1024
#define ESP8266_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 2)
/* ============================= esp8266 network interface operations ============================= */
static void esp8266_get_netdev_info(struct rt_work *work, void *work_data)
{
#define AT_ADDR_LEN 32
at_response_t resp = RT_NULL;
char ip[AT_ADDR_LEN] = {0}, mac[AT_ADDR_LEN] = {0};
char gateway[AT_ADDR_LEN] = {0}, netmask[AT_ADDR_LEN] = {0};
char dns_server1[AT_ADDR_LEN] = {0}, dns_server2[AT_ADDR_LEN] = {0};
const char *resp_expr = "%*[^\"]\"%[^\"]\"";
const char *resp_dns = "+CIPDNS_CUR:%s";
ip_addr_t ip_addr;
rt_uint32_t mac_addr[6] = {0};
rt_uint32_t num = 0;
rt_uint8_t dhcp_stat = 0;
struct rt_delayed_work *delay_work = (struct rt_delayed_work *)work;
struct at_device *device = (struct at_device *)work_data;
struct netdev *netdev = device->netdev;
struct at_client *client = device->client;
if (delay_work)
{
rt_free(delay_work);
}
resp = at_create_resp(512, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%d) response structure.", device->name);
return;
}
/* send mac addr query commond "AT+CIFSR" and wait response */
if (at_obj_exec_cmd(client, resp, "AT+CIFSR") < 0)
{
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "STAMAC", resp_expr, mac) <= 0)
{
LOG_E("esp8266 device(%s) parse \"AT+CIFSR\" command response data error.", device->name);
goto __exit;
}
/* send addr info query commond "AT+CIPSTA?" and wait response */
if (at_obj_exec_cmd(client, resp, "AT+CIPSTA?") < 0)
{
LOG_E("esp8266 device(%s) send \"AT+CIPSTA?\" commands error.", device->name);
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "ip", resp_expr, ip) <= 0 ||
at_resp_parse_line_args_by_kw(resp, "gateway", resp_expr, gateway) <= 0 ||
at_resp_parse_line_args_by_kw(resp, "netmask", resp_expr, netmask) <= 0)
{
LOG_E("esp8266 device(%s) prase \"AT+CIPSTA?\" command resposne data error.", device->name);
goto __exit;
}
/* set netdev info */
inet_aton(ip, &ip_addr);
netdev_low_level_set_ipaddr(netdev, &ip_addr);
inet_aton(gateway, &ip_addr);
netdev_low_level_set_gw(netdev, &ip_addr);
inet_aton(netmask, &ip_addr);
netdev_low_level_set_netmask(netdev, &ip_addr);
sscanf(mac, "%x:%x:%x:%x:%x:%x",
&mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]);
for (num = 0; num < netdev->hwaddr_len; num++)
{
netdev->hwaddr[num] = mac_addr[num];
}
/* send dns server query commond "AT+CIPDNS_CUR?" and wait response */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPDNS_CUR?") < 0)
{
LOG_W("please check and update device(%s) firmware to support the \"AT+CIPDNS_CUR?\" command.", device->name);
goto __exit;
}
if (at_resp_parse_line_args(resp, 1, resp_dns, dns_server1) <= 0 &&
at_resp_parse_line_args(resp, 2, resp_dns, dns_server2) <= 0)
{
LOG_E("esp8266 device(%d) prase \"AT+CIPDNS_CUR?\" commands resposne data error.", device->name);
goto __exit;
}
if (rt_strlen(dns_server1) > 0)
{
inet_aton(dns_server1, &ip_addr);
netdev_low_level_set_dns_server(netdev, 0, &ip_addr);
}
if (rt_strlen(dns_server2) > 0)
{
inet_aton(dns_server2, &ip_addr);
netdev_low_level_set_dns_server(netdev, 1, &ip_addr);
}
/* send DHCP query commond " AT+CWDHCP_CUR?" and wait response */
if (at_obj_exec_cmd(client, resp, "AT+CWDHCP_CUR?") < 0)
{
goto __exit;
}
/* parse response data, get the DHCP status */
if (at_resp_parse_line_args_by_kw(resp, "+CWDHCP_CUR:", "+CWDHCP_CUR:%d", &dhcp_stat) < 0)
{
LOG_E("esp8266 device(%s) get DHCP status failed.", device->name);
goto __exit;
}
/* Bit0 - SoftAP DHCP status, Bit1 - Station DHCP status */
netdev_low_level_set_dhcp_status(netdev, dhcp_stat & 0x02 ? RT_TRUE : RT_FALSE);
__exit:
if (resp)
{
at_delete_resp(resp);
}
}
static int esp8266_net_init(struct at_device *device);
static int esp8266_netdev_set_up(struct netdev *netdev)
{
struct at_device *device = RT_NULL;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
if (device->is_init == RT_FALSE)
{
esp8266_net_init(device);
netdev_low_level_set_status(netdev, RT_TRUE);
LOG_D("the network interface device(%s) set up status", netdev->name);
}
return RT_EOK;
}
static int esp8266_netdev_set_down(struct netdev *netdev)
{
struct at_device *device = RT_NULL;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
if (device->is_init == RT_TRUE)
{
device->is_init = RT_FALSE;
netdev_low_level_set_status(netdev, RT_FALSE);
LOG_D("the network interface device(%s) set down status", netdev->name);
}
return RT_EOK;
}
static int esp8266_netdev_set_addr_info(struct netdev *netdev, ip_addr_t *ip_addr, ip_addr_t *netmask, ip_addr_t *gw)
{
#define IPADDR_RESP_SIZE 128
#define IPADDR_SIZE 16
int result = RT_EOK;
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
char esp8266_ip_addr[IPADDR_SIZE] = {0};
char esp8266_gw_addr[IPADDR_SIZE] = {0};
char esp8266_netmask_addr[IPADDR_SIZE] = {0};
RT_ASSERT(netdev);
RT_ASSERT(ip_addr || netmask || gw);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
resp = at_create_resp(IPADDR_RESP_SIZE, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
result = -RT_ENOMEM;
goto __exit;
}
/* Convert numeric IP address into decimal dotted ASCII representation. */
if (ip_addr)
rt_memcpy(esp8266_ip_addr, inet_ntoa(*ip_addr), IPADDR_SIZE);
else
rt_memcpy(esp8266_ip_addr, inet_ntoa(netdev->ip_addr), IPADDR_SIZE);
if (gw)
rt_memcpy(esp8266_gw_addr, inet_ntoa(*gw), IPADDR_SIZE);
else
rt_memcpy(esp8266_gw_addr, inet_ntoa(netdev->gw), IPADDR_SIZE);
if (netmask)
rt_memcpy(esp8266_netmask_addr, inet_ntoa(*netmask), IPADDR_SIZE);
else
rt_memcpy(esp8266_netmask_addr, inet_ntoa(netdev->netmask), IPADDR_SIZE);
/* send addr info set commond "AT+CIPSTA_CUR=<ip>[,<gateway>,<netmask>]" and wait response */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPSTA_CUR=\"%s\",\"%s\",\"%s\"",
esp8266_ip_addr, esp8266_gw_addr, esp8266_netmask_addr) < 0)
{
LOG_E("esp8266 device(%s) set address information failed.", device->name);
result = -RT_ERROR;
}
else
{
/* Update netdev information */
if (ip_addr)
netdev_low_level_set_ipaddr(netdev, ip_addr);
if (gw)
netdev_low_level_set_gw(netdev, gw);
if (netmask)
netdev_low_level_set_netmask(netdev, netmask);
LOG_D("esp8266 device(%s) set address information successfully.", device->name);
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static int esp8266_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server)
{
#define DNS_RESP_SIZE 128
int result = RT_EOK;
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(netdev);
RT_ASSERT(dns_server);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
resp = at_create_resp(DNS_RESP_SIZE, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
/* send dns server set commond "AT+CIPDNS_CUR=<enable>[,<DNS server0>,<DNS server1>]" and wait response */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPDNS_CUR=1,\"%s\"", inet_ntoa(*dns_server)) < 0)
{
LOG_E("esp8266 device(%s) set DNS server(%s) failed.", device->name, inet_ntoa(*dns_server));
result = -RT_ERROR;
}
else
{
netdev_low_level_set_dns_server(netdev, dns_num, dns_server);
LOG_D("esp8266 device(%s) set DNS server(%s) successfully.", device->name, inet_ntoa(*dns_server));
}
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static int esp8266_netdev_set_dhcp(struct netdev *netdev, rt_bool_t is_enabled)
{
#define ESP8266_STATION 1
#define RESP_SIZE 128
int result = RT_EOK;
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(netdev);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get AT device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
resp = at_create_resp(RESP_SIZE, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.",device->name);
return -RT_ENOMEM;
}
/* send dhcp set commond "AT+CWDHCP_CUR=<mode>,<en>" and wait response */
if (at_obj_exec_cmd(device->client, resp, "AT+CWDHCP_CUR=%d,%d", ESP8266_STATION, is_enabled) < 0)
{
LOG_E("esp8266 device(%s) set DHCP status(%d) failed.", device->name, is_enabled);
result = -RT_ERROR;
goto __exit;
}
else
{
netdev_low_level_set_dhcp_status(netdev, is_enabled);
LOG_D("esp8266 device(%d) set DHCP status(%d) successfully.", device->name, is_enabled);
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
#ifdef NETDEV_USING_PING
static int esp8266_netdev_ping(struct netdev *netdev, const char *host,
size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp)
{
#define ESP8266_PING_IP_SIZE 16
rt_err_t result = RT_EOK;
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
char ip_addr[ESP8266_PING_IP_SIZE] = {0};
int req_time;
RT_ASSERT(netdev);
RT_ASSERT(host);
RT_ASSERT(ping_resp);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
resp = at_create_resp(64, 0, timeout);
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
/* send domain commond "AT+CIPDOMAIN=<domain name>" and wait response */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPDOMAIN=\"%s\"", host) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* parse the third line of response data, get the IP address */
if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", ip_addr) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* send ping commond "AT+PING=<IP>" and wait response */
if (at_obj_exec_cmd(device->client, resp, "AT+PING=\"%s\"", host) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "+", "+%d", &req_time) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (req_time)
{
inet_aton(ip_addr, &(ping_resp->ip_addr));
ping_resp->data_len = data_len;
ping_resp->ttl = 0;
ping_resp->ticks = req_time;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
#endif /* NETDEV_USING_PING */
#ifdef NETDEV_USING_NETSTAT
void esp8266_netdev_netstat(struct netdev *netdev)
{
#define ESP8266_NETSTAT_RESP_SIZE 320
#define ESP8266_NETSTAT_TYPE_SIZE 4
#define ESP8266_NETSTAT_IPADDR_SIZE 17
#define ESP8266_NETSTAT_EXPRESSION "+CIPSTATUS:%*d,\"%[^\"]\",\"%[^\"]\",%d,%d,%*d"
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
int remote_port, local_port, i;
char *type = RT_NULL;
char *ipaddr = RT_NULL;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name);
return;
}
type = (char *) rt_calloc(1, ESP8266_NETSTAT_TYPE_SIZE);
ipaddr = (char *) rt_calloc(1, ESP8266_NETSTAT_IPADDR_SIZE);
if ((type && ipaddr) == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
goto __exit;
}
resp = at_create_resp(ESP8266_NETSTAT_RESP_SIZE, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
goto __exit;
}
/* send network connection information commond "AT+CIPSTATUS" and wait response */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPSTATUS") < 0)
{
goto __exit;
}
for (i = 1; i <= resp->line_counts; i++)
{
if (strstr(at_resp_get_line(resp, i), "+CIPSTATUS"))
{
/* parse the third line of response data, get the network connection information */
if (at_resp_parse_line_args(resp, i, ESP8266_NETSTAT_EXPRESSION, type, ipaddr, &remote_port, &local_port) < 0)
{
goto __exit;
}
else
{
LOG_RAW("%s: %s:%d ==> %s:%d\n", type, inet_ntoa(netdev->ip_addr), local_port, ipaddr, remote_port);
}
}
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
if (type)
{
rt_free(type);
}
if (ipaddr)
{
rt_free(ipaddr);
}
}
#endif /* NETDEV_USING_NETSTAT */
static const struct netdev_ops esp8266_netdev_ops =
{
esp8266_netdev_set_up,
esp8266_netdev_set_down,
esp8266_netdev_set_addr_info,
esp8266_netdev_set_dns_server,
esp8266_netdev_set_dhcp,
#ifdef NETDEV_USING_PING
esp8266_netdev_ping,
#endif
#ifdef NETDEV_USING_NETSTAT
esp8266_netdev_netstat,
#endif
};
static struct netdev *esp8266_netdev_add(const char *netdev_name)
{
#define ETHERNET_MTU 1500
#define HWADDR_LEN 6
struct netdev *netdev = RT_NULL;
RT_ASSERT(netdev_name);
netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev));
if (netdev == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) netdev structure.", netdev_name);
return RT_NULL;
}
netdev->mtu = ETHERNET_MTU;
netdev->ops = &esp8266_netdev_ops;
netdev->hwaddr_len = HWADDR_LEN;
#ifdef SAL_USING_AT
extern int sal_at_netdev_set_pf_info(struct netdev *netdev);
/* set the network interface socket/netdb operations */
sal_at_netdev_set_pf_info(netdev);
#endif
netdev_register(netdev, netdev_name, RT_NULL);
return netdev;
}
/* ============================= esp8266 device operations ============================= */
#define AT_SEND_CMD(client, resp, cmd) \
do { \
(resp) = at_resp_set_info((resp), 256, 0, 5 * RT_TICK_PER_SECOND); \
if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \
{ \
result = -RT_ERROR; \
goto __exit; \
} \
} while(0) \
static void esp8266_netdev_start_delay_work(struct at_device *device)
{
struct rt_delayed_work *net_work = RT_NULL;
net_work = (struct rt_delayed_work *)rt_calloc(1, sizeof(struct rt_delayed_work));
if (net_work == RT_NULL)
{
return;
}
rt_delayed_work_init(net_work, esp8266_get_netdev_info, (void *)device);
rt_work_submit(&(net_work->work), RT_TICK_PER_SECOND);
}
static void esp8266_init_thread_entry(void *parameter)
{
#define INIT_RETRY 5
struct at_device *device = (struct at_device *) parameter;
struct at_device_esp8266 *esp8266 = (struct at_device_esp8266 *) device->user_data;
struct at_client *client = device->client;
at_response_t resp = RT_NULL;
rt_err_t result = RT_EOK;
rt_size_t i = 0, retry_num = INIT_RETRY;
LOG_D("esp8266 device(%s) initialize start.", device->name);
/* wait esp8266 device startup finish */
if (at_client_obj_wait_connect(client, ESP8266_WAIT_CONNECT_TIME))
{
return;
}
resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%d) response structure.", device->name);
return;
}
while (retry_num--)
{
/* reset module */
AT_SEND_CMD(client, resp, "AT+RST");
/* reset waiting delay */
rt_thread_mdelay(1000);
/* disable echo */
AT_SEND_CMD(client, resp, "ATE0");
/* set current mode to Wi-Fi station */
AT_SEND_CMD(client, resp, "AT+CWMODE=1");
/* get module version */
AT_SEND_CMD(client, resp, "AT+GMR");
/* show module version */
for (i = 0; i < resp->line_counts - 1; i++)
{
LOG_D("%s", at_resp_get_line(resp, i + 1));
}
AT_SEND_CMD(client, resp, "AT+CIPMUX=1");
/* connect to WiFi AP */
if (at_obj_exec_cmd(client, at_resp_set_info(resp, 128, 0, 20 * RT_TICK_PER_SECOND),
"AT+CWJAP=\"%s\",\"%s\"", esp8266->wifi_ssid, esp8266->wifi_password) != RT_EOK)
{
LOG_E("AT device(%s) network initialize failed, check ssid(%s) and password(%s).",
device->name, esp8266->wifi_ssid, esp8266->wifi_password);
result = -RT_ERROR;
goto __exit;
}
__exit:
if (result == RT_EOK)
{
break;
}
else
{
rt_thread_mdelay(1000);
LOG_I("esp8266 device(%s) initialize retry...", device->name);
}
}
if (resp)
{
at_delete_resp(resp);
}
if (result != RT_EOK)
{
netdev_low_level_set_status(device->netdev, RT_FALSE);
LOG_E("esp8266 device(%s) network initialize failed(%d).", device->name, result);
}
else
{
device->is_init = RT_TRUE;
netdev_low_level_set_status(device->netdev, RT_TRUE);
netdev_low_level_set_link_status(device->netdev, RT_TRUE);
esp8266_netdev_start_delay_work(device);
LOG_I("esp8266 device(%s) network initialize successfully.", device->name);
}
}
static int esp8266_net_init(struct at_device *device)
{
#ifdef AT_DEVICE_ESP8266_INIT_ASYN
rt_thread_t tid;
tid = rt_thread_create("esp8266_net_init", esp8266_init_thread_entry, (void *) device,
ESP8266_THREAD_STACK_SIZE, ESP8266_THREAD_PRIORITY, 20);
if (tid)
{
rt_thread_startup(tid);
}
else
{
LOG_E("create esp8266 device(%s) initialize thread failed.", device->name);
return -RT_ERROR;
}
#else
esp8266_init_thread_entry(device);
#endif /* AT_DEVICE_ESP8266_INIT_ASYN */
return RT_EOK;
}
static void urc_busy_p_func(struct at_client *client, const char *data, rt_size_t size)
{
LOG_D("system is processing a commands and it cannot respond to the current commands.");
}
static void urc_busy_s_func(struct at_client *client, const char *data, rt_size_t size)
{
LOG_D("system is sending data and it cannot respond to the current commands.");
}
static void urc_func(struct at_client *client, const char *data, rt_size_t size)
{
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(client && data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by client name(%s) failed.", client_name);
return;
}
if (rt_strstr(data, "WIFI CONNECTED"))
{
LOG_I("esp8266 device(%s) WIFI is connected.", device->name);
if (device->is_init)
{
netdev_low_level_set_link_status(device->netdev, RT_TRUE);
esp8266_netdev_start_delay_work(device);
}
}
else if (rt_strstr(data, "WIFI DISCONNECT"))
{
LOG_I("esp8266 device(%s) WIFI is disconnect.", device->name);
if (device->is_init)
{
netdev_low_level_set_link_status(device->netdev, RT_FALSE);
}
}
}
static const struct at_urc urc_table[] =
{
{"busy p", "\r\n", urc_busy_p_func},
{"busy s", "\r\n", urc_busy_s_func},
{"WIFI CONNECTED", "\r\n", urc_func},
{"WIFI DISCONNECT", "\r\n", urc_func},
};
static int esp8266_init(struct at_device *device)
{
struct at_device_esp8266 *esp8266 = (struct at_device_esp8266 *) device->user_data;
/* initialize AT client */
at_client_init(esp8266->client_name, esp8266->recv_line_num);
device->client = at_client_get(esp8266->client_name);
if (device->client == RT_NULL)
{
LOG_E("esp8266 device(%s) initialize failed, get AT client(%s) failed.",
esp8266->device_name, esp8266->client_name);
return -RT_ERROR;
}
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
#ifdef AT_USING_SOCKET
esp8266_socket_init(device);
#endif
/* add esp8266 device to the netdev list */
device->netdev = esp8266_netdev_add(esp8266->device_name);
if (device->netdev == RT_NULL)
{
LOG_E("esp8266 device(%s) initialize failed, get network interface device failed.", esp8266->device_name);
return -RT_ERROR;
}
/* initialize esp8266 device network */
return esp8266_netdev_set_up(device->netdev);
}
static int esp8266_deinit(struct at_device *device)
{
return esp8266_netdev_set_down(device->netdev);
}
/* reset eap8266 device and initialize device network again */
static int esp8266_reset(struct at_device *device)
{
int result = RT_EOK;
struct at_client *client = device->client;
/* send "AT+RST" commonds to esp8266 device */
result = at_obj_exec_cmd(client, RT_NULL, "AT+RST");
rt_thread_mdelay(1000);
/* waiting 10 seconds for esp8266 device reset */
device->is_init = RT_FALSE;
if (at_client_obj_wait_connect(client, ESP8266_WAIT_CONNECT_TIME))
{
return -RT_ETIMEOUT;
}
/* initialize esp8266 device network */
esp8266_net_init(device);
device->is_init = RT_TRUE;
return result;
}
/* change eap8266 wifi ssid and password information */
static int esp8266_wifi_info_set(struct at_device *device, struct at_device_ssid_pwd *info)
{
int result = RT_EOK;
struct at_response *resp = RT_NULL;
if (info->ssid == RT_NULL || info->password == RT_NULL)
{
LOG_E("input esp8266 wifi ssid(%s) and password(%s) error.", info->ssid, info->password);
return -RT_ERROR;
}
resp = at_create_resp(128, 0, 20 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
/* connect to input wifi ap */
if (at_obj_exec_cmd(device->client, resp, "AT+CWJAP=\"%s\",\"%s\"", info->ssid, info->password) != RT_EOK)
{
LOG_E("esp8266 device(%s) wifi connect failed, check ssid(%s) and password(%s).",
device->name, info->ssid, info->password);
result = -RT_ERROR;
}
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static int esp8266_control(struct at_device *device, int cmd, void *arg)
{
int result = -RT_ERROR;
RT_ASSERT(device);
switch (cmd)
{
case AT_DEVICE_CTRL_POWER_ON:
case AT_DEVICE_CTRL_POWER_OFF:
case AT_DEVICE_CTRL_LOW_POWER:
case AT_DEVICE_CTRL_SLEEP:
case AT_DEVICE_CTRL_WAKEUP:
case AT_DEVICE_CTRL_NET_CONN:
case AT_DEVICE_CTRL_NET_DISCONN:
case AT_DEVICE_CTRL_GET_SIGNAL:
case AT_DEVICE_CTRL_GET_GPS:
case AT_DEVICE_CTRL_GET_VER:
LOG_W("esp8266 not support the control command(%d).", cmd);
break;
case AT_DEVICE_CTRL_RESET:
result = esp8266_reset(device);
break;
case AT_DEVICE_CTRL_SET_WIFI_INFO:
result = esp8266_wifi_info_set(device, (struct at_device_ssid_pwd *) arg);
break;
default:
LOG_E("input error control command(%d).", cmd);
break;
}
return result;
}
static const struct at_device_ops esp8266_device_ops =
{
esp8266_init,
esp8266_deinit,
esp8266_control,
};
static int esp8266_device_class_register(void)
{
struct at_device_class *class = RT_NULL;
class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class));
if (class == RT_NULL)
{
LOG_E("no memory for esp8266 device class create.");
return -RT_ENOMEM;
}
/* fill ESP8266 device class object */
#ifdef AT_USING_SOCKET
esp8266_socket_class_register(class);
#endif
class->device_ops = &esp8266_device_ops;
return at_device_class_register(class, AT_DEVICE_CLASS_ESP8266);
}
INIT_DEVICE_EXPORT(esp8266_device_class_register);
#endif /* AT_DEVICE_USING_ESP8266 */

View File

@ -0,0 +1,66 @@
/*
* File : at_device_esp8266.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-16 chenyong first version
*/
#ifndef __AT_DEVICE_ESP8266_H__
#define __AT_DEVICE_ESP8266_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <at_device.h>
/* The maximum number of sockets supported by the esp8266 device */
#define AT_DEVICE_ESP8266_SOCKETS_NUM 5
struct at_device_esp8266
{
char *device_name;
char *client_name;
char *wifi_ssid;
char *wifi_password;
size_t recv_line_num;
struct at_device device;
void *user_data;
};
#ifdef AT_USING_SOCKET
/* esp8266 device socket initialize */
int esp8266_socket_init(struct at_device *device);
/* esp8266 device class socket register */
int esp8266_socket_class_register(struct at_device_class *class);
#endif /* AT_USING_SOCKET */
#ifdef __cplusplus
}
#endif
#endif /* __AT_DEVICE_ESP8266_H__ */

View File

@ -0,0 +1,559 @@
/*
* File : at_socket_esp8266.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-20 chenyong first version
* 2019-05-09 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_esp8266.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.skt"
#include <at_log.h>
#if defined(AT_DEVICE_USING_ESP8266) && defined(AT_USING_SOCKET)
#define ESP8266_MODULE_SEND_MAX_SIZE 2048
/* set real event by current socket and current state */
#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event))
/* AT socket event type */
#define ESP8266_EVENT_CONN_OK (1L << 0)
#define ESP8266_EVENT_SEND_OK (1L << 1)
#define ESP8266_EVENT_RECV_OK (1L << 2)
#define ESP8266_EVNET_CLOSE_OK (1L << 3)
#define ESP8266_EVENT_CONN_FAIL (1L << 4)
#define ESP8266_EVENT_SEND_FAIL (1L << 5)
static at_evt_cb_t at_evt_cb_set[] = {
[AT_SOCKET_EVT_RECV] = NULL,
[AT_SOCKET_EVT_CLOSED] = NULL,
};
static int esp8266_socket_event_send(struct at_device *device, uint32_t event)
{
return (int) rt_event_send(device->socket_event, event);
}
static int esp8266_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option)
{
int result = 0;
rt_uint32_t recved;
result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved);
if (result != RT_EOK)
{
return -RT_ETIMEOUT;
}
return recved;
}
/**
* close socket by AT commands.
*
* @param current socket
*
* @return 0: close socket success
* -1: send AT commands error
* -2: wait socket event timeout
* -5: no memory
*/
static int esp8266_socket_close(struct at_socket *socket)
{
int result = RT_EOK;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
resp = at_create_resp(64, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
result = at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE=%d", device_socket);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* create TCP/UDP client or server connect by AT commands.
*
* @param socket current socket
* @param ip server or client IP address
* @param port server or client port
* @param type connect socket type(tcp, udp)
* @param is_client connection is client
*
* @return 0: connect success
* -1: connect failed, send commands error or type error
* -2: wait socket event timeout
* -5: no memory
*/
static int esp8266_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client)
{
int result = RT_EOK;
rt_bool_t retryed = RT_FALSE;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
RT_ASSERT(ip);
RT_ASSERT(port >= 0);
resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
__retry:
if (is_client)
{
switch (type)
{
case AT_SOCKET_TCP:
/* send AT commands to connect TCP server */
if (at_obj_exec_cmd(device->client, resp,
"AT+CIPSTART=%d,\"TCP\",\"%s\",%d,60", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
}
break;
case AT_SOCKET_UDP:
if (at_obj_exec_cmd(device->client, resp,
"AT+CIPSTART=%d,\"UDP\",\"%s\",%d", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
}
break;
default:
LOG_E("esp8266 device(%s) not supported connect type %d.", device->name, type);
result = -RT_ERROR;
goto __exit;
}
}
if (result != RT_EOK && retryed == RT_FALSE)
{
LOG_D("esp8266 device(%s) socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.",
device->name, device_socket);
if (esp8266_socket_close(socket) < 0)
{
goto __exit;
}
retryed = RT_TRUE;
result = RT_EOK;
goto __retry;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* send data to server or client by AT commands.
*
* @param socket current socket
* @param buff send buffer
* @param bfsz send buffer size
* @param type connect socket type(tcp, udp)
*
* @return >=0: the size of send success
* -1: send AT commands error or send data error
* -2: waited socket event timeout
* -5: no memory
*/
static int esp8266_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type)
{
int result = RT_EOK;
int event_result = 0;
size_t cur_pkt_size = 0, sent_size = 0;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
struct at_device_esp8266 *esp8266 = (struct at_device_esp8266 *) device->user_data;
rt_mutex_t lock = device->client->lock;
RT_ASSERT(buff);
RT_ASSERT(bfsz > 0);
resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
rt_mutex_take(lock, RT_WAITING_FOREVER);
/* set current socket for send URC event */
esp8266->user_data = (void *) device_socket;
/* set AT client end sign to deal with '>' sign */
at_obj_set_end_sign(device->client, '>');
while (sent_size < bfsz)
{
if (bfsz - sent_size < ESP8266_MODULE_SEND_MAX_SIZE)
{
cur_pkt_size = bfsz - sent_size;
}
else
{
cur_pkt_size = ESP8266_MODULE_SEND_MAX_SIZE;
}
/* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d", device_socket, cur_pkt_size) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* send the real data to server or client */
result = (int) at_client_obj_send(device->client, buff + sent_size, cur_pkt_size);
if (result == 0)
{
result = -RT_ERROR;
goto __exit;
}
/* waiting result event from AT URC */
if (esp8266_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0)
{
LOG_E("esp8266 device(%s) socket(%d) send failed, wait connect result timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
event_result = esp8266_socket_event_recv(device, ESP8266_EVENT_SEND_OK | ESP8266_EVENT_SEND_FAIL,
5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR);
if (event_result < 0)
{
LOG_E("esp8266 device(%s) socket(%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & ESP8266_EVENT_SEND_FAIL)
{
LOG_E("esp8266 device(%s) socket(%d) send failed.", device->name, device_socket);
result = -RT_ERROR;
goto __exit;
}
sent_size += cur_pkt_size;
}
__exit:
/* reset the end sign for data */
at_obj_set_end_sign(device->client, 0);
rt_mutex_release(lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* domain resolve by AT commands.
*
* @param name domain name
* @param ip parsed IP address, it's length must be 16
*
* @return 0: domain resolve success
* -2: wait socket event timeout
* -5: no memory
*/
static int esp8266_domain_resolve(const char *name, char ip[16])
{
#define RESOLVE_RETRY 5
int i, result = RT_EOK;
char recv_ip[16] = { 0 };
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(name);
RT_ASSERT(ip);
device = at_device_get_first_initialized();
if (device == RT_NULL)
{
LOG_E("get first initialization esp8266 device failed.");
return -RT_ERROR;
}
resp = at_create_resp(128, 0, 20 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
for (i = 0; i < RESOLVE_RETRY; i++)
{
if (at_obj_exec_cmd(device->client, resp, "AT+CIPDOMAIN=\"%s\"", name) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* parse the third line of response data, get the IP address */
if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", recv_ip) < 0)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
if (rt_strlen(recv_ip) < 8)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
else
{
rt_strncpy(ip, recv_ip, 15);
ip[15] = '\0';
break;
}
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* set AT socket event notice callback
*
* @param event notice event
* @param cb notice callback
*/
static void esp8266_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb)
{
if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1]))
{
at_evt_cb_set[event] = cb;
}
}
static const struct at_socket_ops esp8266_socket_ops =
{
esp8266_socket_connect,
esp8266_socket_close,
esp8266_socket_send,
esp8266_domain_resolve,
esp8266_socket_set_event_cb,
};
static void urc_send_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_device *device = RT_NULL;
struct at_device_esp8266 *esp8266 = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by client name(%s) failed.", client_name);
return;
}
esp8266 = (struct at_device_esp8266 *) device->user_data;
device_socket = (int) esp8266->user_data;
if (rt_strstr(data, "SEND OK"))
{
esp8266_socket_event_send(device, SET_EVENT(device_socket, ESP8266_EVENT_SEND_OK));
}
else if (rt_strstr(data, "SEND FAIL"))
{
esp8266_socket_event_send(device, SET_EVENT(device_socket, ESP8266_EVENT_SEND_FAIL));
}
}
static void urc_send_bfsz_func(struct at_client *client, const char *data, rt_size_t size)
{
static int cur_send_bfsz = 0;
RT_ASSERT(data && size);
sscanf(data, "Recv %d bytes", &cur_send_bfsz);
}
static void urc_close_func(struct at_client *client, const char *data, rt_size_t size)
{
int index = 0;
struct at_socket *socket = RT_NULL;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by client name(%s) failed.", client_name);
return;
}
sscanf(data, "%d,CLOSED", &index);
socket = &(device->sockets[index]);
/* notice the socket is disconnect by remote */
if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED])
{
at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0);
}
}
static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
rt_int32_t timeout = 0;
rt_size_t bfsz = 0, temp_size = 0;
char *recv_buf = RT_NULL, temp[8] = {0};
struct at_socket *socket = RT_NULL;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get esp8266 device by client name(%s) failed.", client_name);
return;
}
/* get the at deveice socket and receive buffer size by receive data */
sscanf(data, "+IPD,%d,%d:", &device_socket, (int *) &bfsz);
/* get receive timeout by receive buffer length */
timeout = bfsz;
if (device_socket < 0 || bfsz == 0)
return;
recv_buf = (char *) rt_calloc(1, bfsz);
if (recv_buf == RT_NULL)
{
LOG_E("no memory for esp8266 device(%s) URC receive buffer(%d).", device->name, bfsz);
/* read and clean the coming data */
while (temp_size < bfsz)
{
if (bfsz - temp_size > sizeof(temp))
{
at_client_obj_recv(client, temp, sizeof(temp), timeout);
}
else
{
at_client_obj_recv(client, temp, bfsz - temp_size, timeout);
}
temp_size += sizeof(temp);
}
return;
}
/* sync receive data */
if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz)
{
LOG_E("esp8266 device(%s) receive size(%d) data failed.", device->name, bfsz);
rt_free(recv_buf);
return;
}
/* get at socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the receive buffer and buffer size */
if (at_evt_cb_set[AT_SOCKET_EVT_RECV])
{
at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz);
}
}
static const struct at_urc urc_table[] =
{
{"SEND OK", "\r\n", urc_send_func},
{"SEND FAIL", "\r\n", urc_send_func},
{"Recv", "bytes\r\n", urc_send_bfsz_func},
{"", ",CLOSED\r\n", urc_close_func},
{"+IPD", ":", urc_recv_func},
};
int esp8266_socket_init(struct at_device *device)
{
RT_ASSERT(device);
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
return RT_EOK;
}
int esp8266_socket_class_register(struct at_device_class *class)
{
RT_ASSERT(class);
class->socket_num = AT_DEVICE_ESP8266_SOCKETS_NUM;
class->socket_ops = &esp8266_socket_ops;
return RT_EOK;
}
#endif /* AT_DEVICE_USING_ESP8266 && AT_USING_SOCKET */

881
class/m26/at_device_m26.c Normal file
View File

@ -0,0 +1,881 @@
/*
* File : at_device_m26.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-12 chenyong first version
* 2019-05-12 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_m26.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.dev"
#include <at_log.h>
#ifdef AT_DEVICE_USING_M26
#define M26_WAIT_CONNECT_TIME 5000
#define M26_THREAD_STACK_SIZE 1024
#define M26_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2)
static void m26_power_on(struct at_device *device)
{
struct at_device_m26 *m26 = RT_NULL;
m26 = (struct at_device_m26 *) device->user_data;
/* not nead to set pin configuration for m26 device power on */
if (m26->power_pin == -1 || m26->power_status_pin == -1)
{
return;
}
if (rt_pin_read(m26->power_status_pin) == PIN_HIGH)
{
return;
}
rt_pin_write(m26->power_pin, PIN_HIGH);
while (rt_pin_read(m26->power_status_pin) == PIN_LOW)
{
rt_thread_mdelay(10);
}
rt_pin_write(m26->power_pin, PIN_LOW);
}
static void m26_power_off(struct at_device *device)
{
struct at_device_m26 *m26 = RT_NULL;
m26 = (struct at_device_m26 *) device->user_data;
/* not nead to set pin configuration for m26 device power on */
if (m26->power_pin == -1 || m26->power_status_pin == -1)
{
return;
}
if (rt_pin_read(m26->power_status_pin) == PIN_LOW)
{
return;
}
rt_pin_write(m26->power_pin, PIN_HIGH);
while (rt_pin_read(m26->power_status_pin) == PIN_HIGH)
{
rt_thread_mdelay(10);
}
rt_pin_write(m26->power_pin, PIN_LOW);
}
/* ============================= m26 network interface operations ============================= */
/* set m26 network interface device status and address information */
static int m26_netdev_set_info(struct netdev *netdev)
{
#define M26_IEMI_RESP_SIZE 32
#define M26_IPADDR_RESP_SIZE 32
#define M26_DNS_RESP_SIZE 96
#define M26_INFO_RESP_TIMO rt_tick_from_millisecond(300)
int result = RT_EOK;
ip_addr_t addr;
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
struct at_client *client = RT_NULL;
if (netdev == RT_NULL)
{
LOG_E("input network interface device is NULL.");
return -RT_ERROR;
}
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get m26 deivce by netdev name failed.", netdev->name);
return -RT_ERROR;
}
client = device->client;
/* set network interface device up status */
netdev_low_level_set_status(netdev, RT_TRUE);
netdev_low_level_set_dhcp_status(netdev, RT_TRUE);
resp = at_create_resp(M26_IEMI_RESP_SIZE, 0, M26_INFO_RESP_TIMO);
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response structure.", netdev->name);
result = -RT_ENOMEM;
goto __exit;
}
/* set network interface device hardware address(IEMI) */
{
#define M26_NETDEV_HWADDR_LEN 8
#define M26_IEMI_LEN 15
char iemi[M26_IEMI_LEN] = {0};
int i = 0, j = 0;
/* send "AT+GSN" commond to get device IEMI */
if (at_obj_exec_cmd(client, resp, "AT+GSN") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args(resp, 2, "%s", iemi) <= 0)
{
LOG_E("m26 device(%s) prase \"AT+GSN\" commands resposne data error.", device->name);
result = -RT_ERROR;
goto __exit;
}
LOG_D("m26 device(%s) IEMI number: %s", device->name, iemi);
netdev->hwaddr_len = M26_NETDEV_HWADDR_LEN;
/* get hardware address by IEMI */
for (i = 0, j = 0; i < M26_NETDEV_HWADDR_LEN && j < M26_IEMI_LEN; i++, j+=2)
{
if (j != M26_IEMI_LEN - 1)
{
netdev->hwaddr[i] = (iemi[j] - '0') * 10 + (iemi[j + 1] - '0');
}
else
{
netdev->hwaddr[i] = (iemi[j] - '0');
}
}
}
/* set network interface device IP address */
{
#define IP_ADDR_SIZE_MAX 16
char ipaddr[IP_ADDR_SIZE_MAX] = {0};
at_resp_set_info(resp, M26_IPADDR_RESP_SIZE, 2, M26_INFO_RESP_TIMO);
/* send "AT+QILOCIP" commond to get IP address */
if (at_obj_exec_cmd(client, resp, "AT+QILOCIP") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, ".", "%s", ipaddr) <= 0)
{
LOG_E("m26 device(%s) prase \"AT+QILOCIP\" commands resposne data error.", device->name);
result = -RT_ERROR;
goto __exit;
}
LOG_D("m26 device(%s) IP address: %s", device->name, ipaddr);
/* set network interface address information */
inet_aton(ipaddr, &addr);
netdev_low_level_set_ipaddr(netdev, &addr);
}
/* set network interface device dns server */
{
#define DNS_ADDR_SIZE_MAX 16
char dns_server1[DNS_ADDR_SIZE_MAX] = {0}, dns_server2[DNS_ADDR_SIZE_MAX] = {0};
at_resp_set_info(resp, M26_DNS_RESP_SIZE, 0, M26_INFO_RESP_TIMO);
/* send "AT+QIDNSCFG?" commond to get DNS servers address */
if (at_obj_exec_cmd(client, resp, "AT+QIDNSCFG?") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "PrimaryDns:", "PrimaryDns:%s", dns_server1) <= 0 ||
at_resp_parse_line_args_by_kw(resp, "SecondaryDns:", "SecondaryDns:%s", dns_server2) <= 0)
{
LOG_E("Prase \"AT+QIDNSCFG?\" commands resposne data error!");
result = -RT_ERROR;
goto __exit;
}
LOG_D("m26 device(%s) primary DNS server address: %s", device->name, dns_server1);
LOG_D("m26 device(%s) secondary DNS server address: %s", device->name, dns_server2);
inet_aton(dns_server1, &addr);
netdev_low_level_set_dns_server(netdev, 0, &addr);
inet_aton(dns_server2, &addr);
netdev_low_level_set_dns_server(netdev, 1, &addr);
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static void check_link_status_entry(void *parameter)
{
#define M26_LINK_STATUS_OK 0
#define M26_LINK_RESP_SIZE 64
#define M26_LINK_RESP_TIMO (3 * RT_TICK_PER_SECOND)
#define M26_LINK_DELAY_TIME (30 * RT_TICK_PER_SECOND)
struct netdev *netdev = (struct netdev *)parameter;
struct at_device *device = RT_NULL;
at_response_t resp = RT_NULL;
int link_status;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get m26 deivce by netdev name failed.", netdev->name);
return;
}
resp = at_create_resp(M26_LINK_RESP_SIZE, 0, M26_LINK_RESP_TIMO);
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response object.", netdev->name);
return;
}
while (1)
{
/* send "AT+QNSTATUS" commond to check netweork interface device link status */
if (at_obj_exec_cmd(device->client, resp, "AT+QNSTATUS") < 0)
{
rt_thread_mdelay(M26_LINK_DELAY_TIME);
continue;
}
link_status = -1;
at_resp_parse_line_args_by_kw(resp, "+QNSTATUS:", "+QNSTATUS: %d", &link_status);
/* check the network interface device link status */
if ((M26_LINK_STATUS_OK == link_status) != netdev_is_link_up(netdev))
{
netdev_low_level_set_link_status(netdev, (M26_LINK_STATUS_OK == link_status));
}
rt_thread_mdelay(M26_LINK_DELAY_TIME);
}
}
static int m26_netdev_check_link_status(struct netdev *netdev)
{
#define M26_LINK_THREAD_TICK 20
#define M26_LINK_THREAD_STACK_SIZE 512
#define M26_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2)
rt_thread_t tid;
char tname[RT_NAME_MAX] = {0};
if (netdev == RT_NULL)
{
LOG_E("input network interface device is NULL.");
return -RT_ERROR;
}
rt_snprintf(tname, RT_NAME_MAX, "%s_link", netdev->name);
tid = rt_thread_create(tname, check_link_status_entry, (void *)netdev,
M26_LINK_THREAD_STACK_SIZE, M26_LINK_THREAD_PRIORITY, M26_LINK_THREAD_TICK);
if (tid)
{
rt_thread_startup(tid);
}
return RT_EOK;
}
static int m26_net_init(struct at_device *device);
static int m26_netdev_set_up(struct netdev *netdev)
{
struct at_device *device = RT_NULL;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get m26 device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
if (device->is_init == RT_FALSE)
{
m26_net_init(device);
device->is_init = RT_TRUE;
netdev_low_level_set_status(netdev, RT_TRUE);
LOG_D("the network interface device(%s) set up status.", netdev->name);
}
return RT_EOK;
}
static int m26_netdev_set_down(struct netdev *netdev)
{
struct at_device *device = RT_NULL;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get m26 device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
if (device->is_init == RT_TRUE)
{
m26_power_off(device);
device->is_init = RT_FALSE;
netdev_low_level_set_status(netdev, RT_FALSE);
LOG_D("the network interface device(%s) set down status.", netdev->name);
}
return RT_EOK;
}
static int m26_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server)
{
#define M26_DNS_RESP_LEN 8
#define M26_DNS_RESP_TIMEO rt_tick_from_millisecond(300)
int result = RT_EOK;
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(netdev);
RT_ASSERT(dns_server);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get m26 deivce by netdev name failed.", netdev->name);
return - RT_ERROR;
}
resp = at_create_resp(M26_DNS_RESP_LEN, 0, M26_DNS_RESP_TIMEO);
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response object.", netdev->name);
return -RT_ENOMEM;
}
/* send "AT+QIDNSCFG=<pri_dns>[,<sec_dns>]" commond to set dns servers */
if (at_exec_cmd(resp, "AT+QIDNSCFG=\"%s\"", inet_ntoa(*dns_server)) < 0)
{
result = -RT_ERROR;
goto __exit;
}
netdev_low_level_set_dns_server(netdev, dns_num, dns_server);
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
#ifdef NETDEV_USING_PING
static int m26_netdev_ping(struct netdev *netdev, const char *host,
size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp)
{
#define M26_PING_RESP_SIZE 128
#define M26_PING_IP_SIZE 16
#define M26_PING_TIMEO (5 * RT_TICK_PER_SECOND)
int result = RT_EOK;
at_response_t resp = RT_NULL;
char ip_addr[M26_PING_IP_SIZE] = {0};
int response, recv_data_len, time, ttl;
struct at_device *device = RT_NULL;
RT_ASSERT(netdev);
RT_ASSERT(host);
RT_ASSERT(ping_resp);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get m26 deivce by netdev name failed.", netdev->name);
return - RT_ERROR;
}
resp = at_create_resp(M26_PING_RESP_SIZE, 5, M26_PING_TIMEO);
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response object.", netdev->name);
return -RT_ENOMEM;
}
/* send "AT+QPING="<host>"[,[<timeout>][,<pingnum>]]" commond to send ping request */
if (at_obj_exec_cmd(device->client, resp, "AT+QPING=\"%s\",%d,1", host, M26_PING_TIMEO / RT_TICK_PER_SECOND) < 0)
{
result = -RT_ERROR;
goto __exit;
}
at_resp_parse_line_args_by_kw(resp, "+QPING:","+QPING:%d", &response);
/* Received the ping response from the server */
if (response == 0)
{
if (at_resp_parse_line_args_by_kw(resp, "+QPING:", "+QPING:%d,%[^,],%d,%d,%d",
&response, ip_addr, &recv_data_len, &time, &ttl) <= 0)
{
result = -RT_ERROR;
goto __exit;
}
}
/* prase response number */
switch (response)
{
case 0:
inet_aton(ip_addr, &(ping_resp->ip_addr));
ping_resp->data_len = recv_data_len;
ping_resp->ticks = time;
ping_resp->ttl = ttl;
result = RT_EOK;
break;
case 1:
result = -RT_ETIMEOUT;
break;
default:
result = -RT_ERROR;
break;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
#endif /* NETDEV_USING_PING */
#ifdef NETDEV_USING_NETSTAT
static void m26_netdev_netstat(struct netdev *netdev)
{
// TODO netstat support
}
#endif /* NETDEV_USING_NETSTAT */
const struct netdev_ops m26_netdev_ops =
{
m26_netdev_set_up,
m26_netdev_set_down,
RT_NULL, /* not support set ip, netmask, gatway address */
m26_netdev_set_dns_server,
RT_NULL, /* not support set DHCP status */
#ifdef NETDEV_USING_PING
m26_netdev_ping,
#endif
#ifdef NETDEV_USING_NETSTAT
m26_netdev_netstat,
#endif
};
static struct netdev *m26_netdev_add(const char *netdev_name)
{
#define M26_NETDEV_MTU 1500
struct netdev *netdev = RT_NULL;
RT_ASSERT(netdev_name);
netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev));
if (netdev == RT_NULL)
{
LOG_E("no memory for m26 device(%s) netdev structure.", netdev_name);
return RT_NULL;
}
netdev->mtu = M26_NETDEV_MTU;
netdev->ops = &m26_netdev_ops;
#ifdef SAL_USING_AT
extern int sal_at_netdev_set_pf_info(struct netdev *netdev);
/* set the network interface socket/netdb operations */
sal_at_netdev_set_pf_info(netdev);
#endif
netdev_register(netdev, netdev_name, RT_NULL);
return netdev;
}
/* ============================= m26 device operations ============================= */
#define AT_SEND_CMD(client, resp, resp_line, timeout, cmd) \
do { \
(resp) = at_resp_set_info((resp), 128, (resp_line), rt_tick_from_millisecond(timeout)); \
if (at_obj_exec_cmd((client),(resp), (cmd)) < 0) \
{ \
result = -RT_ERROR; \
goto __exit; \
} \
} while(0); \
/* init for m26 or mc20 */
static void m26_init_thread_entry(void *parameter)
{
#define INIT_RETRY 5
#define CPIN_RETRY 10
#define CSQ_RETRY 10
#define CREG_RETRY 10
#define CGREG_RETRY 20
at_response_t resp = RT_NULL;
int i, qimux, qimode;
int retry_num = INIT_RETRY;
char parsed_data[10];
rt_err_t result = RT_EOK;
struct at_device *device = (struct at_device *)parameter;
struct at_client *client = device->client;
resp = at_create_resp(128, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response structure.", device->name);
return;
}
LOG_D("start initializing the m26/mc20 device(%s)", device->name);
while (retry_num--)
{
/* power on the m26 device */
m26_power_on(device);
rt_thread_mdelay(1000);
/* wait m26|mc20 startup finish */
if (at_client_obj_wait_connect(client, M26_WAIT_CONNECT_TIME))
{
result = -RT_ETIMEOUT;
goto __exit;
}
/* disable echo */
AT_SEND_CMD(client, resp, 0, 300, "ATE0");
/* get module version */
AT_SEND_CMD(client, resp, 0, 300, "ATI");
/* show module version */
for (i = 0; i < (int) resp->line_counts - 1; i++)
{
LOG_D("%s", at_resp_get_line(resp, i + 1));
}
/* check SIM card */
for (i = 0; i < CPIN_RETRY; i++)
{
AT_SEND_CMD(client, resp, 2, 5 * RT_TICK_PER_SECOND, "AT+CPIN?");
if (at_resp_get_line_by_kw(resp, "READY"))
{
LOG_D("m26 device(%s) SIM card detection success.", device->name);
break;
}
rt_thread_mdelay(1000);
}
if (i == CPIN_RETRY)
{
LOG_E("SIM card detection failed!");
result = -RT_ERROR;
goto __exit;
}
/* waiting for dirty data to be digested */
rt_thread_mdelay(10);
/* check signal strength */
for (i = 0; i < CSQ_RETRY; i++)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+CSQ");
at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %s", &parsed_data);
if (rt_strncmp(parsed_data, "99,99", sizeof(parsed_data)))
{
LOG_D("m26 device(%s) signal strength: %s", device->name, parsed_data);
break;
}
rt_thread_mdelay(1000);
}
if (i == CSQ_RETRY)
{
LOG_E("m26 device(%s) signal strength check failed(%s).", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
/* check the GSM network is registered */
for (i = 0; i < CREG_RETRY; i++)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+CREG?");
at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data);
if (!rt_strncmp(parsed_data, "0,1", sizeof(parsed_data)) ||
!rt_strncmp(parsed_data, "0,5", sizeof(parsed_data)))
{
LOG_D("m26 device(%s) GSM network is registered(%s).", device->name, parsed_data);
break;
}
rt_thread_mdelay(1000);
}
if (i == CREG_RETRY)
{
LOG_E("m26 device(%s) GSM network is register failed(%s)", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
/* check the GPRS network is registered */
for (i = 0; i < CGREG_RETRY; i++)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+CGREG?");
at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data);
if (!rt_strncmp(parsed_data, "0,1", sizeof(parsed_data)) ||
!rt_strncmp(parsed_data, "0,5", sizeof(parsed_data)))
{
LOG_D("m26 device(%s) GPRS network is registered(%s).", device->name, parsed_data);
break;
}
rt_thread_mdelay(1000);
}
if (i == CGREG_RETRY)
{
LOG_E("m26 device(%s) GPRS network is register failed(%s).", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
AT_SEND_CMD(client, resp, 0, 300, "AT+QIFGCNT=0");
AT_SEND_CMD(client, resp, 0, 300, "AT+QICSGP=1, \"CMNET\"");
AT_SEND_CMD(client, resp, 0, 300, "AT+QIMODE?");
at_resp_parse_line_args_by_kw(resp, "+QIMODE:", "+QIMODE: %d", &qimode);
if (qimode == 1)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+QIMODE=0");
}
/* the device default response timeout is 40 seconds, but it set to 15 seconds is convenient to use. */
AT_SEND_CMD(client, resp, 2, 20 * 1000, "AT+QIDEACT");
/* Set to multiple connections */
AT_SEND_CMD(client, resp, 0, 300, "AT+QIMUX?");
at_resp_parse_line_args_by_kw(resp, "+QIMUX:", "+QIMUX: %d", &qimux);
if (qimux == 0)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+QIMUX=1");
}
AT_SEND_CMD(client, resp, 0, 300, "AT+QIREGAPP");
/* the device default response timeout is 150 seconds, but it set to 20 seconds is convenient to use. */
AT_SEND_CMD(client, resp, 0, 20 * 1000, "AT+QIACT");
AT_SEND_CMD(client, resp, 2, 300, "AT+QILOCIP");
result = RT_EOK;
__exit:
if (result == RT_EOK)
{
break;
}
else
{
/* power off the m26 device */
m26_power_off(device);
rt_thread_mdelay(1000);
LOG_I("m26 device(%s) initialize retry...", device->name);
}
}
if (resp)
{
at_delete_resp(resp);
}
if (result == RT_EOK)
{
m26_netdev_set_info(device->netdev);
m26_netdev_check_link_status(device->netdev);
LOG_I("m26 device(%s) network initialize successfully.", device->name);
}
else
{
LOG_E("m26 device(%s) network initialize failed(%d).", device->name, result);
}
}
static int m26_net_init(struct at_device *device)
{
#ifdef AT_DEVICE_M26_INIT_ASYN
rt_thread_t tid;
tid = rt_thread_create("m26_net_init", m26_init_thread_entry, (void *)device,
M26_THREAD_STACK_SIZE, M26_THREAD_PRIORITY, 20);
if (tid)
{
rt_thread_startup(tid);
}
else
{
LOG_E("create m26 device(%s) initialization thread failed.", device->name);
return -RT_ERROR;
}
#else
m26_init_thread_entry(device);
#endif /* AT_DEVICE_M26_INIT_ASYN */
return RT_EOK;
}
static void urc_func(struct at_client *client, const char *data, rt_size_t size)
{
RT_ASSERT(data);
LOG_I("URC data : %.*s", size, data);
}
static const struct at_urc urc_table[] = {
{"RING", "\r\n", urc_func},
{"Call Ready", "\r\n", urc_func},
{"RDY", "\r\n", urc_func},
{"NO CARRIER", "\r\n", urc_func},
};
static int m26_init(struct at_device *device)
{
struct at_device_m26 *m26 = (struct at_device_m26 *) device->user_data;
/* initialize AT client */
at_client_init(m26->client_name, m26->recv_line_num);
device->client = at_client_get(m26->client_name);
if (device->client == RT_NULL)
{
LOG_E("m26 device(%s) initialize failed, get AT client(%s) failed.", m26->device_name, m26->client_name);
return -RT_ERROR;
}
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
#ifdef AT_USING_SOCKET
m26_socket_init(device);
#endif
/* add m26 netdev to the netdev list */
device->netdev = m26_netdev_add(m26->device_name);
if (device->netdev == RT_NULL)
{
LOG_E("m26 device(%s) initialize failed, get network interface device failed.", m26->device_name);
return -RT_ERROR;
}
/* initialize m26 pin configuration */
if (m26->power_pin != -1 && m26->power_status_pin != -1)
{
rt_pin_mode(m26->power_pin, PIN_MODE_OUTPUT);
rt_pin_mode(m26->power_status_pin, PIN_MODE_INPUT);
}
/* initialize m26 device network */
return m26_netdev_set_up(device->netdev);
}
static int m26_deinit(struct at_device *device)
{
return m26_netdev_set_down(device->netdev);
}
static int m26_control(struct at_device *device, int cmd, void *arg)
{
int result = -RT_ERROR;
RT_ASSERT(device);
switch (cmd)
{
case AT_DEVICE_CTRL_POWER_ON:
case AT_DEVICE_CTRL_POWER_OFF:
case AT_DEVICE_CTRL_RESET:
case AT_DEVICE_CTRL_LOW_POWER:
case AT_DEVICE_CTRL_SLEEP:
case AT_DEVICE_CTRL_WAKEUP:
case AT_DEVICE_CTRL_NET_CONN:
case AT_DEVICE_CTRL_NET_DISCONN:
case AT_DEVICE_CTRL_SET_WIFI_INFO:
case AT_DEVICE_CTRL_GET_SIGNAL:
case AT_DEVICE_CTRL_GET_GPS:
case AT_DEVICE_CTRL_GET_VER:
LOG_W("m26 not support the control command(%d).", cmd);
break;
default:
LOG_E("input error control command(%d).", cmd);
break;
}
return result;
}
static const struct at_device_ops m26_device_ops =
{
m26_init,
m26_deinit,
m26_control,
};
static int m26_device_class_register(void)
{
struct at_device_class *class = RT_NULL;
class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class));
if (class == RT_NULL)
{
LOG_E("no memory for m26 device class create.");
return -RT_ENOMEM;
}
/* fill m26 device class object */
#ifdef AT_USING_SOCKET
m26_socket_class_register(class);
#endif
class->device_ops = &m26_device_ops;
return at_device_class_register(class, AT_DEVICE_CLASS_M26_MC20);
}
INIT_DEVICE_EXPORT(m26_device_class_register);
#endif /* AT_DEVICE_USING_M26 */

66
class/m26/at_device_m26.h Normal file
View File

@ -0,0 +1,66 @@
/*
* File : at_device_m26.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-16 chenyong first version
*/
#ifndef __AT_DEVICE_M26_H__
#define __AT_DEVICE_M26_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <at_device.h>
/* The maximum number of sockets supported by the m26 device */
#define AT_DEVICE_M26_SOCKETS_NUM 6
struct at_device_m26
{
char *device_name;
char *client_name;
int power_pin;
int power_status_pin;
size_t recv_line_num;
struct at_device device;
void *user_data;
};
#ifdef AT_USING_SOCKET
/* m26 device socket initialize */
int m26_socket_init(struct at_device *device);
/* m26 device class socket register */
int m26_socket_class_register(struct at_device_class *class);
#endif /* AT_USING_SOCKET */
#ifdef __cplusplus
}
#endif
#endif /* __AT_DEVICE_M26_H__ */

702
class/m26/at_socket_m26.c Normal file
View File

@ -0,0 +1,702 @@
/*
* File : at_socket_m26.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-12 chenyong first version
* 2019-05-12 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_m26.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.dev"
#include <at_log.h>
#if defined(AT_DEVICE_USING_M26) && defined(AT_USING_SOCKET)
#define M26_MODULE_SEND_MAX_SIZE 1460
/* set real event by current socket and current state */
#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event))
/* AT socket event type */
#define M26_EVENT_CONN_OK (1L << 0)
#define M26_EVENT_SEND_OK (1L << 1)
#define M26_EVENT_RECV_OK (1L << 2)
#define M26_EVNET_CLOSE_OK (1L << 3)
#define M26_EVENT_CONN_FAIL (1L << 4)
#define M26_EVENT_SEND_FAIL (1L << 5)
static at_evt_cb_t at_evt_cb_set[] =
{
[AT_SOCKET_EVT_RECV] = NULL,
[AT_SOCKET_EVT_CLOSED] = NULL,
};
static int m26_socket_event_send(struct at_device *device, uint32_t event)
{
return (int) rt_event_send(device->socket_event, event);
}
static int m26_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option)
{
int result = 0;
rt_uint32_t recved = 0;
result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved);
if (result != RT_EOK)
{
return -RT_ETIMEOUT;
}
return recved;
}
/**
* close socket by AT commands.
*
* @param current socket
*
* @return 0: close socket success
* -1: send AT commands error
* -2: wait socket event timeout
* -5: no memory
*/
static int m26_socket_close(struct at_socket *socket)
{
int result = 0;
at_response_t resp = RT_NULL;
int device_socke = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
resp = at_create_resp(64, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
/* clear socket close event */
m26_socket_event_recv(device, SET_EVENT(device_socke, M26_EVNET_CLOSE_OK), 0, RT_EVENT_FLAG_OR);
if (at_obj_exec_cmd(device->client, resp, "AT+QICLOSE=%d", device_socke) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (m26_socket_event_recv(device, SET_EVENT(device_socke, M26_EVNET_CLOSE_OK),
rt_tick_from_millisecond(300 * 3), RT_EVENT_FLAG_AND) < 0)
{
LOG_E("m26 device(%s) socket(%d) close failed, wait close OK timeout.", device->name, device_socke);
result = -RT_ETIMEOUT;
goto __exit;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* create TCP/UDP client or server connect by AT commands.
*
* @param socket current socket
* @param ip server or client IP address
* @param port server or client port
* @param type connect socket type(tcp, udp)
* @param is_client connection is client
*
* @return 0: connect success
* -1: connect failed, send commands error or type error
* -2: wait socket event timeout
* -5: no memory
*/
static int m26_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client)
{
rt_bool_t retryed = RT_FALSE;
at_response_t resp = RT_NULL;
int result = 0, event_result = 0;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
resp = at_create_resp(128, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
RT_ASSERT(ip);
RT_ASSERT(port >= 0);
__retry:
/* clear socket connect event */
event_result = SET_EVENT(device_socket, M26_EVENT_CONN_OK | M26_EVENT_CONN_FAIL);
m26_socket_event_recv(device, event_result, 0, RT_EVENT_FLAG_OR);
if (is_client)
{
switch (type)
{
case AT_SOCKET_TCP:
/* send AT commands(eg: AT+QIOPEN=0,"TCP","x.x.x.x", 1234) to connect TCP server */
if (at_obj_exec_cmd(device->client, resp,
"AT+QIOPEN=%d,\"TCP\",\"%s\",%d", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
goto __exit;
}
break;
case AT_SOCKET_UDP:
if (at_obj_exec_cmd(device->client, resp,
"AT+QIOPEN=%d,\"UDP\",\"%s\",%d", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
goto __exit;
}
break;
default:
LOG_E("m26 device(%s) not supported connect type : %d.", device->name, type);
return -RT_ERROR;
}
}
/* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/
if (m26_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0)
{
LOG_E("m26 device(%s) socket(%d) connect failed, wait connect result timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
if ((event_result = m26_socket_event_recv(device, M26_EVENT_CONN_OK | M26_EVENT_CONN_FAIL,
1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR)) < 0)
{
LOG_E("m26 device(%s) socket(%d) connect failed, wait connect OK|FAIL timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & M26_EVENT_CONN_FAIL)
{
if (retryed == RT_FALSE)
{
LOG_D("m26 device(%s) socket(%d) connect failed, maybe the socket was not be closed and now will retry.",
device->name, device_socket);
if (m26_socket_close(socket) < 0)
{
goto __exit;
}
retryed = RT_TRUE;
goto __retry;
}
LOG_E("m26 device(%s) socket(%d) connect failed, failed to establish a connection.", device->name, device_socket);
result = -RT_ERROR;
goto __exit;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static int at_get_send_size(struct at_socket *socket, size_t *size, size_t *acked, size_t *nacked)
{
int result = 0;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
resp = at_create_resp(64, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response structure!", device->name);
result = -RT_ENOMEM;
goto __exit;
}
if (at_obj_exec_cmd(device->client, resp, "AT+QISACK=%d", device_socket) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "+QISACK:", "+QISACK: %d, %d, %d", size, acked, nacked) <= 0)
{
LOG_E("m26 device(%s) prase \"AT+QISACK\" commands resposne data error!", device->name);
result = -RT_ERROR;
goto __exit;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static int at_wait_send_finish(struct at_socket *socket, size_t settings_size)
{
/* get the timeout by the input data size */
rt_tick_t timeout = rt_tick_from_millisecond(settings_size);
rt_tick_t last_time = rt_tick_get();
size_t size = 0, acked = 0, nacked = 0xFFFF;
while (rt_tick_get() - last_time <= timeout)
{
at_get_send_size(socket, &size, &acked, &nacked);
if (nacked == 0)
{
return RT_EOK;
}
rt_thread_mdelay(50);
}
return -RT_ETIMEOUT;
}
/**
* send data to server or client by AT commands.
*
* @param socket current socket
* @param buff send buffer
* @param bfsz send buffer size
* @param type connect socket type(tcp, udp)
*
* @return >=0: the size of send success
* -1: send AT commands error or send data error
* -2: waited socket event timeout
* -5: no memory
*/
static int m26_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type)
{
int result = 0, event_result = 0;
size_t pkt_size = 0, sent_size = 0;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
struct at_device_m26 *m26 = (struct at_device_m26 *) device->user_data;
rt_mutex_t lock = device->client->lock;
RT_ASSERT(buff);
resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
rt_mutex_take(lock, RT_WAITING_FOREVER);
/* Clear socket send event */
event_result = SET_EVENT(device_socket, M26_EVENT_SEND_OK | M26_EVENT_SEND_FAIL);
m26_socket_event_recv(device, event_result, 0, RT_EVENT_FLAG_OR);
/* set current socket for send URC event */
m26->user_data = (void *) device_socket;
/* set AT client end sign to deal with '>' sign.*/
at_obj_set_end_sign(device->client, '>');
while (sent_size < bfsz)
{
if (bfsz - sent_size < M26_MODULE_SEND_MAX_SIZE)
{
pkt_size = bfsz - sent_size;
}
else
{
pkt_size = M26_MODULE_SEND_MAX_SIZE;
}
/* send the "AT+QISEND" commands to AT server than receive the '>' response on the first line. */
if (at_obj_exec_cmd(device->client, resp, "AT+QISEND=%d,%d", device_socket, pkt_size) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* send the real data to server or client */
result = (int) at_client_obj_send(device->client, buff + sent_size, pkt_size);
if (result == 0)
{
result = -RT_ERROR;
goto __exit;
}
/* waiting result event from AT URC */
if (m26_socket_event_recv(device, SET_EVENT(device_socket, 0), 15 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0)
{
LOG_E("m26 device(%s) socket(%d) send failed, wait connect result timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
if ((event_result = m26_socket_event_recv(device, M26_EVENT_SEND_OK | M26_EVENT_SEND_FAIL,
1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR)) < 0)
{
LOG_E("m26 device(%s) socket(%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & M26_EVENT_SEND_FAIL)
{
LOG_E("m26 device(%s) socket(%d) send failed.", device->name, device_socket);
result = -RT_ERROR;
goto __exit;
}
if (type == AT_SOCKET_TCP)
{
at_wait_send_finish(socket, pkt_size);
}
sent_size += pkt_size;
}
__exit:
/* reset the end sign for data conflict */
at_obj_set_end_sign(device->client, 0);
rt_mutex_release(lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* domain resolve by AT commands.
*
* @param name domain name
* @param ip parsed IP address, it's length must be 16
*
* @return 0: domain resolve success
* -1: send AT commands error or response error
* -2: wait socket event timeout
* -5: no memory
*/
static int m26_domain_resolve(const char *name, char ip[16])
{
#define RESOLVE_RETRY 5
int i, result = RT_EOK;
char recv_ip[16] = { 0 };
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(name);
RT_ASSERT(ip);
device = at_device_get_first_initialized();
if (device == RT_NULL)
{
LOG_E("get first initialization m26 device failed.");
return -RT_ERROR;
}
/* The maximum response time is 14 seconds, affected by network status */
resp = at_create_resp(128, 4, 14 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for m26 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
for(i = 0; i < RESOLVE_RETRY; i++)
{
if (at_obj_exec_cmd(device->client, resp, "AT+QIDNSGIP=\"%s\"", name) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* parse the third line of response data, get the IP address */
if(at_resp_parse_line_args_by_kw(resp, ".", "%s", recv_ip) < 0)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
if (rt_strlen(recv_ip) < 8)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
else
{
rt_thread_mdelay(10);
rt_strncpy(ip, recv_ip, 15);
ip[15] = '\0';
break;
}
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* set AT socket event notice callback
*
* @param event notice event
* @param cb notice callback
*/
static void m26_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb)
{
if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1]))
{
at_evt_cb_set[event] = cb;
}
}
static void urc_connect_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get m26 device by client name(%s) failed.", client_name);
return;
}
sscanf(data, "%d%*[^0-9]", &device_socket);
if (rt_strstr(data, "CONNECT OK"))
{
m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVENT_CONN_OK));
}
else
{
m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVENT_CONN_FAIL));
}
}
static void urc_send_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_device *device = RT_NULL;
struct at_device_m26 *m26 = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get m26 device by client name(%s) failed.", client_name);
return;
}
m26 = (struct at_device_m26 *) device->user_data;
device_socket = (int) m26->user_data;
if (rt_strstr(data, "SEND OK"))
{
m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVENT_SEND_OK));
}
else if (rt_strstr(data, "SEND FAIL"))
{
m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVENT_SEND_FAIL));
}
}
static void urc_close_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get m26 device by client name(%s) failed.", client_name);
return;
}
sscanf(data, "%d%*s", &device_socket);
if (rt_strstr(data, "CLOSE OK"))
{
m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVNET_CLOSE_OK));
}
else if (rt_strstr(data, "CLOSED"))
{
struct at_socket *socket = RT_NULL;
/* get at socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the socket is disconnect by remote */
if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED])
{
at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, NULL, 0);
}
}
}
static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
rt_int32_t timeout;
rt_size_t bfsz = 0, temp_size = 0;
char *recv_buf = RT_NULL, temp[8] = {0};
struct at_socket *socket = RT_NULL;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get m26 device by client name(%s) failed.", client_name);
return;
}
/* get the current socket and receive buffer size by receive data */
sscanf(data, "+RECEIVE: %d, %d", &device_socket, (int *) &bfsz);
/* get receive timeout by receive buffer length */
timeout = bfsz;
if (device_socket < 0 || bfsz == 0)
{
return;
}
recv_buf = (char *) rt_calloc(1, bfsz);
if (recv_buf == RT_NULL)
{
LOG_E("no memory for m26 device(%s) urc receive buffer (%d).", device->name, bfsz);
/* read and clean the coming data */
while (temp_size < bfsz)
{
if (bfsz - temp_size > sizeof(temp))
{
at_client_obj_recv(client, temp, sizeof(temp), timeout);
}
else
{
at_client_obj_recv(client, temp, bfsz - temp_size, timeout);
}
temp_size += sizeof(temp);
}
return;
}
/* sync receive data */
if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz)
{
LOG_E("m26 device(%s) receive size(%d) data failed.", device->name, bfsz);
rt_free(recv_buf);
return;
}
/* get at socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the receive buffer and buffer size */
if (at_evt_cb_set[AT_SOCKET_EVT_RECV])
{
at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz);
}
}
static const struct at_urc urc_table[] =
{
{"", ", CONNECT OK\r\n", urc_connect_func},
{"", ", CONNECT FAIL\r\n", urc_connect_func},
{"SEND OK", "\r\n", urc_send_func},
{"SEND FAIL", "\r\n", urc_send_func},
{"", ", CLOSE OK\r\n", urc_close_func},
{"", ", CLOSED\r\n", urc_close_func},
{"+RECEIVE:", "\r\n", urc_recv_func},
};
static const struct at_socket_ops m26_socket_ops =
{
m26_socket_connect,
m26_socket_close,
m26_socket_send,
m26_domain_resolve,
m26_socket_set_event_cb,
};
int m26_socket_init(struct at_device *device)
{
RT_ASSERT(device);
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
return RT_EOK;
}
int m26_socket_class_register(struct at_device_class *class)
{
RT_ASSERT(class);
class->socket_num = AT_DEVICE_M26_SOCKETS_NUM;
class->socket_ops = &m26_socket_ops;
return RT_EOK;
}
#endif /* AT_DEVICE_USING_M26 && AT_USING_SOCKET */

View File

@ -0,0 +1,403 @@
/*
* File : at_socket_rw007.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-20 chenyong first version
* 2019-05-12 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_rw007.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.dev"
#include <at_log.h>
#ifdef AT_DEVICE_USING_RW007
#define RW007_WAIT_CONNECT_TIME 5000
#define RW007_THREAD_STACK_SIZE 1024
#define RW007_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 2)
/* ============================= rw007 network interface operations ============================= */
static struct netdev *rw007_netdev_add(const char *netdev_name)
{
#define ETHERNET_MTU 1500
#define HWADDR_LEN 6
struct netdev *netdev = RT_NULL;
RT_ASSERT(netdev_name);
netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev));
if (netdev == RT_NULL)
{
return RT_NULL;
}
/* TODO: improve netdev adaptation */
netdev->mtu = ETHERNET_MTU;
netdev->hwaddr_len = HWADDR_LEN;
netdev->ops = RT_NULL;
#ifdef SAL_USING_AT
extern int sal_at_netdev_set_pf_info(struct netdev *netdev);
/* set the network interface socket/netdb operations */
sal_at_netdev_set_pf_info(netdev);
#endif
netdev_register(netdev, netdev_name, RT_NULL);
/*TODO: improve netdev adaptation */
netdev_low_level_set_status(netdev, RT_TRUE);
netdev_low_level_set_link_status(netdev, RT_TRUE);
netdev_low_level_set_dhcp_status(netdev, RT_TRUE);
netdev->flags |= NETDEV_FLAG_INTERNET_UP;
return netdev;
}
/* ============================= rw007 device operations ============================= */
#define AT_SEND_CMD(client, resp, cmd) \
do { \
(resp) = at_resp_set_info((resp), 256, 0, 5 * RT_TICK_PER_SECOND); \
if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \
{ \
result = -RT_ERROR; \
goto __exit; \
} \
} while(0) \
static void rw007_init_thread_entry(void *parameter)
{
#define INIT_RETRY 5
struct at_device *device = (struct at_device *) parameter;
struct at_device_rw007 *rw007 = (struct at_device_rw007 *) device->user_data;
struct at_client *client = device->client;
at_response_t resp = RT_NULL;
rt_err_t result = RT_EOK;
rt_size_t i = 0, retry_num = INIT_RETRY;
/* wait rw007 device startup finish */
if (at_client_obj_wait_connect(client, RW007_WAIT_CONNECT_TIME))
{
return;
}
resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for rw007 device(%d) response structure.", device->name);
return;
}
while (retry_num--)
{
/* reset module */
AT_SEND_CMD(client, resp, "AT+RST");
/* reset waiting delay */
rt_thread_mdelay(1000);
/* disable echo */
AT_SEND_CMD(client, resp, "ATE0");
/* set current mode to Wi-Fi station */
AT_SEND_CMD(client, resp, "AT+CWMODE=1");
/* get module version */
AT_SEND_CMD(client, resp, "AT+GMR");
/* show module version */
for (i = 0; i < resp->line_counts - 1; i++)
{
LOG_D("%s", at_resp_get_line(resp, i + 1));
}
/* connect to WiFi AP */
if (at_obj_exec_cmd(client, at_resp_set_info(resp, 128, 0, 20 * RT_TICK_PER_SECOND),
"AT+CWJAP=\"%s\",\"%s\"", rw007->wifi_ssid, rw007->wifi_password) != RT_EOK)
{
LOG_E("rw007 device(%s) network initialize failed, check ssid(%s) and password(%s).",
device->name, rw007->wifi_ssid, rw007->wifi_password);
result = -RT_ERROR;
goto __exit;
}
AT_SEND_CMD(client, resp, "AT+CIPMUX=1");
__exit:
if (result == RT_EOK)
{
break;
}
else
{
rt_thread_mdelay(1000);
LOG_I("rw007 device(%s) initialize retry...", device->name);
}
}
if (resp)
{
at_delete_resp(resp);
}
if (result != RT_EOK)
{
netdev_low_level_set_status(device->netdev, RT_FALSE);
LOG_E("rw007 device(%s) network initialize failed(%d).", device->name, result);
}
else
{
netdev_low_level_set_status(device->netdev, RT_TRUE);
LOG_I("rw007 device(%s) network initialize successfully.", device->name);
}
}
int rw007_net_init(struct at_device *device)
{
#ifdef AT_DEVICE_RW007_INIT_ASYN
rt_thread_t tid;
tid = rt_thread_create("rw007_net_init", rw007_init_thread_entry,
(void *)device, RW007_THREAD_STACK_SIZE, RW007_THREAD_PRIORITY, 20);
if (tid)
{
rt_thread_startup(tid);
}
else
{
LOG_E("create rw007 device(%s) initialization thread failed.", device->name);
return -RT_ERROR;
}
#else
rw007_init_thread_entry(device);
#endif /* AT_DEVICE_RW007_INIT_ASYN */
return RT_EOK;
}
static void urc_busy_p_func(struct at_client *client, const char *data, rt_size_t size)
{
LOG_D("system is processing a commands and it cannot respond to the current commands.");
}
static void urc_busy_s_func(struct at_client *client, const char *data, rt_size_t size)
{
LOG_D("system is sending data and it cannot respond to the current commands.");
}
static void urc_func(struct at_client *client, const char *data, rt_size_t size)
{
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(client && data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get rw007 device by client name(%s) failed.", client_name);
return;
}
if (rt_strstr(data, "WIFI CONNECTED"))
{
LOG_I("rw007 device(%s) WIFI is connected.", device->name);
}
else if (rt_strstr(data, "WIFI DISCONNECT"))
{
LOG_I("rw007 device(%s) WIFI is disconnect.", device->name);
}
}
static struct at_urc urc_table[] = {
{"busy p", "\r\n", urc_busy_p_func},
{"busy s", "\r\n", urc_busy_s_func},
{"WIFI CONNECTED", "\r\n", urc_func},
{"WIFI DISCONNECT", "\r\n", urc_func},
};
static int rw007_init(struct at_device *device)
{
struct at_device_rw007 *rw007 = (struct at_device_rw007 *) device->user_data;
/* initialize AT client */
at_client_init(rw007->client_name, rw007->recv_line_num);
device->client = at_client_get(rw007->client_name);
if (device->client == RT_NULL)
{
LOG_E("rw007 device(%s) initialize failed, get AT client(%s) failed.",
rw007->device_name, rw007->client_name);
return -RT_ERROR;
}
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
#ifdef AT_USING_SOCKET
rw007_socket_init(device);
#endif
/* add rw007 device to the netdev list */
device->netdev = rw007_netdev_add(rw007->device_name);
if (device->netdev == RT_NULL)
{
LOG_E("rw007 device(%s) initialize failed, get network interface device failed.", rw007->device_name);
return -RT_ERROR;
}
/* initialize rw007 device network */
return rw007_net_init(device);
}
static int rw007_deinit(struct at_device *device)
{
// TODO add netdev operation
device->is_init = RT_FALSE;
return RT_EOK;
}
/* reset eap8266 device and initialize device network again */
static int rw007_reset(struct at_device *device)
{
int result = RT_EOK;
struct at_client *client = device->client;
/* send "AT+RST" commonds to rw007 device */
result = at_obj_exec_cmd(client, RT_NULL, "AT+RST");
rt_thread_delay(1000);
/* waiting 10 seconds for rw007 device reset */
device->is_init = RT_FALSE;
if (at_client_obj_wait_connect(client, RW007_WAIT_CONNECT_TIME))
{
return -RT_ETIMEOUT;
}
/* initialize rw007 device network */
rw007_net_init(device);
device->is_init = RT_TRUE;
return result;
}
/* change eap8266 wifi ssid and password information */
static int rw007_wifi_info_set(struct at_device *device, struct at_device_ssid_pwd *info)
{
int result = RT_EOK;
struct at_response *resp = RT_NULL;
if (info->ssid == RT_NULL || info->password == RT_NULL)
{
LOG_E("input rwoo7 wifi ssid(%s) and password(%s) error.", info->ssid, info->password);
return -RT_ERROR;
}
resp = at_create_resp(128, 0, 20 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for rw007 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
/* connect to input wifi ap */
if (at_obj_exec_cmd(device->client, resp, "AT+CWJAP=\"%s\",\"%s\"", info->ssid, info->password) != RT_EOK)
{
LOG_E("rw007 device(%s) wifi connect failed, check ssid(%s) and password(%s).",
device->name, info->ssid, info->password);
result = -RT_ERROR;
}
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static int rw007_control(struct at_device *device, int cmd, void *arg)
{
int result = -RT_ERROR;
RT_ASSERT(device);
switch (cmd)
{
case AT_DEVICE_CTRL_POWER_ON:
case AT_DEVICE_CTRL_POWER_OFF:
case AT_DEVICE_CTRL_LOW_POWER:
case AT_DEVICE_CTRL_SLEEP:
case AT_DEVICE_CTRL_WAKEUP:
case AT_DEVICE_CTRL_NET_CONN:
case AT_DEVICE_CTRL_NET_DISCONN:
case AT_DEVICE_CTRL_GET_SIGNAL:
case AT_DEVICE_CTRL_GET_GPS:
case AT_DEVICE_CTRL_GET_VER:
LOG_W("rw007 not support the control command(%d).", cmd);
break;
case AT_DEVICE_CTRL_RESET:
result = rw007_reset(device);
break;
case AT_DEVICE_CTRL_SET_WIFI_INFO:
result = rw007_wifi_info_set(device, (struct at_device_ssid_pwd *) arg);
break;
default:
LOG_E("input error control command(%d).", cmd);
break;
}
return result;
}
const struct at_device_ops rw007_device_ops =
{
rw007_init,
rw007_deinit,
rw007_control,
};
static int rw007_device_class_register(void)
{
struct at_device_class *class = RT_NULL;
class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class));
if (class == RT_NULL)
{
LOG_E("no memory for rw007 device class create.");
return -RT_ENOMEM;
}
/* fill rw007 device class object */
#ifdef AT_USING_SOCKET
rw007_socket_class_register(class);
#endif
class->device_ops = &rw007_device_ops;
return at_device_class_register(class, AT_DEVICE_CLASS_RW007);
}
INIT_DEVICE_EXPORT(rw007_device_class_register);
#endif /* AT_DEVICE_USING_RW007 */

View File

@ -0,0 +1,66 @@
/*
* File : at_device_RW007.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-16 chenyong first version
*/
#ifndef __AT_DEVICE_RW007_H__
#define __AT_DEVICE_RW007_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <at_device.h>
/* The maximum number of sockets supported by the rw007 device */
#define AT_DEVICE_RW007_SOCKETS_NUM 5
struct at_device_rw007
{
char *device_name;
char *client_name;
char *wifi_ssid;
char *wifi_password;
size_t recv_line_num;
struct at_device device;
void *user_data;
};
#ifdef AT_USING_SOCKET
/* rw007 device socket initialize */
int rw007_socket_init(struct at_device *device);
/* rw007 device class socket register */
int rw007_socket_class_register(struct at_device_class *class);
#endif /* AT_USING_SOCKET */
#ifdef __cplusplus
}
#endif
#endif /* __AT_DEVICE_RW007_H__ */

View File

@ -0,0 +1,561 @@
/*
* File : at_socket_rw007.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-20 chenyong first version
* 2019-05-12 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_rw007.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.skt"
#include <at_log.h>
#if defined(AT_DEVICE_USING_RW007) && defined(AT_USING_SOCKET)
#define RW007_MODULE_SEND_MAX_SIZE 2048
/* set real event by current socket and current state */
#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event))
/* AT socket event type */
#define RW007_EVENT_CONN_OK (1L << 0)
#define RW007_EVENT_SEND_OK (1L << 1)
#define RW007_EVENT_RECV_OK (1L << 2)
#define RW007_EVNET_CLOSE_OK (1L << 3)
#define RW007_EVENT_CONN_FAIL (1L << 4)
#define RW007_EVENT_SEND_FAIL (1L << 5)
static at_evt_cb_t at_evt_cb_set[] = {
[AT_SOCKET_EVT_RECV] = NULL,
[AT_SOCKET_EVT_CLOSED] = NULL,
};
static int rw007_socket_event_send(struct at_device *device, uint32_t event)
{
return (int) rt_event_send(device->socket_event, event);
}
static int rw007_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option)
{
int result = RT_EOK;
rt_uint32_t recved;
result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved);
if (result != RT_EOK)
{
return -RT_ETIMEOUT;
}
return recved;
}
/**
* close socket by AT commands.
*
* @param current socket
*
* @return 0: close socket success
* -1: send AT commands error
* -2: wait socket event timeout
* -5: no memory
*/
static int rw007_socket_close(struct at_socket *socket)
{
int result = RT_EOK;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
resp = at_create_resp(64, 0, RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for rw007 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
result = at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE=%d", device_socket);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* create TCP/UDP client or server connect by AT commands.
*
* @param socket current socket
* @param ip server or client IP address
* @param port server or client port
* @param type connect socket type(tcp, udp)
* @param is_client connection is client
*
* @return 0: connect success
* -1: connect failed, send commands error or type error
* -2: wait socket event timeout
* -5: no memory
*/
static int rw007_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client)
{
int result = RT_EOK;
rt_bool_t retryed = RT_FALSE;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
RT_ASSERT(ip);
RT_ASSERT(port >= 0);
resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for rw007 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
__retry:
if (is_client)
{
switch (type)
{
case AT_SOCKET_TCP:
/* send AT commands to connect TCP server */
if (at_obj_exec_cmd(device->client, resp,
"AT+CIPSTART=%d,\"TCP\",\"%s\",%d,60", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
}
break;
case AT_SOCKET_UDP:
if (at_obj_exec_cmd(device->client, resp,
"AT+CIPSTART=%d,\"UDP\",\"%s\",%d", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
}
break;
default:
LOG_E("rw007 device(%s) not supported connect type %d.", device->name, type);
result = -RT_ERROR;
goto __exit;
}
}
if (result != RT_EOK && retryed == RT_FALSE)
{
LOG_D("rw007 device(%s) socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.",
device->name, socket);
if (rw007_socket_close(socket) < 0)
{
goto __exit;
}
retryed = RT_TRUE;
result = RT_EOK;
goto __retry;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* send data to server or client by AT commands.
*
* @param socket current socket
* @param buff send buffer
* @param bfsz send buffer size
* @param type connect socket type(tcp, udp)
*
* @return >=0: the size of send success
* -1: send AT commands error or send data error
* -2: waited socket event timeout
* -5: no memory
*/
static int rw007_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type)
{
int result = RT_EOK;
int event_result = 0;
size_t cur_pkt_size = 0, sent_size = 0;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
struct at_device_rw007 *rw007 = (struct at_device_rw007 *) device->user_data;
rt_mutex_t lock = device->client->lock;
RT_ASSERT(buff);
RT_ASSERT(bfsz > 0);
resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for rw007 device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
rt_mutex_take(lock, RT_WAITING_FOREVER);
/* set current socket for send URC event */
rw007->user_data = (void *) device_socket;
/* set AT client end sign to deal with '>' sign */
at_obj_set_end_sign(device->client, '>');
while (sent_size < bfsz)
{
if (bfsz - sent_size < RW007_MODULE_SEND_MAX_SIZE)
{
cur_pkt_size = bfsz - sent_size;
}
else
{
cur_pkt_size = RW007_MODULE_SEND_MAX_SIZE;
}
/* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d", device_socket, cur_pkt_size) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* send the real data to server or client */
result = (int) at_client_obj_send(device->client, buff + sent_size, cur_pkt_size);
if (result == 0)
{
result = -RT_ERROR;
goto __exit;
}
/* waiting result event from AT URC */
if (rw007_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0)
{
LOG_E("rw007 device(%s) socket (%d) send failed, wait connect result timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
event_result = rw007_socket_event_recv(device, RW007_EVENT_SEND_OK | RW007_EVENT_SEND_FAIL,
5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR);
if (event_result < 0)
{
LOG_E("rw007 device(%s) socket (%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & RW007_EVENT_SEND_FAIL)
{
LOG_E("rw007 device(%s) socket (%d) send failed.", device->name, device_socket);
result = -RT_ERROR;
goto __exit;
}
sent_size += cur_pkt_size;
}
__exit:
/* reset the end sign for data */
at_obj_set_end_sign(device->client, 0);
rt_mutex_release(lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* domain resolve by AT commands.
*
* @param name domain name
* @param ip parsed IP address, it's length must be 16
*
* @return 0: domain resolve success
* -2: wait socket event timeout
* -5: no memory
*/
static int rw007_domain_resolve(const char *name, char ip[16])
{
#define RESOLVE_RETRY 5
int i, result = RT_EOK;
char recv_ip[16] = { 0 };
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(name);
RT_ASSERT(ip);
device = at_device_get_first_initialized();
if (device == RT_NULL)
{
LOG_E("get first initialization rw007 device failed.");
return -RT_ERROR;
}
resp = at_create_resp(128, 0, 20 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for rw007 device response structure.", device->name);
return -RT_ENOMEM;
}
for (i = 0; i < RESOLVE_RETRY; i++)
{
if (at_obj_exec_cmd(device->client, resp, "AT+CIPDOMAIN=\"%s\"", name) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* parse the third line of response data, get the IP address */
if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", recv_ip) < 0)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
if (rt_strlen(recv_ip) < 8)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
else
{
rt_strncpy(ip, recv_ip, 15);
ip[15] = '\0';
break;
}
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* set AT socket event notice callback
*
* @param event notice event
* @param cb notice callback
*/
static void rw007_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb)
{
if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1]))
{
at_evt_cb_set[event] = cb;
}
}
static const struct at_socket_ops rw007_socket_ops =
{
rw007_socket_connect,
rw007_socket_close,
rw007_socket_send,
rw007_domain_resolve,
rw007_socket_set_event_cb,
};
static void urc_send_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_device *device = RT_NULL;
struct at_device_rw007 *rw007 = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get rw007 device by client name(%s) failed.", client_name);
return;
}
rw007 = (struct at_device_rw007 *) device->user_data;
device_socket = (int) rw007->user_data;
if (rt_strstr(data, "SEND OK"))
{
rw007_socket_event_send(device, SET_EVENT(device_socket, RW007_EVENT_SEND_OK));
}
else if (rt_strstr(data, "SEND FAIL"))
{
rw007_socket_event_send(device, SET_EVENT(device_socket, RW007_EVENT_SEND_FAIL));
}
}
static void urc_send_bfsz_func(struct at_client *client, const char *data, rt_size_t size)
{
static int cur_send_bfsz = 0;
RT_ASSERT(data && size);
sscanf(data, "Recv %d bytes", &cur_send_bfsz);
}
static void urc_close_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_socket *socket = RT_NULL;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get rw007 device by client name(%s) failed.", client_name);
return;
}
sscanf(data, "%d,CLOSED", &device_socket);
/* get at socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the socket is disconnect by remote */
if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED])
{
at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0);
}
}
static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
rt_int32_t timeout = 0;
rt_size_t bfsz = 0, temp_size = 0;
char *recv_buf = RT_NULL, temp[8] = {0};
struct at_socket *socket = RT_NULL;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get rw007 device by client name(%s) failed.", client_name);
return;
}
/* get the current socket and receive buffer size by receive data */
sscanf(data, "+IPD,%d,%d:", &device_socket, (int *) &bfsz);
/* get receive timeout by receive buffer length */
timeout = bfsz;
if (device_socket < 0 || bfsz == 0)
{
return;
}
recv_buf = (char *) rt_calloc(1, bfsz);
if (recv_buf == RT_NULL)
{
LOG_E("no memory for rw007 device(%s) URC receive buffer (%d).", device->name, bfsz);
/* read and clean the coming data */
while (temp_size < bfsz)
{
if (bfsz - temp_size > sizeof(temp))
{
at_client_obj_recv(client, temp, sizeof(temp), timeout);
}
else
{
at_client_obj_recv(client, temp, bfsz - temp_size, timeout);
}
temp_size += sizeof(temp);
}
return;
}
/* sync receive data */
if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz)
{
LOG_E("rw007 device(%s) receive size(%d) data failed.", device->name, bfsz);
rt_free(recv_buf);
return;
}
/* get at socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the receive buffer and buffer size */
if (at_evt_cb_set[AT_SOCKET_EVT_RECV])
{
at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz);
}
}
static struct at_urc urc_table[] =
{
{"SEND OK", "\r\n", urc_send_func},
{"SEND FAIL", "\r\n", urc_send_func},
{"Recv", "bytes\r\n", urc_send_bfsz_func},
{"", ",CLOSED\r\n", urc_close_func},
{"+IPD", ":", urc_recv_func},
};
int rw007_socket_init(struct at_device *device)
{
RT_ASSERT(device);
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
return RT_EOK;
}
int rw007_socket_class_register(struct at_device_class *class)
{
RT_ASSERT(class);
class->socket_num = AT_DEVICE_RW007_SOCKETS_NUM;
class->socket_ops = &rw007_socket_ops;
return RT_EOK;
}
#endif /* AT_DEVICE_USING_RW007 && AT_USING_SOCKET */

View File

@ -0,0 +1,610 @@
/*
* File : at_socket_sim76xx.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-12-22 thomasonegd first version
* 2019-03-06 thomasonegd fix udp connection.
* 2019-03-08 thomasonegd add power_on & power_off api
* 2019-05-14 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_sim76xx.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.dev"
#include <at_log.h>
#ifdef AT_DEVICE_USING_SIM76XX
#define SIM76XX_WAIT_CONNECT_TIME 5000
#define SIM76XX_THREAD_STACK_SIZE 1024
#define SIM76XX_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 2)
/**
* power up sim76xx modem
*/
static void sim76xx_power_on(struct at_device *device)
{
struct at_device_sim76xx *sim76xx = RT_NULL;
sim76xx = (struct at_device_sim76xx *) device->user_data;
/* not nead to set pin configuration for m26 device power on */
if (sim76xx->power_pin == -1 || sim76xx->power_status_pin == -1)
{
return;
}
if (rt_pin_read(sim76xx->power_status_pin) == PIN_HIGH)
{
return;
}
rt_pin_write(sim76xx->power_pin, PIN_HIGH);
while (rt_pin_read(sim76xx->power_status_pin) == PIN_LOW)
{
rt_thread_mdelay(10);
}
rt_pin_write(sim76xx->power_pin, PIN_LOW);
}
static void sim76xx_power_off(struct at_device *device)
{
struct at_device_sim76xx *sim76xx = RT_NULL;
sim76xx = (struct at_device_sim76xx *) device->user_data;
/* not nead to set pin configuration for m26 device power on */
if (sim76xx->power_pin == -1 || sim76xx->power_status_pin == -1)
{
return;
}
if (rt_pin_read(sim76xx->power_status_pin) == PIN_LOW)
{
return;
}
rt_pin_write(sim76xx->power_pin, PIN_HIGH);
while (rt_pin_read(sim76xx->power_status_pin) == PIN_HIGH)
{
rt_thread_mdelay(10);
}
rt_pin_write(sim76xx->power_pin, PIN_LOW);
}
/* ============================= sim76xx network interface operations ============================= */
static struct netdev *sim76xx_netdev_add(const char *netdev_name)
{
#define ETHERNET_MTU 1500
#define HWADDR_LEN 8
struct netdev *netdev = RT_NULL;
RT_ASSERT(netdev_name);
netdev = (struct netdev *)rt_calloc(1, sizeof(struct netdev));
if (netdev == RT_NULL)
{
LOG_E("no memory for sim76xx device(%s) netdev structure.", netdev_name);
return RT_NULL;
}
netdev->mtu = ETHERNET_MTU;
netdev->hwaddr_len = HWADDR_LEN;
netdev->ops = RT_NULL;
#ifdef SAL_USING_AT
extern int sal_at_netdev_set_pf_info(struct netdev * netdev);
/* set the network interface socket/netdb operations */
sal_at_netdev_set_pf_info(netdev);
#endif
netdev_register(netdev, netdev_name, RT_NULL);
/*TODO: improve netdev adaptation */
netdev_low_level_set_status(netdev, RT_TRUE);
netdev_low_level_set_link_status(netdev, RT_TRUE);
netdev_low_level_set_dhcp_status(netdev, RT_TRUE);
netdev->flags |= NETDEV_FLAG_INTERNET_UP;
return netdev;
}
/* ============================= sim76xx device operations ============================= */
#define AT_SEND_CMD(client, resp, cmd) \
do { \
(resp) = at_resp_set_info((resp), 256, 0, 5 * RT_TICK_PER_SECOND); \
if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \
{ \
result = -RT_ERROR; \
goto __exit; \
} \
} while(0) \
static void sim76xx_init_thread_entry(void *parameter)
{
#define INIT_RETRY 5
#define CSQ_RETRY 20
#define CREG_RETRY 10
#define CGREG_RETRY 20
#define CGATT_RETRY 10
#define CCLK_RETRY 10
at_response_t resp = RT_NULL;
rt_err_t result = RT_EOK;
rt_size_t i, qi_arg[3] = {0};
int retry_num = INIT_RETRY;
char parsed_data[20] = {0};
struct at_device *device = (struct at_device *)parameter;
struct at_client *client = device->client;
resp = at_create_resp(128, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for sim76xx device(%s) response structure.", device->name);
return;
}
LOG_D("start initializing the sim76xx device(%s).", device->name);
while (retry_num--)
{
/* power-up sim76xx */
sim76xx_power_on(device);
rt_thread_mdelay(1000);
/* wait SIM76XX startup finish, Send AT every 5s, if receive OK, SYNC success*/
if (at_client_wait_connect(SIM76XX_WAIT_CONNECT_TIME))
{
result = -RT_ETIMEOUT;
goto __exit;
}
/* disable echo */
AT_SEND_CMD(client, resp, "ATE0");
/* get module version */
AT_SEND_CMD(client, resp, "ATI");
/* show module version */
for (i = 0; i < (int)resp->line_counts - 1; i++)
{
LOG_D("%s", at_resp_get_line(resp, i + 1));
}
/* check SIM card */
AT_SEND_CMD(client, resp, "AT+CPIN?");
if (!at_resp_get_line_by_kw(resp, "READY"))
{
LOG_E("sim76xx device(%s) SIM card detection failed.", device->name);
result = -RT_ERROR;
goto __exit;
}
/* waiting for dirty data to be digested */
rt_thread_mdelay(10);
/* check signal strength */
for (i = 0; i < CSQ_RETRY; i++)
{
AT_SEND_CMD(client, resp, "AT+CSQ");
at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %d,%d", &qi_arg[0], &qi_arg[1]);
if (qi_arg[0] != 99)
{
LOG_D("sim76xx device(%s) signal strength: %d Channel bit error rate: %d", device->name, qi_arg[0], qi_arg[1]);
break;
}
rt_thread_mdelay(1000);
}
if (i == CSQ_RETRY)
{
LOG_E("sim76xx device(%s) signal strength check failed (%s).", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
/* do not show the prompt when receiving data */
AT_SEND_CMD(client, resp, "AT+CIPSRIP=0");
/* check the GSM network is registered */
for (i = 0; i < CREG_RETRY; i++)
{
AT_SEND_CMD(client, resp, "AT+CREG?");
at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data);
if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data)))
{
LOG_D("sim76xx device(%s) GSM network is registered(%s).", device->name, parsed_data);
break;
}
rt_thread_mdelay(1000);
}
if (i == CREG_RETRY)
{
LOG_E("sim76xx device(%s) GSM network is register failed(%s).", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
/* check the GPRS network is registered */
for (i = 0; i < CGREG_RETRY; i++)
{
AT_SEND_CMD(client, resp, "AT+CGREG?");
at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data);
if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data)))
{
LOG_D("sim76xx device(%s) GPRS network is registered(%s).", device->name, parsed_data);
break;
}
rt_thread_mdelay(1000);
}
if (i == CGREG_RETRY)
{
LOG_E("sim76xx device(%s) GPRS network is register failed(%s).", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
/* check packet domain attach or detach */
for (i = 0; i < CGATT_RETRY; i++)
{
AT_SEND_CMD(client, resp, "AT+CGATT?");
at_resp_parse_line_args_by_kw(resp, "+CGATT:", "+CGATT: %s", &parsed_data);
if (!strncmp(parsed_data, "1", 1))
{
LOG_I("sim76xx device(%s) Packet domain attach.", device->name);
break;
}
rt_thread_mdelay(1000);
}
if (i == CGATT_RETRY)
{
LOG_E("sim76xx device(%s) GPRS network attach failed.", device->name);
result = -RT_ERROR;
goto __exit;
}
/* get real time */
int year, month, day, hour, min, sec;
for (i = 0; i < CCLK_RETRY; i++)
{
if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 5 * RT_TICK_PER_SECOND), "AT+CCLK?") < 0)
{
rt_thread_mdelay(500);
continue;
}
/* +CCLK: "18/12/22,18:33:12+32" */
if (at_resp_parse_line_args_by_kw(resp, "+CCLK:", "+CCLK: \"%d/%d/%d,%d:%d:%d", &year, &month, &day, &hour, &min, &sec) < 0)
{
rt_thread_mdelay(500);
continue;
}
set_date(year + 2000, month, day);
set_time(hour, min, sec);
break;
}
if (i == CCLK_RETRY)
{
LOG_E("sim76xx device(%s) GPRS network attach failed.", device->name);
result = -RT_ERROR;
goto __exit;
}
/* set active PDP context's profile number */
AT_SEND_CMD(client, resp, "AT+CSOCKSETPN=1");
__exit:
if (result == RT_EOK)
{
break;
}
else
{
/* power off the sim76xx device */
sim76xx_power_off(device);
rt_thread_mdelay(1000);
LOG_I("sim76xx device(%s) initialize retry...", device->name);
}
}
if (resp)
{
at_delete_resp(resp);
}
if (result == RT_EOK)
{
LOG_I("sim76xx devuce(%s) network initialize success!", device->name);
}
else
{
LOG_E("sim76xx device(%s) network initialize failed(%d)!", device->name, result);
}
}
int sim76xx_net_init(struct at_device *device)
{
#ifdef AT_DEVICE_SIM76XX_INIT_ASYN
rt_thread_t tid;
tid = rt_thread_create("sim76xx_net_init", sim76xx_init_thread_entry, (void *)device,
SIM76XX_THREAD_STACK_SIZE, SIM76XX_THREAD_PRIORITY, 20);
if (tid)
{
rt_thread_startup(tid);
}
else
{
LOG_E("create sim76xx device(%s) initialization thread failed.", device->name);
return -RT_ERROR;
}
#else
sim76xx_init_thread_entry(device);
#endif /* AT_DEVICE_SIM76XX_INIT_ASYN */
return RT_EOK;
}
int sim76xx_ping(int argc, char **argv)
{
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
if (argc != 2)
{
rt_kprintf("Please input: at_ping <host address>\n");
return -RT_ERROR;
}
device = at_device_get_first_initialized();
if (device == RT_NULL)
{
rt_kprintf("get first initialized sim76xx device failed.\n");
return -RT_ERROR;
}
resp = at_create_resp(64, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
rt_kprintf("no memory for sim76xx device(%s) response structure.\n", device->name);
return -RT_ENOMEM;
}
if (at_obj_exec_cmd(device->client, resp, "AT+CPING=\"%s\",1,4,64,1000,10000,255", argv[1]) < 0)
{
if (resp)
{
at_delete_resp(resp);
}
rt_kprintf("sim76xx device(%s) send ping commands error.\n", device->name);
return -RT_ERROR;
}
if (resp)
{
at_delete_resp(resp);
}
return RT_EOK;
}
int sim76xx_ifconfig(void)
{
at_response_t resp = RT_NULL;
char resp_arg[AT_CMD_MAX_LEN] = {0};
rt_err_t result = RT_EOK;
struct at_device *device = RT_NULL;
device = at_device_get_first_initialized();
if (device == RT_NULL)
{
rt_kprintf("get first initialized sim76xx device failed.\n");
return -RT_ERROR;
}
resp = at_create_resp(128, 2, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
rt_kprintf("no memory for sim76xx device(%s) response structure.\n", device->name);
return -RT_ENOMEM;
}
/* Show PDP address */
AT_SEND_CMD(device->client, resp, "AT+CGPADDR");
at_resp_parse_line_args_by_kw(resp, "+CGPADDR:", "+CGPADDR: %s", &resp_arg);
rt_kprintf("IP adress : %s\n", resp_arg);
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
#ifdef FINSH_USING_MSH
#include <finsh.h>
MSH_CMD_EXPORT_ALIAS(sim76xx_net_init, at_net_init, initialize AT network);
MSH_CMD_EXPORT_ALIAS(sim76xx_ping, at_ping, AT ping network host);
MSH_CMD_EXPORT_ALIAS(sim76xx_ifconfig, at_ifconfig, list the information of network interfaces);
#endif
static void urc_ping_func(struct at_client *client, const char *data, rt_size_t size)
{
static int icmp_seq = 0;
int i, j = 0;
int result, recv_len, time, ttl;
int sent, rcvd, lost, min, max, avg;
char dst_ip[16] = {0};
RT_ASSERT(data);
for (i = 0; i < size; i++)
{
if (*(data + i) == '.')
j++;
}
if (j != 0)
{
sscanf(data, "+CPING: %d,%[^,],%d,%d,%d", &result, dst_ip, &recv_len, &time, &ttl);
if (result == 1)
LOG_I("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms", recv_len, dst_ip, icmp_seq++, ttl, time);
}
else
{
sscanf(data, "+CPING: %d,%d,%d,%d,%d,%d,%d", &result, &sent, &rcvd, &lost, &min, &max, &avg);
if (result == 3)
LOG_I("%d sent %d received %d lost, min=%dms max=%dms average=%dms", sent, rcvd, lost, min, max, avg);
if (result == 2)
LOG_I("ping requst timeout");
}
}
/* sim76xx device URC table for the device control */
static struct at_urc urc_table[] =
{
{"+CPING:", "\r\n", urc_ping_func},
};
static int sim76xx_init(struct at_device *device)
{
struct at_device_sim76xx *sim76xx = (struct at_device_sim76xx *) device->user_data;
/* initialize AT client */
at_client_init(sim76xx->client_name, sim76xx->recv_line_num);
device->client = at_client_get(sim76xx->client_name);
if (device->client == RT_NULL)
{
LOG_E("sim76xx device(%s) initialize failed, get AT client(%s) failed.", sim76xx->device_name, sim76xx->client_name);
return -RT_ERROR;
}
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
#ifdef AT_USING_SOCKET
sim76xx_socket_init(device);
#endif
/* add sim76xx device to the netdev list */
device->netdev = sim76xx_netdev_add(sim76xx->device_name);
if (device->netdev == RT_NULL)
{
LOG_E("sim76xx device(%s) initialize failed, get network interface device failed.", sim76xx->device_name);
return -RT_ERROR;
}
/* initialize sim76xx pin configuration */
if (sim76xx->power_pin != -1 && sim76xx->power_status_pin != -1)
{
rt_pin_mode(sim76xx->power_pin, PIN_MODE_OUTPUT);
rt_pin_mode(sim76xx->power_status_pin, PIN_MODE_INPUT);
}
/* initialize sim76xx device network */
sim76xx_net_init(device);
return RT_EOK;
}
static int sim76xx_deinit(struct at_device *device)
{
// TODO netdev operation
device->is_init = RT_FALSE;
return RT_EOK;
}
static int sim76xx_control(struct at_device *device, int cmd, void *arg)
{
int result = -RT_ERROR;
RT_ASSERT(device);
switch (cmd)
{
case AT_DEVICE_CTRL_POWER_ON:
case AT_DEVICE_CTRL_POWER_OFF:
case AT_DEVICE_CTRL_RESET:
case AT_DEVICE_CTRL_LOW_POWER:
case AT_DEVICE_CTRL_SLEEP:
case AT_DEVICE_CTRL_WAKEUP:
case AT_DEVICE_CTRL_NET_CONN:
case AT_DEVICE_CTRL_NET_DISCONN:
case AT_DEVICE_CTRL_SET_WIFI_INFO:
case AT_DEVICE_CTRL_GET_SIGNAL:
case AT_DEVICE_CTRL_GET_GPS:
case AT_DEVICE_CTRL_GET_VER:
LOG_W("sim76xx not support the control command(%d).", cmd);
break;
default:
LOG_E("input error control command(%d).", cmd);
break;
}
return result;
}
const struct at_device_ops sim76xx_device_ops =
{
sim76xx_init,
sim76xx_deinit,
sim76xx_control,
};
static int sim76xx_device_class_register(void)
{
struct at_device_class *class = RT_NULL;
class = (struct at_device_class *)rt_calloc(1, sizeof(struct at_device_class));
if (class == RT_NULL)
{
LOG_E("no memory for sim76xx device class create.");
return -RT_ENOMEM;
}
/* fill sim76xx device class object */
#ifdef AT_USING_SOCKET
sim76xx_socket_class_register(class);
#endif
class->device_ops = &sim76xx_device_ops;
return at_device_class_register(class, AT_DEVICE_CLASS_SIM76XX);
}
INIT_DEVICE_EXPORT(sim76xx_device_class_register);
#endif /* AT_DEVICE_SIM76XX */

View File

@ -0,0 +1,66 @@
/*
* File : at_device_sim76xx.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-16 chenyong first version
*/
#ifndef __AT_DEVICE_SIM76XX_H__
#define __AT_DEVICE_SIM76XX_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <at_device.h>
/* The maximum number of sockets supported by the sim76xx device */
#define AT_DEVICE_SIM76XX_SOCKETS_NUM 10
struct at_device_sim76xx
{
char *device_name;
char *client_name;
int power_pin;
int power_status_pin;
size_t recv_line_num;
struct at_device device;
void *user_data;
};
#ifdef AT_USING_SOCKET
/* sim76xx device socket initialize */
int sim76xx_socket_init(struct at_device *device);
/* sim76xx device class socket register */
int sim76xx_socket_class_register(struct at_device_class *class);
#endif /* AT_USING_SOCKET */
#ifdef __cplusplus
}
#endif
#endif /* __AT_DEVICE_SIM76XX_H__ */

View File

@ -0,0 +1,882 @@
/*
* File : at_socket_sim76xx.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-12-22 thomasonegd first version
* 2019-03-06 thomasonegd fix udp connection.
* 2019-03-08 thomasonegd add power_on & power_off api
* 2019-05-14 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_sim76xx.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.skt"
#include <at_log.h>
#ifdef AT_DEVICE_USING_SIM76XX
#define SIM76XX_MODULE_SEND_MAX_SIZE 1500
#define SIM76XX_MAX_CONNECTIONS 10
/* set real event by current socket and current state */
#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event))
/* AT socket event type */
#define SIM76XX_EVENT_CONN_OK (1L << 0)
#define SIM76XX_EVENT_SEND_OK (1L << 1)
#define SIM76XX_EVENT_RECV_OK (1L << 2)
#define SIM76XX_EVNET_CLOSE_OK (1L << 3)
#define SIM76XX_EVENT_CONN_FAIL (1L << 4)
#define SIM76XX_EVENT_SEND_FAIL (1L << 5)
static at_evt_cb_t at_evt_cb_set[] =
{
[AT_SOCKET_EVT_RECV] = NULL,
[AT_SOCKET_EVT_CLOSED] = NULL,
};
static char udp_ipstr[SIM76XX_MAX_CONNECTIONS][16];
static int udp_port[SIM76XX_MAX_CONNECTIONS];
/* unsolicited TCP/IP command<err> codes */
static void at_tcp_ip_errcode_parse(int result)
{
switch(result)
{
case 0 : LOG_D("%d : operation succeeded ", result); break;
case 1 : LOG_E("%d : UNetwork failure", result); break;
case 2 : LOG_E("%d : Network not opened", result); break;
case 3 : LOG_E("%d : Wrong parameter", result); break;
case 4 : LOG_E("%d : Operation not supported", result); break;
case 5 : LOG_E("%d : Failed to create socket", result); break;
case 6 : LOG_E("%d : Failed to bind socket", result); break;
case 7 : LOG_E("%d : TCP server is already listening", result); break;
case 8 : LOG_E("%d : Busy", result); break;
case 9 : LOG_E("%d : Sockets opened", result); break;
case 10 : LOG_E("%d : Timeout ", result); break;
case 11 : LOG_E("%d : DNS parse failed for AT+CIPOPEN", result); break;
case 255 : LOG_E("%d : Unknown error", result); break;
}
}
static int sim76xx_socket_event_send(struct at_device *device, uint32_t event)
{
return (int)rt_event_send(device->socket_event, event);
}
static int sim76xx_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option)
{
int result = RT_EOK;
rt_uint32_t recved;
result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved);
if (result != RT_EOK)
{
return -RT_ETIMEOUT;
}
return recved;
}
/**
* close socket by AT commands.
*
* @param current socket
*
* @return 0: close socket success
* -1: send AT commands error
* -2: wait socket event timeout
* -5: no memory
*/
static int sim76xx_socket_close(struct at_socket *socket)
{
int result = RT_EOK;
int activated = 0;
uint8_t lnk_stat[10] = {0};
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
resp = at_create_resp(64, 0, RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim76xx device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
rt_thread_mdelay(100);
/* check socket link_state */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE?") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "+CIPCLOSE:", "+CIPCLOSE: %d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
&lnk_stat[0], &lnk_stat[1], &lnk_stat[1], &lnk_stat[2], &lnk_stat[3], &lnk_stat[4],
&lnk_stat[5], &lnk_stat[6], &lnk_stat[7], &lnk_stat[8], &lnk_stat[9]) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (lnk_stat[device_socket])
{
/* close tcp or udp socket if connected */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE=%d", device_socket) < 0)
{
result = -RT_ERROR;
goto __exit;
}
}
/* check the network open or not */
if (at_obj_exec_cmd(device->client, resp, "AT+NETOPEN?") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "+NETOPEN:", "+NETOPEN: %d", &activated) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (activated)
{
/* if already open,then close it */
if (at_obj_exec_cmd(device->client, resp, "AT+NETCLOSE") < 0)
{
result = -RT_ERROR;
goto __exit;
}
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* open packet network
*/
static int sim76xx_network_socket_open(struct at_socket *socket)
{
int result = RT_EOK, activated = 0;
at_response_t resp = RT_NULL;
struct at_device *device = (struct at_device *) socket->device;
resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim76xx device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
/* check the network open or not */
if (at_obj_exec_cmd(device->client, resp, "AT+NETOPEN?") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "+NETOPEN:", "+NETOPEN: %d", &activated) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (activated)
{
/* network socket is already opened */
goto __exit;
}
else
{
/* if not opened the open it */
if (at_obj_exec_cmd(device->client, resp, "AT+NETOPEN") < 0)
{
result = -RT_ERROR;
goto __exit;
}
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* create TCP/UDP client or server connect by AT commands.
*
* @param socket current socket
* @param ip server or client IP address
* @param port server or client port
* @param type connect socket type(tcp, udp)
* @param is_client connection is client
*
* @return 0: connect success
* -1: connect failed, send commands error or type error
* -2: wait socket event timeout
* -5: no memory
*/
static int sim76xx_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client)
{
int result = RT_EOK, event_result = 0;
rt_bool_t retryed = RT_FALSE;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
rt_mutex_t lock = device->client->lock;
RT_ASSERT(ip);
RT_ASSERT(port >= 0);
resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim76xx device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
rt_mutex_take(lock, RT_WAITING_FOREVER);
__retry:
if (is_client)
{
/* open network socket first(AT+NETOPEN) */
sim76xx_network_socket_open(socket);
switch (type)
{
case AT_SOCKET_TCP:
/* send AT commands to connect TCP server */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPOPEN=%d,\"TCP\",\"%s\",%d", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
}
break;
case AT_SOCKET_UDP:
if (at_obj_exec_cmd(device->client, resp, "AT+CIPOPEN=%d,\"UDP\",,,%d", device_socket, port) < 0)
{
result = -RT_ERROR;
}
strcpy(udp_ipstr[device_socket], ip);
udp_port[device_socket] = port;
break;
default:
LOG_E("Not supported connect type : %d.", type);
result = -RT_ERROR;
goto __exit;
}
}
/* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/
if (sim76xx_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0)
{
LOG_E("sim76xx device(%s) socket(%d) connect failed, wait connect result timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
event_result = sim76xx_socket_event_recv(device, SIM76XX_EVENT_CONN_OK | SIM76XX_EVENT_CONN_FAIL,
1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR);
if (event_result < 0)
{
LOG_E("sim76xx device(%s) socket(%d) connect failed, wait connect OK|FAIL timeout.", device->name, socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & SIM76XX_EVENT_CONN_FAIL)
{
if (!retryed)
{
LOG_E("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket);
if (sim76xx_socket_close(socket) < 0)
{
result = -RT_ERROR;
goto __exit;
}
retryed = RT_TRUE;
goto __retry;
}
LOG_E("socket (%d) connect failed, failed to establish a connection.", socket);
result = -RT_ERROR;
goto __exit;
}
if (result != RT_EOK && !retryed)
{
LOG_D("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket);
if (sim76xx_socket_close(socket) < 0)
{
goto __exit;
}
retryed = RT_TRUE;
result = RT_EOK;
goto __retry;
}
__exit:
rt_mutex_release(lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* send data to server or client by AT commands.
*
* @param socket current socket
* @param buff send buffer
* @param bfsz send buffer size
* @param type connect socket type(tcp, udp)
*
* @return >=0: the size of send success
* -1: send AT commands error or send data error
* -2: waited socket event timeout
* -5: no memory
*/
static int sim76xx_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type)
{
int result = RT_EOK;
int event_result = 0;
size_t cur_pkt_size = 0, sent_size = 0;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
struct at_device_sim76xx *sim76xx = (struct at_device_sim76xx *) device->user_data;
rt_mutex_t lock = device->client->lock;
RT_ASSERT(buff);
RT_ASSERT(bfsz > 0);
resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim76xx device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
rt_mutex_take(lock, RT_WAITING_FOREVER);
/* set current socket for send URC event */
sim76xx->user_data = (void *) device_socket;
/* set AT client end sign to deal with '>' sign.*/
at_obj_set_end_sign(device->client, '>');
while (sent_size < bfsz)
{
if (bfsz - sent_size < SIM76XX_MODULE_SEND_MAX_SIZE)
{
cur_pkt_size = bfsz - sent_size;
}
else
{
cur_pkt_size = SIM76XX_MODULE_SEND_MAX_SIZE;
}
switch (socket->type)
{
case AT_SOCKET_TCP:
/* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d", device_socket, cur_pkt_size) < 0)
{
result = -RT_ERROR;
goto __exit;
}
break;
case AT_SOCKET_UDP:
/* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d,\"%s\",%d",
device_socket, cur_pkt_size, udp_ipstr[device_socket], udp_port[device_socket]) < 0)
{
result = -RT_ERROR;
goto __exit;
}
break;
}
/* send the real data to server or client */
result = (int) at_client_send(buff + sent_size, cur_pkt_size);
if (result == 0)
{
result = -RT_ERROR;
goto __exit;
}
/* waiting result event from AT URC */
if (sim76xx_socket_event_recv(device, SET_EVENT(device_socket, 0), 5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0)
{
LOG_E("sim76xx device(%s) socket (%d) send failed, wait connect result timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
event_result = sim76xx_socket_event_recv(device, SIM76XX_EVENT_SEND_OK | SIM76XX_EVENT_SEND_FAIL,
5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR);
if (event_result < 0)
{
LOG_E("sim76xx device(%s) socket(%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & SIM76XX_EVENT_SEND_FAIL)
{
LOG_E("sim76xx device(%s) socket(%d) send failed.", device->name, device_socket);
result = -RT_ERROR;
goto __exit;
}
if (type == AT_SOCKET_TCP)
{
//cur_pkt_size = cur_send_bfsz;
}
sent_size += cur_pkt_size;
}
__exit:
/* reset the end sign for data */
at_obj_set_end_sign(device->client, 0);
rt_mutex_release(lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* domain resolve by AT commands.
*
* @param name domain name
* @param ip parsed IP address, it's length must be 16
*
* @return 0: domain resolve success
* -2: wait socket event timeout
* -5: no memory
*/
static int sim76xx_domain_resolve(const char *name, char ip[16])
{
#define RESOLVE_RETRY 5
int i, result = RT_EOK;
char domain[32] = {0};
char domain_ip[16] = {0};
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(name);
RT_ASSERT(ip);
device = at_device_get_first_initialized();
if (device == RT_NULL)
{
LOG_E("get first initialized sim76xx device failed.");
return -RT_ERROR;
}
resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim76xx device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
for (i = 0; i < RESOLVE_RETRY; i++)
{
if (at_obj_exec_cmd(device->client, resp, "AT+CDNSGIP=\"%s\"", name) < 0)
{
rt_thread_mdelay(200);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
/* parse the third line of response data, get the IP address */
/* +CDNSGIP: 1,"www.baidu.com","14.215.177.39" */
if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP:", "+CDNSGIP: 1,%[^,],\"%[^\"]", domain, domain_ip) < 0)
{
rt_thread_mdelay(200);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
if (rt_strlen(domain_ip) < 8)
{
rt_thread_mdelay(200);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
else
{
rt_strncpy(ip, domain_ip, 15);
ip[15] = '\0';
break;
}
}
if (i == RESOLVE_RETRY)
{
result = -RT_ERROR;
}
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* set AT socket event notice callback
*
* @param event notice event
* @param cb notice callback
*/
static void sim76xx_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb)
{
if (event < (sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])))
{
at_evt_cb_set[event] = cb;
}
}
static void urc_send_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0, rqst_size, cnf_size;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get sim76xx device by client name(%s) failed.", client_name);
return;
}
sscanf(data, "+CIPSEND: %d,%d,%d", &device_socket, &rqst_size, &cnf_size);
//cur_send_bfsz = cnf_size;
sim76xx_socket_event_send(device, SET_EVENT(device_socket, SIM76XX_EVENT_SEND_OK));
}
static void urc_ping_func(struct at_client *client, const char *data, rt_size_t size)
{
static int icmp_seq = 0;
int i, j = 0;
int result, recv_len, time, ttl;
int sent, rcvd, lost, min, max, avg;
char dst_ip[16] = {0};
RT_ASSERT(data);
for (i = 0; i < size; i++)
{
if (*(data + i) == '.')
j++;
}
if (j != 0)
{
sscanf(data, "+CPING: %d,%[^,],%d,%d,%d", &result, dst_ip, &recv_len, &time, &ttl);
if (result == 1)
LOG_I("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms", recv_len, dst_ip, icmp_seq++, ttl, time);
}
else
{
sscanf(data, "+CPING: %d,%d,%d,%d,%d,%d,%d", &result, &sent, &rcvd, &lost, &min, &max, &avg);
if (result == 3)
LOG_I("%d sent %d received %d lost, min=%dms max=%dms average=%dms", sent, rcvd, lost, min, max, avg);
if (result == 2)
LOG_I("ping requst timeout");
}
}
static void urc_connect_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0, result = 0;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get sim76xx device by client name(%s) failed.", client_name);
return;
}
sscanf(data, "+CIPOPEN: %d,%d", &device_socket, &result);
if (result == 0)
{
sim76xx_socket_event_send(device, SET_EVENT(device_socket, SIM76XX_EVENT_CONN_OK));
}
else
{
at_tcp_ip_errcode_parse(result);
sim76xx_socket_event_send(device, SET_EVENT(device_socket, SIM76XX_EVENT_CONN_FAIL));
}
}
static void urc_close_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0, reason = 0;
struct at_socket *socket = RT_NULL;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get sim76xx device by client name(%s) failed.", device->name);
return;
}
sscanf(data, "+IPCLOSE %d,%d", &device_socket, &reason);
switch (reason)
{
case 0:
LOG_E("socket is closed by local,active");
break;
case 1:
LOG_E("socket is closed by remote,passive");
break;
case 2:
LOG_E("socket is closed for sending timeout");
break;
}
/* get AT socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the socket is disconnect by remote */
if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED])
{
at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0);
}
}
static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size)
{
rt_size_t bfsz = 0, temp_size = 0;
rt_int32_t timeout;
char *recv_buf = RT_NULL, temp[8] = {0};
int device_socket = 0;
struct at_socket *socket = RT_NULL;
struct at_device *device = RT_NULL;
struct at_device_sim76xx *sim76xx = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get sim76xx device by client name(%s) failed.", client_name);
return;
}
sim76xx = (struct at_device_sim76xx *) device->user_data;
device_socket = (int) sim76xx->user_data;
/* get the current socket and receive buffer size by receive data */
sscanf(data, "+IPD%d:", (int *)&bfsz);
/* get receive timeout by receive buffer length */
timeout = bfsz * 10;
if (bfsz == 0)
return;
recv_buf = (char *) rt_calloc(1, bfsz);
if (recv_buf == RT_NULL)
{
LOG_E("no memory for sim76xx device(%s) URC receive buffer(%d).", device->name, bfsz);
/* read and clean the coming data */
while (temp_size < bfsz)
{
if (bfsz - temp_size > sizeof(temp))
{
at_client_obj_recv(client, temp, sizeof(temp), timeout);
}
else
{
at_client_obj_recv(client, temp, bfsz - temp_size, timeout);
}
temp_size += sizeof(temp);
}
return;
}
/* sync receive data */
if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz)
{
LOG_E("sim76xx device(%s) receive size(%d) data failed.", device->name, bfsz);
rt_free(recv_buf);
return;
}
/* get AT socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the receive buffer and buffer size */
if (at_evt_cb_set[AT_SOCKET_EVT_RECV])
{
at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz);
}
}
static struct at_urc urc_table[] =
{
{"+CIPSEND:", "\r\n", urc_send_func},
{"+CIPOPEN:", "\r\n", urc_connect_func},
{"+CPING:", "\r\n", urc_ping_func},
{"+IPCLOSE", "\r\n", urc_close_func},
{"+IPD", "\r\n", urc_recv_func},
};
int sim76xx_connect(int argc, char **argv)
{
int32_t port;
if (argc != 3)
{
rt_kprintf("Please input: at_connect <host address>\n");
return -RT_ERROR;
}
sscanf(argv[2], "%d", &port);
sim76xx_socket_connect(at_get_socket(0), argv[1], port, AT_SOCKET_TCP, 1);
return RT_EOK;
}
int sim76xx_close(int argc, char **argv)
{
if (sim76xx_socket_close(at_get_socket(0)) < 0)
{
rt_kprintf("sim76xx_socket_close fail\n");
}
else
{
rt_kprintf("sim76xx_socket_closeed\n");
}
return RT_EOK;
}
int sim76xx_send(int argc, char **argv)
{
const char *buff = "1234567890\n";
if (sim76xx_socket_send(at_get_socket(0), buff, 11, AT_SOCKET_TCP) < 0)
{
rt_kprintf("sim76xx_socket_send fail\n");
}
return RT_EOK;
}
int sim76xx_domain(int argc, char **argv)
{
char ip[16];
if (sim76xx_domain_resolve("www.baidu.com", ip) < 0)
{
rt_kprintf("sim76xx_socket_send fail\n");
}
else
{
rt_kprintf("baidu.com : %s\n", ip);
}
return RT_EOK;
}
#ifdef FINSH_USING_MSH
#include <finsh.h>
MSH_CMD_EXPORT_ALIAS(sim76xx_connect, at_connect, AT connect network host);
MSH_CMD_EXPORT_ALIAS(sim76xx_close, at_close, AT close a socket);
MSH_CMD_EXPORT_ALIAS(sim76xx_send, at_send, AT send a pack);
MSH_CMD_EXPORT_ALIAS(sim76xx_domain, at_domain, AT domain resolve);
#endif
static const struct at_socket_ops sim76xx_socket_ops =
{
sim76xx_socket_connect,
sim76xx_socket_close,
sim76xx_socket_send,
sim76xx_domain_resolve,
sim76xx_socket_set_event_cb,
};
int sim76xx_socket_init(struct at_device *device)
{
RT_ASSERT(device);
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
return RT_EOK;
}
int sim76xx_socket_class_register(struct at_device_class *class)
{
RT_ASSERT(class);
class->socket_num = AT_DEVICE_SIM76XX_SOCKETS_NUM;
class->socket_ops = &sim76xx_socket_ops;
return RT_EOK;
}
#endif /* AT_DEVICE_SIM76XX */

View File

@ -0,0 +1,971 @@
/*
* File : at_socket_sim800c.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-12 malongwei first version
* 2019-05-13 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <at_device_sim800c.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.dev"
#include <at_log.h>
#ifdef AT_DEVICE_USING_SIM800C
#define SIM800C_WAIT_CONNECT_TIME 5000
#define SIM800C_THREAD_STACK_SIZE 1024
#define SIM800C_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2)
/* AT+CSTT command default*/
static char *CSTT_CHINA_MOBILE = "AT+CSTT=\"CMNET\"";
static char *CSTT_CHINA_UNICOM = "AT+CSTT=\"UNINET\"";
static char *CSTT_CHINA_TELECOM = "AT+CSTT=\"CTNET\"";
static void sim800c_power_on(struct at_device *device)
{
struct at_device_sim800c *sim800c = RT_NULL;
sim800c = (struct at_device_sim800c *) device->user_data;
/* not nead to set pin configuration for m26 device power on */
if (sim800c->power_pin == -1 || sim800c->power_status_pin == -1)
{
return;
}
if (rt_pin_read(sim800c->power_status_pin) == PIN_HIGH)
{
return;
}
rt_pin_write(sim800c->power_pin, PIN_HIGH);
while (rt_pin_read(sim800c->power_status_pin) == PIN_LOW)
{
rt_thread_mdelay(10);
}
rt_pin_write(sim800c->power_pin, PIN_LOW);
}
static void sim800c_power_off(struct at_device *device)
{
struct at_device_sim800c *sim800c = RT_NULL;
sim800c = (struct at_device_sim800c *) device->user_data;
/* not nead to set pin configuration for m26 device power on */
if (sim800c->power_pin == -1 || sim800c->power_status_pin == -1)
{
return;
}
if (rt_pin_read(sim800c->power_status_pin) == PIN_LOW)
{
return;
}
rt_pin_write(sim800c->power_pin, PIN_HIGH);
while (rt_pin_read(sim800c->power_status_pin) == PIN_HIGH)
{
rt_thread_mdelay(10);
}
rt_pin_write(sim800c->power_pin, PIN_LOW);
}
/* ============================= sim76xx network interface operations ============================= */
/* set sim800c network interface device status and address information */
static int sim800c_netdev_set_info(struct netdev *netdev)
{
#define SIM800C_IEMI_RESP_SIZE 32
#define SIM800C_IPADDR_RESP_SIZE 32
#define SIM800C_DNS_RESP_SIZE 96
#define SIM800C_INFO_RESP_TIMO rt_tick_from_millisecond(300)
int result = RT_EOK;
ip_addr_t addr;
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
if (netdev == RT_NULL)
{
LOG_E("input network interface device is NULL.");
return -RT_ERROR;
}
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
/* set network interface device status */
netdev_low_level_set_status(netdev, RT_TRUE);
netdev_low_level_set_link_status(netdev, RT_TRUE);
netdev_low_level_set_dhcp_status(netdev, RT_TRUE);
resp = at_create_resp(SIM800C_IEMI_RESP_SIZE, 0, SIM800C_INFO_RESP_TIMO);
if (resp == RT_NULL)
{
LOG_E("sim800c device(%s) set IP address failed, no memory for response object.", device->name);
result = -RT_ENOMEM;
goto __exit;
}
/* set network interface device hardware address(IEMI) */
{
#define SIM800C_NETDEV_HWADDR_LEN 8
#define SIM800C_IEMI_LEN 15
char iemi[SIM800C_IEMI_LEN] = {0};
int i = 0, j = 0;
/* send "AT+GSN" commond to get device IEMI */
if (at_obj_exec_cmd(device->client, resp, "AT+GSN") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args(resp, 2, "%s", iemi) <= 0)
{
LOG_E("sim800c device(%s) prase \"AT+GSN\" commands resposne data error.", device->name);
result = -RT_ERROR;
goto __exit;
}
LOG_D("sim800c device(%s) IEMI number: %s", device->name, iemi);
netdev->hwaddr_len = SIM800C_NETDEV_HWADDR_LEN;
/* get hardware address by IEMI */
for (i = 0, j = 0; i < SIM800C_NETDEV_HWADDR_LEN && j < SIM800C_IEMI_LEN; i++, j += 2)
{
if (j != SIM800C_IEMI_LEN - 1)
{
netdev->hwaddr[i] = (iemi[j] - '0') * 10 + (iemi[j + 1] - '0');
}
else
{
netdev->hwaddr[i] = (iemi[j] - '0');
}
}
}
/* set network interface device IP address */
{
#define IP_ADDR_SIZE_MAX 16
char ipaddr[IP_ADDR_SIZE_MAX] = {0};
at_resp_set_info(resp, SIM800C_IPADDR_RESP_SIZE, 2, SIM800C_INFO_RESP_TIMO);
/* send "AT+CIFSR" commond to get IP address */
if (at_obj_exec_cmd(device->client, resp, "AT+CIFSR") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, ".", "%s", ipaddr) <= 0)
{
LOG_E("sim800c device(%s) prase \"AT+CIFSR\" commands resposne data error!", device->name);
result = -RT_ERROR;
goto __exit;
}
LOG_D("sim800c device(%s) IP address: %s", device->name, ipaddr);
/* set network interface address information */
inet_aton(ipaddr, &addr);
netdev_low_level_set_ipaddr(netdev, &addr);
}
/* set network interface device dns server */
{
#define DNS_ADDR_SIZE_MAX 16
char dns_server1[DNS_ADDR_SIZE_MAX] = {0}, dns_server2[DNS_ADDR_SIZE_MAX] = {0};
at_resp_set_info(resp, SIM800C_DNS_RESP_SIZE, 0, SIM800C_INFO_RESP_TIMO);
/* send "AT+CDNSCFG?" commond to get DNS servers address */
if (at_obj_exec_cmd(device->client, resp, "AT+CDNSCFG?") < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "PrimaryDns:", "PrimaryDns:%s", dns_server1) <= 0 ||
at_resp_parse_line_args_by_kw(resp, "SecondaryDns:", "SecondaryDns:%s", dns_server2) <= 0)
{
LOG_E("Prase \"AT+CDNSCFG?\" commands resposne data error!");
result = -RT_ERROR;
goto __exit;
}
LOG_D("sim800c device(%s) primary DNS server address: %s", device->name, dns_server1);
LOG_D("sim800c device(%s) secondary DNS server address: %s", device->name, dns_server2);
inet_aton(dns_server1, &addr);
netdev_low_level_set_dns_server(netdev, 0, &addr);
inet_aton(dns_server2, &addr);
netdev_low_level_set_dns_server(netdev, 1, &addr);
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static void check_link_status_entry(void *parameter)
{
#define SIM800C_LINK_STATUS_OK 1
#define SIM800C_LINK_RESP_SIZE 64
#define SIM800C_LINK_RESP_TIMO (3 * RT_TICK_PER_SECOND)
#define SIM800C_LINK_DELAY_TIME (30 * RT_TICK_PER_SECOND)
at_response_t resp = RT_NULL;
int result_code, link_status;
struct at_device *device = RT_NULL;
struct netdev *netdev = (struct netdev *)parameter;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name);
return;
}
resp = at_create_resp(SIM800C_LINK_RESP_SIZE, 0, SIM800C_LINK_RESP_TIMO);
if (resp == RT_NULL)
{
LOG_E("sim800c device(%s) set check link status failed, no memory for response object.", device->name);
return;
}
while (1)
{
/* send "AT+CGREG?" commond to check netweork interface device link status */
if (at_obj_exec_cmd(device->client, resp, "AT+CGREG?") < 0)
{
rt_thread_mdelay(SIM800C_LINK_DELAY_TIME);
continue;
}
link_status = -1;
at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %d,%d", &result_code, &link_status);
/* check the network interface device link status */
if ((SIM800C_LINK_STATUS_OK == link_status) != netdev_is_link_up(netdev))
{
netdev_low_level_set_link_status(netdev, (SIM800C_LINK_STATUS_OK == link_status));
}
rt_thread_mdelay(SIM800C_LINK_DELAY_TIME);
}
}
static int sim800c_netdev_check_link_status(struct netdev *netdev)
{
#define SIM800C_LINK_THREAD_TICK 20
#define SIM800C_LINK_THREAD_STACK_SIZE 512
#define SIM800C_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2)
rt_thread_t tid;
char tname[RT_NAME_MAX] = {0};
if (netdev == RT_NULL)
{
LOG_E("input network interface device is NULL.\n");
return -RT_ERROR;
}
rt_snprintf(tname, RT_NAME_MAX, "%s_link", netdev->name);
tid = rt_thread_create(tname, check_link_status_entry, (void *) netdev,
SIM800C_LINK_THREAD_STACK_SIZE, SIM800C_LINK_THREAD_PRIORITY, SIM800C_LINK_THREAD_TICK);
if (tid)
{
rt_thread_startup(tid);
}
return RT_EOK;
}
static int sim800c_net_init(struct at_device *device);
static int sim800c_netdev_set_up(struct netdev *netdev)
{
struct at_device *device = RT_NULL;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
if (device->is_init == RT_FALSE)
{
sim800c_net_init(device);
device->is_init = RT_TRUE;
netdev_low_level_set_status(netdev, RT_TRUE);
LOG_D("the network interface device(%s) set up status.", netdev->name);
}
return RT_EOK;
}
static int sim800c_netdev_set_down(struct netdev *netdev)
{
struct at_device *device = RT_NULL;
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
if (device->is_init == RT_TRUE)
{
sim800c_power_off(device);
device->is_init = RT_FALSE;
netdev_low_level_set_status(netdev, RT_FALSE);
LOG_D("the network interface device(%s) set down status.", netdev->name);
}
return RT_EOK;
}
static int sim800c_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server)
{
#define SIM800C_DNS_RESP_LEN 8
#define SIM800C_DNS_RESP_TIMEO rt_tick_from_millisecond(300)
int result = RT_EOK;
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(netdev);
RT_ASSERT(dns_server);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
resp = at_create_resp(SIM800C_DNS_RESP_LEN, 0, SIM800C_DNS_RESP_TIMEO);
if (resp == RT_NULL)
{
LOG_D("sim800c set dns server failed, no memory for response object.");
result = -RT_ENOMEM;
goto __exit;
}
/* send "AT+CDNSCFG=<pri_dns>[,<sec_dns>]" commond to set dns servers */
if (at_obj_exec_cmd(device->client, resp, "AT+CDNSCFG=\"%s\"", inet_ntoa(*dns_server)) < 0)
{
result = -RT_ERROR;
goto __exit;
}
netdev_low_level_set_dns_server(netdev, dns_num, dns_server);
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
static int sim800c_ping_domain_resolve(struct at_device *device, const char *name, char ip[16])
{
int result = RT_EOK;
char recv_ip[16] = { 0 };
at_response_t resp = RT_NULL;
/* The maximum response time is 14 seconds, affected by network status */
resp = at_create_resp(128, 4, 14 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim800c device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
if (at_obj_exec_cmd(device->client, resp, "AT+CDNSGIP=\"%s\"", name) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* parse the third line of response data, get the IP address */
if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP:", "%*[^,],%*[^,],\"%[^\"]", recv_ip) < 0)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
}
if (rt_strlen(recv_ip) < 8)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
}
else
{
rt_strncpy(ip, recv_ip, 15);
ip[15] = '\0';
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
#ifdef NETDEV_USING_PING
static int sim800c_netdev_ping(struct netdev *netdev, const char *host,
size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp)
{
#define SIM800C_PING_RESP_SIZE 128
#define SIM800C_PING_IP_SIZE 16
#define SIM800C_PING_TIMEO (5 * RT_TICK_PER_SECOND)
#define SIM800C_PING_ERR_TIME 600
#define SIM800C_PING_ERR_TTL 255
int result = RT_EOK;
int response, time, ttl, i, err_code = 0;
char ip_addr[SIM800C_PING_IP_SIZE] = {0};
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(netdev);
RT_ASSERT(host);
RT_ASSERT(ping_resp);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name);
return -RT_ERROR;
}
for (i = 0; i < rt_strlen(host) && !isalpha(host[i]); i++);
if (i < strlen(host))
{
/* check domain name is usable */
if (sim800c_ping_domain_resolve(device, host, ip_addr) < 0)
{
return -RT_ERROR;
}
rt_memset(ip_addr, 0x00, SIM800C_PING_IP_SIZE);
}
resp = at_create_resp(SIM800C_PING_RESP_SIZE, 0, SIM800C_PING_TIMEO);
if (resp == RT_NULL)
{
LOG_E("sim800c device(%s) set dns server failed, no memory for response object.", device->name);
result = -RT_ERROR;
goto __exit;
}
/* domain name prase error options */
if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP: 0", "+CDNSGIP: 0,%d", &err_code) > 0)
{
/* 3 - network error, 8 - dns common error */
if (err_code == 3 || err_code == 8)
{
result = -RT_ERROR;
goto __exit;
}
}
/* send "AT+CIPPING=<IP addr>[,<retryNum>[,<dataLen>[,<timeout>[,<ttl>]]]]" commond to send ping request */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPPING=%s,1,%d,%d,64",
host, data_len, SIM800C_PING_TIMEO / (RT_TICK_PER_SECOND / 10)) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "+CIPPING:", "+CIPPING:%d,\"%[^\"]\",%d,%d",
&response, ip_addr, &time, &ttl) <= 0)
{
result = -RT_ERROR;
goto __exit;
}
/* the ping request timeout expires, the response time settting to 600 and ttl setting to 255 */
if (time == SIM800C_PING_ERR_TIME && ttl == SIM800C_PING_ERR_TTL)
{
result = -RT_ETIMEOUT;
goto __exit;
}
inet_aton(ip_addr, &(ping_resp->ip_addr));
ping_resp->data_len = data_len;
/* reply time, in units of 100 ms */
ping_resp->ticks = time * 100;
ping_resp->ttl = ttl;
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
#endif /* NETDEV_USING_PING */
#ifdef NETDEV_USING_NETSTAT
void sim800c_netdev_netstat(struct netdev *netdev)
{
// TODO netstat support
}
#endif /* NETDEV_USING_NETSTAT */
const struct netdev_ops sim800c_netdev_ops =
{
sim800c_netdev_set_up,
sim800c_netdev_set_down,
RT_NULL, /* not support set ip, netmask, gatway address */
sim800c_netdev_set_dns_server,
RT_NULL, /* not support set DHCP status */
#ifdef NETDEV_USING_PING
sim800c_netdev_ping,
#endif
#ifdef NETDEV_USING_NETSTAT
sim800c_netdev_netstat,
#endif
};
static struct netdev *sim800c_netdev_add(const char *netdev_name)
{
#define SIM800C_NETDEV_MTU 1500
struct netdev *netdev = RT_NULL;
RT_ASSERT(netdev_name);
netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev));
if (netdev == RT_NULL)
{
LOG_E("no memory for sim800c device(%s) netdev structure.", netdev_name);
return RT_NULL;
}
netdev->mtu = SIM800C_NETDEV_MTU;
netdev->ops = &sim800c_netdev_ops;
#ifdef SAL_USING_AT
extern int sal_at_netdev_set_pf_info(struct netdev *netdev);
/* set the network interface socket/netdb operations */
sal_at_netdev_set_pf_info(netdev);
#endif
netdev_register(netdev, netdev_name, RT_NULL);
return netdev;
}
/* ============================= sim76xx device operations ============================= */
#define AT_SEND_CMD(client, resp, resp_line, timeout, cmd) \
do { \
(resp) = at_resp_set_info((resp), 128, (resp_line), rt_tick_from_millisecond(timeout)); \
if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \
{ \
result = -RT_ERROR; \
goto __exit; \
} \
} while(0) \
/* init for sim800c */
static void sim800c_init_thread_entry(void *parameter)
{
#define INIT_RETRY 5
#define CPIN_RETRY 10
#define CSQ_RETRY 10
#define CREG_RETRY 10
#define CGREG_RETRY 20
int i, qimux, retry_num = INIT_RETRY;
char parsed_data[10] = {0};
rt_err_t result = RT_EOK;
at_response_t resp = RT_NULL;
struct at_device *device = (struct at_device *)parameter;
struct at_client *client = device->client;
resp = at_create_resp(128, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for sim800c device(%s) response structure.", device->name);
return;
}
LOG_D("start initializing the sim800c device(%s)", device->name);
while (retry_num--)
{
rt_memset(parsed_data, 0, sizeof(parsed_data));
rt_thread_mdelay(500);
sim800c_power_on(device);
rt_thread_mdelay(1000);
/* wait sim800c startup finish */
if (at_client_obj_wait_connect(client, SIM800C_WAIT_CONNECT_TIME))
{
result = -RT_ETIMEOUT;
goto __exit;
}
/* disable echo */
AT_SEND_CMD(client, resp, 0, 300, "ATE0");
/* get module version */
AT_SEND_CMD(client, resp, 0, 300, "ATI");
/* show module version */
for (i = 0; i < (int)resp->line_counts - 1; i++)
{
LOG_D("%s", at_resp_get_line(resp, i + 1));
}
/* check SIM card */
for (i = 0; i < CPIN_RETRY; i++)
{
AT_SEND_CMD(client, resp, 2, 5 * RT_TICK_PER_SECOND, "AT+CPIN?");
if (at_resp_get_line_by_kw(resp, "READY"))
{
LOG_D("sim800c device(%s) SIM card detection success.", device->name);
break;
}
rt_thread_mdelay(1000);
}
if (i == CPIN_RETRY)
{
LOG_E("sim800c device(%s) SIM card detection failed.", device->name);
result = -RT_ERROR;
goto __exit;
}
/* waiting for dirty data to be digested */
rt_thread_mdelay(10);
/* check the GSM network is registered */
for (i = 0; i < CREG_RETRY; i++)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+CREG?");
at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data);
if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) ||
!strncmp(parsed_data, "0,5", sizeof(parsed_data)))
{
LOG_D("sim800c device(%s) GSM network is registered(%s),", device->name, parsed_data);
break;
}
rt_thread_mdelay(1000);
}
if (i == CREG_RETRY)
{
LOG_E("sim800c device(%s) GSM network is register failed(%s).", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
/* check the GPRS network is registered */
for (i = 0; i < CGREG_RETRY; i++)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+CGREG?");
at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data);
if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) ||
!strncmp(parsed_data, "0,5", sizeof(parsed_data)))
{
LOG_D("sim800c device(%s) GPRS network is registered(%s).", device->name, parsed_data);
break;
}
rt_thread_mdelay(1000);
}
if (i == CGREG_RETRY)
{
LOG_E("sim800c device(%s) GPRS network is register failed(%s).", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
/* check signal strength */
for (i = 0; i < CSQ_RETRY; i++)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+CSQ");
at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %s", &parsed_data);
if (strncmp(parsed_data, "99,99", sizeof(parsed_data)))
{
LOG_D("sim800c device(%s) signal strength: %s", device->name, parsed_data);
break;
}
rt_thread_mdelay(1000);
}
if (i == CSQ_RETRY)
{
LOG_E("sim800c device(%s) signal strength check failed (%s)", device->name, parsed_data);
result = -RT_ERROR;
goto __exit;
}
/* the device default response timeout is 40 seconds, but it set to 15 seconds is convenient to use. */
AT_SEND_CMD(client, resp, 2, 20 * 1000, "AT+CIPSHUT");
/* Set to multiple connections */
AT_SEND_CMD(client, resp, 0, 300, "AT+CIPMUX?");
at_resp_parse_line_args_by_kw(resp, "+CIPMUX:", "+CIPMUX: %d", &qimux);
if (qimux == 0)
{
AT_SEND_CMD(client, resp, 0, 300, "AT+CIPMUX=1");
}
AT_SEND_CMD(client, resp, 0, 300, "AT+COPS?");
at_resp_parse_line_args_by_kw(resp, "+COPS:", "+COPS: %*[^\"]\"%[^\"]", &parsed_data);
if (rt_strcmp(parsed_data, "CHINA MOBILE") == 0)
{
/* "CMCC" */
LOG_I("sim800c device(%s) network operator: %s", device->name, parsed_data);
AT_SEND_CMD(client, resp, 0, 300, CSTT_CHINA_MOBILE);
}
else if (rt_strcmp(parsed_data, "CHN-UNICOM") == 0)
{
/* "UNICOM" */
LOG_I("sim800c device(%s) network operator: %s", device->name, parsed_data);
AT_SEND_CMD(client, resp, 0, 300, CSTT_CHINA_UNICOM);
}
else if (rt_strcmp(parsed_data, "CHN-CT") == 0)
{
AT_SEND_CMD(client, resp, 0, 300, CSTT_CHINA_TELECOM);
/* "CT" */
LOG_I("sim800c device(%s) network operator: %s", device->name, parsed_data);
}
/* the device default response timeout is 150 seconds, but it set to 20 seconds is convenient to use. */
AT_SEND_CMD(client, resp, 0, 20 * 1000, "AT+CIICR");
AT_SEND_CMD(client, resp, 2, 300, "AT+CIFSR");
if (at_resp_get_line_by_kw(resp, "ERROR") != RT_NULL)
{
LOG_E("sim800c device(%s) get the local address failed.", device->name);
result = -RT_ERROR;
goto __exit;
}
result = RT_EOK;
__exit:
if (result == RT_EOK)
{
break;
}
else
{
/* power off the sim800c device */
sim800c_power_off(device);
rt_thread_mdelay(1000);
LOG_I("sim800c device(%s) initialize retry...", device->name);
}
}
if (resp)
{
at_delete_resp(resp);
}
if (result == RT_EOK)
{
/* set network interface device status and address information */
sim800c_netdev_set_info(device->netdev);
sim800c_netdev_check_link_status(device->netdev);
LOG_I("sim800c device(%s) network initialize success!", device->name);
}
else
{
LOG_E("sim800c device(%s) network initialize failed(%d)!", device->name, result);
}
}
static int sim800c_net_init(struct at_device *device)
{
#ifdef AT_DEVICE_SIM800C_INIT_ASYN
rt_thread_t tid;
tid = rt_thread_create("sim800c_net_init", sim800c_init_thread_entry, (void *)device,
SIM800C_THREAD_STACK_SIZE, SIM800C_THREAD_PRIORITY, 20);
if (tid)
{
rt_thread_startup(tid);
}
else
{
LOG_E("create sim800c device(%s) initialization thread failed.", device->name);
return -RT_ERROR;
}
#else
sim800c_init_thread_entry(device);
#endif /* AT_DEVICE_SIM800C_INIT_ASYN */
return RT_EOK;
}
static void urc_func(struct at_client *client, const char *data, rt_size_t size)
{
RT_ASSERT(data);
LOG_I("URC data : %.*s", size, data);
}
/* sim800c device URC table for the device control */
static const struct at_urc urc_table[] =
{
{"RDY", "\r\n", urc_func},
};
static int sim800c_init(struct at_device *device)
{
struct at_device_sim800c *sim800c = (struct at_device_sim800c *) device->user_data;
/* initialize AT client */
at_client_init(sim800c->client_name, sim800c->recv_line_num);
device->client = at_client_get(sim800c->client_name);
if (device->client == RT_NULL)
{
LOG_E("sim800c device(%s) initialize failed, get AT client(%s) failed.", sim800c->device_name, sim800c->client_name);
return -RT_ERROR;
}
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
#ifdef AT_USING_SOCKET
sim800c_socket_init(device);
#endif
/* add sim800c device to the netdev list */
device->netdev = sim800c_netdev_add(sim800c->device_name);
if (device->netdev == RT_NULL)
{
LOG_E("sim800c device(%s) initialize failed, get network interface device failed.", sim800c->device_name);
return -RT_ERROR;
}
/* initialize sim800c pin configuration */
if (sim800c->power_pin != -1 && sim800c->power_status_pin != -1)
{
rt_pin_mode(sim800c->power_pin, PIN_MODE_OUTPUT);
rt_pin_mode(sim800c->power_status_pin, PIN_MODE_INPUT);
}
/* initialize sim800c device network */
return sim800c_netdev_set_up(device->netdev);
}
static int sim800c_deinit(struct at_device *device)
{
return sim800c_netdev_set_down(device->netdev);
}
static int sim800c_control(struct at_device *device, int cmd, void *arg)
{
int result = -RT_ERROR;
RT_ASSERT(device);
switch (cmd)
{
case AT_DEVICE_CTRL_POWER_ON:
case AT_DEVICE_CTRL_POWER_OFF:
case AT_DEVICE_CTRL_RESET:
case AT_DEVICE_CTRL_LOW_POWER:
case AT_DEVICE_CTRL_SLEEP:
case AT_DEVICE_CTRL_WAKEUP:
case AT_DEVICE_CTRL_NET_CONN:
case AT_DEVICE_CTRL_NET_DISCONN:
case AT_DEVICE_CTRL_SET_WIFI_INFO:
case AT_DEVICE_CTRL_GET_SIGNAL:
case AT_DEVICE_CTRL_GET_GPS:
case AT_DEVICE_CTRL_GET_VER:
LOG_W("sim800c not support the control command(%d).", cmd);
break;
default:
LOG_E("input error control command(%d).", cmd);
break;
}
return result;
}
const struct at_device_ops sim800c_device_ops =
{
sim800c_init,
sim800c_deinit,
sim800c_control,
};
static int sim800c_device_class_register(void)
{
struct at_device_class *class = RT_NULL;
class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class));
if (class == RT_NULL)
{
LOG_E("no memory for sim800c device class create.");
return -RT_ENOMEM;
}
/* fill sim800c device class object */
#ifdef AT_USING_SOCKET
sim800c_socket_class_register(class);
#endif
class->device_ops = &sim800c_device_ops;
return at_device_class_register(class, AT_DEVICE_CLASS_SIM800C);
}
INIT_DEVICE_EXPORT(sim800c_device_class_register);
#endif /* AT_DEVICE_USING_SIM800C */

View File

@ -0,0 +1,66 @@
/*
* File : at_device_sim800c.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-16 chenyong first version
*/
#ifndef __AT_DEVICE_SIM800C_H__
#define __AT_DEVICE_SIM800C_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <at_device.h>
/* The maximum number of sockets supported by the sim800c device */
#define AT_DEVICE_SIM800C_SOCKETS_NUM 6
struct at_device_sim800c
{
char *device_name;
char *client_name;
int power_pin;
int power_status_pin;
size_t recv_line_num;
struct at_device device;
void *user_data;
};
#ifdef AT_USING_SOCKET
/* sim800c device socket initialize */
int sim800c_socket_init(struct at_device *device);
/* sim800c device class socket register */
int sim800c_socket_class_register(struct at_device_class *class);
#endif /* AT_USING_SOCKET */
#ifdef __cplusplus
}
#endif
#endif /* __AT_DEVICE_SIM800C_H__ */

View File

@ -0,0 +1,658 @@
/*
* File : at_socket_sim800c.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-06-12 malongwei first version
* 2019-05-13 chenyong multi AT socket client support
*/
#include <stdio.h>
#include <string.h>
#include <at_device_sim800c.h>
#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200
#error "This AT Client version is older, please check and update latest AT Client!"
#endif
#define LOG_TAG "at.skt"
#include <at_log.h>
#if defined(AT_DEVICE_USING_SIM800C) && defined(AT_USING_SOCKET)
#define SIM800C_MODULE_SEND_MAX_SIZE 1000
/* set real event by current socket and current state */
#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event))
/* AT socket event type */
#define SIM800C_EVENT_CONN_OK (1L << 0)
#define SIM800C_EVENT_SEND_OK (1L << 1)
#define SIM800C_EVENT_RECV_OK (1L << 2)
#define SIM800C_EVNET_CLOSE_OK (1L << 3)
#define SIM800C_EVENT_CONN_FAIL (1L << 4)
#define SIM800C_EVENT_SEND_FAIL (1L << 5)
static at_evt_cb_t at_evt_cb_set[] = {
[AT_SOCKET_EVT_RECV] = NULL,
[AT_SOCKET_EVT_CLOSED] = NULL,
};
static int sim800c_socket_event_send(struct at_device *device, uint32_t event)
{
return (int) rt_event_send(device->socket_event, event);
}
static int sim800c_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option)
{
int result = RT_EOK;
rt_uint32_t recved;
result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved);
if (result != RT_EOK)
{
return -RT_ETIMEOUT;
}
return recved;
}
/**
* close socket by AT commands.
*
* @param current socket
*
* @return 0: close socket success
* -1: send AT commands error
* -2: wait socket event timeout
* -5: no memory
*/
static int sim800c_socket_close(struct at_socket *socket)
{
uint32_t event = 0;
int result = RT_EOK;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
resp = at_create_resp(64, 0, rt_tick_from_millisecond(300));
if (resp == RT_NULL)
{
LOG_E("no memory for sim800c device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
/* clear socket close event */
event = SET_EVENT(device_socket, SIM800C_EVNET_CLOSE_OK);
sim800c_socket_event_recv(device, event, 0, RT_EVENT_FLAG_OR);
if (at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE=%d", device_socket) < 0)
{
result = -RT_ERROR;
goto __exit;
}
if (sim800c_socket_event_recv(device, event, rt_tick_from_millisecond(300*3), RT_EVENT_FLAG_AND) < 0)
{
LOG_E("sim800c device(%s) socket(%d) close failed, wait close OK timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* create TCP/UDP client or server connect by AT commands.
*
* @param socket current socket
* @param ip server or client IP address
* @param port server or client port
* @param type connect socket type(tcp, udp)
* @param is_client connection is client
*
* @return 0: connect success
* -1: connect failed, send commands error or type error
* -2: wait socket event timeout
* -5: no memory
*/
static int sim800c_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client)
{
uint32_t event = 0;
rt_bool_t retryed = RT_FALSE;
at_response_t resp = RT_NULL;
int result = RT_EOK, event_result = 0;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
RT_ASSERT(ip);
RT_ASSERT(port >= 0);
resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim800c device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
__retry:
/* clear socket connect event */
event = SET_EVENT(device_socket, SIM800C_EVENT_CONN_OK | SIM800C_EVENT_CONN_FAIL);
sim800c_socket_event_recv(device, event, 0, RT_EVENT_FLAG_OR);
if (is_client)
{
switch (type)
{
case AT_SOCKET_TCP:
/* send AT commands(eg: AT+QIOPEN=0,"TCP","x.x.x.x", 1234) to connect TCP server */
if (at_obj_exec_cmd(device->client, RT_NULL,
"AT+CIPSTART=%d,\"TCP\",\"%s\",%d", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
goto __exit;
}
break;
case AT_SOCKET_UDP:
if (at_obj_exec_cmd(device->client, RT_NULL,
"AT+CIPSTART=%d,\"UDP\",\"%s\",%d", device_socket, ip, port) < 0)
{
result = -RT_ERROR;
goto __exit;
}
break;
default:
LOG_E("sim800c device(%s) not supported connect type : %d.", device->name, type);
result = -RT_ERROR;
goto __exit;
}
}
/* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use */
if (sim800c_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0)
{
LOG_E("sim800c device(%s) socket(%d) connect failed, wait connect result timeout.",device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
event_result = sim800c_socket_event_recv(device,
SIM800C_EVENT_CONN_OK | SIM800C_EVENT_CONN_FAIL, 1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR);
if (event_result < 0)
{
LOG_E("sim800c device(%s) socket(%d) connect failed, wait connect OK|FAIL timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & SIM800C_EVENT_CONN_FAIL)
{
if (retryed == RT_FALSE)
{
LOG_D("sim800c device(%s) socket(%d) connect failed, maybe the socket was not be closed at the last time and now will retry.",
device->name, device_socket);
if (sim800c_socket_close(socket) < 0)
{
result = -RT_ERROR;
goto __exit;
}
retryed = RT_TRUE;
goto __retry;
}
LOG_E("sim800c device(%s) socket(%d) connect failed.", device->name, device_socket);
result = -RT_ERROR;
goto __exit;
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* send data to server or client by AT commands.
*
* @param socket current socket
* @param buff send buffer
* @param bfsz send buffer size
* @param type connect socket type(tcp, udp)
*
* @return >=0: the size of send success
* -1: send AT commands error or send data error
* -2: waited socket event timeout
* -5: no memory
*/
static int sim800c_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type)
{
uint32_t event = 0;
int result = RT_EOK, event_result = 0;
size_t cur_pkt_size = 0, sent_size = 0;
at_response_t resp = RT_NULL;
int device_socket = (int) socket->user_data;
struct at_device *device = (struct at_device *) socket->device;
rt_mutex_t lock = device->client->lock;
RT_ASSERT(buff);
resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim800c device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
rt_mutex_take(lock, RT_WAITING_FOREVER);
/* clear socket connect event */
event = SET_EVENT(device_socket, SIM800C_EVENT_SEND_OK | SIM800C_EVENT_SEND_FAIL);
sim800c_socket_event_recv(device, event, 0, RT_EVENT_FLAG_OR);
/* set AT client end sign to deal with '>' sign.*/
at_obj_set_end_sign(device->client, '>');
while (sent_size < bfsz)
{
if (bfsz - sent_size < SIM800C_MODULE_SEND_MAX_SIZE)
{
cur_pkt_size = bfsz - sent_size;
}
else
{
cur_pkt_size = SIM800C_MODULE_SEND_MAX_SIZE;
}
/* send the "AT+QISEND" commands to AT server than receive the '>' response on the first line. */
if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d", device_socket, cur_pkt_size) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* send the real data to server or client */
result = (int) at_client_obj_send(device->client, buff + sent_size, cur_pkt_size);
if (result == 0)
{
result = -RT_ERROR;
goto __exit;
}
/* waiting result event from AT URC */
if (sim800c_socket_event_recv(device, SET_EVENT(device_socket, 0), 15 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0)
{
LOG_E("sim800c device(%s) socket(%d) send failed, wait connect result timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* waiting OK or failed result */
event_result = sim800c_socket_event_recv(device,
SIM800C_EVENT_SEND_OK | SIM800C_EVENT_SEND_FAIL, 5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR);
if (event_result < 0)
{
LOG_E("simm800c device(%s) socket(%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket);
result = -RT_ETIMEOUT;
goto __exit;
}
/* check result */
if (event_result & SIM800C_EVENT_SEND_FAIL)
{
LOG_E("simm800c device(%s) socket(%d) send failed.",device->name, device_socket);
result = -RT_ERROR;
goto __exit;
}
sent_size += cur_pkt_size;
}
__exit:
/* reset the end sign for data conflict */
at_obj_set_end_sign(device->client, 0);
rt_mutex_release(lock);
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* domain resolve by AT commands.
*
* @param name domain name
* @param ip parsed IP address, it's length must be 16
*
* @return 0: domain resolve success
* -1: send AT commands error or response error
* -2: wait socket event timeout
* -5: no memory
*/
static int sim800c_domain_resolve(const char *name, char ip[16])
{
#define RESOLVE_RETRY 5
int i, result = RT_EOK;
char recv_ip[16] = { 0 };
at_response_t resp = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(name);
RT_ASSERT(ip);
device = at_device_get_first_initialized();
if (device == RT_NULL)
{
LOG_E("get first initialization sim800c device failed.");
return -RT_ERROR;
}
/* The maximum response time is 14 seconds, affected by network status */
resp = at_create_resp(128, 4, 14 * RT_TICK_PER_SECOND);
if (resp == RT_NULL)
{
LOG_E("no memory for sim800c device(%s) response structure.", device->name);
return -RT_ENOMEM;
}
for (i = 0; i < RESOLVE_RETRY; i++)
{
int err_code = 0;
if (at_obj_exec_cmd(device->client, resp, "AT+CDNSGIP=\"%s\"", name) < 0)
{
result = -RT_ERROR;
goto __exit;
}
/* domain name prase error options */
if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP: 0", "+CDNSGIP: 0,%d", &err_code) > 0)
{
/* 3 - network error, 8 - dns common error */
if (err_code == 3 || err_code == 8)
{
result = -RT_ERROR;
goto __exit;
}
}
/* parse the third line of response data, get the IP address */
if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP:", "%*[^,],%*[^,],\"%[^\"]", recv_ip) < 0)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
if (rt_strlen(recv_ip) < 8)
{
rt_thread_mdelay(100);
/* resolve failed, maybe receive an URC CRLF */
continue;
}
else
{
rt_thread_mdelay(10);
rt_strncpy(ip, recv_ip, 15);
ip[15] = '\0';
break;
}
}
__exit:
if (resp)
{
at_delete_resp(resp);
}
return result;
}
/**
* set AT socket event notice callback
*
* @param event notice event
* @param cb notice callback
*/
static void sim800c_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb)
{
if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1]))
{
at_evt_cb_set[event] = cb;
}
}
static void urc_connect_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by client name(%s) failed.", client_name);
return;
}
/* get the current socket by receive data */
sscanf(data, "%d,%*s", &device_socket);
if (strstr(data, "CONNECT OK"))
{
sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVENT_CONN_OK));
}
else if (strstr(data, "CONNECT FAIL"))
{
sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVENT_CONN_FAIL));
}
}
static void urc_send_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by client name(%s) failed.", client_name);
return;
}
/* get the current socket by receive data */
sscanf(data, "%d,%*s", &device_socket);
if (rt_strstr(data, "SEND OK"))
{
sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVENT_SEND_OK));
}
else if (rt_strstr(data, "SEND FAIL"))
{
sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVENT_SEND_FAIL));
}
}
static void urc_close_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get sim800c device by client name(%s) failed.", client_name);
return;
}
/* get the current socket by receive data */
sscanf(data, "%d,%*s", &device_socket);
if (rt_strstr(data, "CLOSE OK"))
{
sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVNET_CLOSE_OK));
}
else if (rt_strstr(data, "CLOSED"))
{
struct at_socket *socket = RT_NULL;
/* get AT socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the socket is disconnect by remote */
if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED])
{
at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0);
}
}
}
static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size)
{
int device_socket = 0;
rt_int32_t timeout;
rt_size_t bfsz = 0, temp_size = 0;
char *recv_buf = RT_NULL, temp[8] = {0};
struct at_socket *socket = RT_NULL;
struct at_device *device = RT_NULL;
char *client_name = client->device->parent.name;
RT_ASSERT(data && size);
/* get the current socket and receive buffer size by receive data */
sscanf(data, "+RECEIVE,%d,%d:", &device_socket, (int *) &bfsz);
/* get receive timeout by receive buffer length */
timeout = bfsz;
if (device_socket < 0 || bfsz == 0)
{
return;
}
device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name);
if (device == RT_NULL)
{
LOG_E("get m26 device by client name(%s) failed.", client_name);
return;
}
recv_buf = (char *) rt_calloc(1, bfsz);
if (recv_buf == RT_NULL)
{
LOG_E("no memory for sim800c device(%s) URC receive buffer (%d).", device->name, bfsz);
/* read and clean the coming data */
while (temp_size < bfsz)
{
if (bfsz - temp_size > sizeof(temp))
{
at_client_obj_recv(client, temp, sizeof(temp), timeout);
}
else
{
at_client_obj_recv(client, temp, bfsz - temp_size, timeout);
}
temp_size += sizeof(temp);
}
return;
}
/* sync receive data */
if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz)
{
LOG_E("sim800c device(%s) receive size(%d) data failed.", device->name, bfsz);
rt_free(recv_buf);
return;
}
/* get AT socket object by device socket descriptor */
socket = &(device->sockets[device_socket]);
/* notice the receive buffer and buffer size */
if (at_evt_cb_set[AT_SOCKET_EVT_RECV])
{
at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz);
}
}
/* sim800c device URC table for the socket data */
static const struct at_urc urc_table[] =
{
{"", ", CONNECT OK\r\n", urc_connect_func},
{"", ", CONNECT FAIL\r\n", urc_connect_func},
{"", ", SEND OK\r\n", urc_send_func},
{"", ", SEND FAIL\r\n", urc_send_func},
{"", ", CLOSE OK\r\n", urc_close_func},
{"", ", CLOSED\r\n", urc_close_func},
{"+RECEIVE,", "\r\n", urc_recv_func},
};
static const struct at_socket_ops sim800c_socket_ops =
{
sim800c_socket_connect,
sim800c_socket_close,
sim800c_socket_send,
sim800c_domain_resolve,
sim800c_socket_set_event_cb,
};
int sim800c_socket_init(struct at_device *device)
{
RT_ASSERT(device);
/* register URC data execution function */
at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
return RT_EOK;
}
int sim800c_socket_class_register(struct at_device_class *class)
{
RT_ASSERT(class);
class->socket_num = AT_DEVICE_SIM800C_SOCKETS_NUM;
class->socket_ops = &sim800c_socket_ops;
return RT_EOK;
}
#endif /* AT_DEVICE_USING_SIM800C && AT_USING_SOCKET */

135
inc/at_device.h Normal file
View File

@ -0,0 +1,135 @@
/*
* File : at_device.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-08 chenyong first version
*/
#ifndef __AT_DEVICE_H__
#define __AT_DEVICE_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <at.h>
#include <at_socket.h>
#if !defined(RT_USING_NETDEV)
#error "This RT-Thread version is older, please check and updata laster RT-Thread!"
#else
#include <arpa/inet.h>
#include <netdev.h>
#endif /* RT_USING_NETDEV */
#define AT_DEVICE_SW_VERSION "2.0.0"
#define AT_DEVICE_SW_VERSION_NUM 0x20000
/* AT device class ID */
#define AT_DEVICE_CLASS_ESP8266 0x01U
#define AT_DEVICE_CLASS_M26_MC20 0x02U
#define AT_DEVICE_CLASS_EC20 0x03U
#define AT_DEVICE_CLASS_SIM800C 0x04U
#define AT_DEVICE_CLASS_SIM76XX 0x05U
#define AT_DEVICE_CLASS_RW007 0x06U
/* Options and Commands for AT device control opreations */
#define AT_DEVICE_CTRL_POWER_ON 0x01L
#define AT_DEVICE_CTRL_POWER_OFF 0x02L
#define AT_DEVICE_CTRL_RESET 0x03L
#define AT_DEVICE_CTRL_LOW_POWER 0x04L
#define AT_DEVICE_CTRL_SLEEP 0x05L
#define AT_DEVICE_CTRL_WAKEUP 0x06L
#define AT_DEVICE_CTRL_NET_CONN 0x07L
#define AT_DEVICE_CTRL_NET_DISCONN 0x08L
#define AT_DEVICE_CTRL_SET_WIFI_INFO 0x09L
#define AT_DEVICE_CTRL_GET_SIGNAL 0x0AL
#define AT_DEVICE_CTRL_GET_GPS 0x0BL
#define AT_DEVICE_CTRL_GET_VER 0x0CL
/* Name type */
#define AT_DEVICE_NAMETYPE_DEVICE 0x01
#define AT_DEVICE_NAMETYPE_NETDEV 0x02
#define AT_DEVICE_NAMETYPE_CLIENT 0x03
struct at_device;
/* AT device wifi ssid and password information */
struct at_device_ssid_pwd
{
char *ssid;
char *password;
};
/* AT device operations */
struct at_device_ops
{
int (*init)(struct at_device *device);
int (*deinit)(struct at_device *device);
int (*control)(struct at_device *device, int cmd, void *arg);
};
struct at_device_class
{
uint16_t class_id; /* AT device class ID */
const struct at_device_ops *device_ops; /* AT device operaiotns */
#ifdef AT_USING_SOCKET
uint32_t socket_num; /* The maximum number of sockets support */
const struct at_socket_ops *socket_ops; /* AT device socket operations */
#endif
rt_slist_t list; /* AT device class list */
};
struct at_device
{
char name[RT_NAME_MAX]; /* AT device name */
rt_bool_t is_init; /* AT device initialization completed */
struct at_device_class *class; /* AT device class object */
struct at_client *client; /* AT Client object for AT device */
struct netdev *netdev; /* Network interface device for AT device */
#ifdef AT_USING_SOCKET
rt_event_t socket_event; /* AT device socket event */
struct at_socket *sockets; /* AT device sockets list */
#endif
rt_slist_t list; /* AT device list */
void *user_data; /* User-specific data */
};
/* Get AT device object */
struct at_device *at_device_get_first_initialized(void);
struct at_device *at_device_get_by_name(int type, const char *name);
#ifdef AT_USING_SOCKET
struct at_device *at_device_get_by_socket(int at_socket);
#endif
/* AT device control operaions */
int at_device_control(struct at_device *device, int cmd, void *arg);
/* Register AT device class object */
int at_device_class_register(struct at_device_class *class, uint16_t class_id);
/* Register AT device object */
int at_device_register(struct at_device *device, const char *device_name,
const char *at_client_name, uint16_t class_id, void *user_data);
#ifdef __cplusplus
}
#endif
#endif /* __AT_DEVICE_H__ */

View File

@ -1,5 +1,5 @@
/*
* File : at_client_sample.c
* File : at_sample_client.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*

52
samples/at_sample_ec20.c Normal file
View File

@ -0,0 +1,52 @@
/*
* File : at_sample_ec20.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-13 chenyong first version
*/
#include <at_device_ec20.h>
#define LOG_TAG "at.sample"
#include <at_log.h>
#define EC20_SAMPLE_DEIVCE_NAME "e0"
static struct at_device_ec20 e0 =
{
EC20_SAMPLE_DEIVCE_NAME,
EC20_SAMPLE_CLIENT_NAME,
EC20_SAMPLE_POWER_PIN,
EC20_SAMPLE_STATUS_PIN,
EC20_SAMPLE_RECV_BUFF_LEN,
};
static int ec20_device_register(void)
{
struct at_device_ec20 *ec20 = &e0;
return at_device_register(&(ec20->device),
ec20->device_name,
ec20->client_name,
AT_DEVICE_CLASS_EC20,
(void *) ec20);
}
INIT_APP_EXPORT(ec20_device_register);

View File

@ -0,0 +1,52 @@
/*
* File : at_sample_esp8266.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-10 chenyong first version
*/
#include <at_device_esp8266.h>
#define LOG_TAG "at.sample"
#include <at_log.h>
#define ESP8266_SAMPLE_DEIVCE_NAME "esp0"
static struct at_device_esp8266 esp0 =
{
ESP8266_SAMPLE_DEIVCE_NAME,
ESP8266_SAMPLE_CLIENT_NAME,
ESP8266_SAMPLE_WIFI_SSID,
ESP8266_SAMPLE_WIFI_PASSWORD,
ESP8266_SAMPLE_RECV_BUFF_LEN,
};
static int esp8266_device_register(void)
{
struct at_device_esp8266 *esp8266 = &esp0;
return at_device_register(&(esp8266->device),
esp8266->device_name,
esp8266->client_name,
AT_DEVICE_CLASS_ESP8266,
(void *) esp8266);
}
INIT_APP_EXPORT(esp8266_device_register);

52
samples/at_sample_m26.c Normal file
View File

@ -0,0 +1,52 @@
/*
* File : at_sample_m26.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-13 chenyong first version
*/
#include <at_device_m26.h>
#define LOG_TAG "at.sample"
#include <at_log.h>
#define M26_SAMPLE_DEIVCE_NAME "m0"
static struct at_device_m26 m0 =
{
M26_SAMPLE_DEIVCE_NAME,
M26_SAMPLE_CLIENT_NAME,
M26_SAMPLE_POWER_PIN,
M26_SAMPLE_STATUS_PIN,
M26_SAMPLE_RECV_BUFF_LEN,
};
static int m26_device_register(void)
{
struct at_device_m26 *m26 = &m0;
return at_device_register(&(m26->device),
m26->device_name,
m26->client_name,
AT_DEVICE_CLASS_M26_MC20,
(void *) m26);
}
INIT_APP_EXPORT(m26_device_register);

52
samples/at_sample_rw007.c Normal file
View File

@ -0,0 +1,52 @@
/*
* File : at_sample_rw007.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-13 chenyong first version
*/
#include <at_device_rw007.h>
#define LOG_TAG "at.sample"
#include <at_log.h>
#define RW007_SAMPLE_DEIVCE_NAME "r0"
static struct at_device_rw007 r0 =
{
RW007_SAMPLE_DEIVCE_NAME,
RW007_SAMPLE_CLIENT_NAME,
RW007_SAMPLE_WIFI_SSID,
RW007_SAMPLE_WIFI_PASSWORD,
RW007_SAMPLE_RECV_BUFF_LEN,
};
static int rw007_device_register(void)
{
struct at_device_rw007 *rw007 = &r0;
return at_device_register(&(rw007->device),
rw007->device_name,
rw007->client_name,
AT_DEVICE_CLASS_RW007,
(void *) rw007);
}
INIT_APP_EXPORT(rw007_device_register);

View File

@ -0,0 +1,52 @@
/*
* File : at_sample_sim76xx.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-13 chenyong first version
*/
#include <at_device_sim76xx.h>
#define LOG_TAG "at.sample"
#include <at_log.h>
#define SIM76XX_SAMPLE_DEIVCE_NAME "sim1"
static struct at_device_sim76xx sim1 =
{
SIM76XX_SAMPLE_DEIVCE_NAME,
SIM76XX_SAMPLE_CLIENT_NAME,
SIM76XX_SAMPLE_POWER_PIN,
SIM76XX_SAMPLE_STATUS_PIN,
SIM76XX_SAMPLE_RECV_BUFF_LEN,
};
static int sim76xx_device_register(void)
{
struct at_device_sim76xx *sim76xx = &sim1;
return at_device_register(&(sim76xx->device),
sim76xx->device_name,
sim76xx->client_name,
AT_DEVICE_CLASS_SIM76XX,
(void *) sim76xx);
}
INIT_APP_EXPORT(sim76xx_device_register);

View File

@ -0,0 +1,52 @@
/*
* File : at_sample_sim800c.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-13 chenyong first version
*/
#include <at_device_sim800c.h>
#define LOG_TAG "at.sample"
#include <at_log.h>
#define SIM800C_SAMPLE_DEIVCE_NAME "sim0"
static struct at_device_sim800c sim0 =
{
SIM800C_SAMPLE_DEIVCE_NAME,
SIM800C_SAMPLE_CLIENT_NAME,
SIM800C_SAMPLE_POWER_PIN,
SIM800C_SAMPLE_STATUS_PIN,
SIM800C_SAMPLE_RECV_BUFF_LEN,
};
static int sim800c_device_register(void)
{
struct at_device_sim800c *sim800c = &sim0;
return at_device_register(&(sim800c->device),
sim800c->device_name,
sim800c->client_name,
AT_DEVICE_CLASS_SIM800C,
(void *) sim800c);
}
INIT_APP_EXPORT(sim800c_device_register);

330
src/at_device.c Normal file
View File

@ -0,0 +1,330 @@
/*
* File : at_device.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2019-05-08 chenyong first version
*/
#include <stdlib.h>
#include <string.h>
#include <at_device.h>
#define DBG_TAG "at.dev"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/* The global list of at device */
static struct at_device *at_device_list = RT_NULL;
/* The global list of at device class */
static struct at_device_class *at_device_class_list = RT_NULL;
/**
* This function will get the first initialized AT device.
*
* @return the AT device structure pointer
*/
struct at_device *at_device_get_first_initialized(void)
{
rt_base_t level;
rt_slist_t *node = RT_NULL;
struct at_device *device = RT_NULL;
level = rt_hw_interrupt_disable();
for (node = &(at_device_list->list); node; node = rt_slist_next(node))
{
device = rt_slist_entry(node, struct at_device, list);
if (device && device->is_init == RT_TRUE)
{
rt_hw_interrupt_enable(level);
return device;
}
}
rt_hw_interrupt_enable(level);
return RT_NULL;
}
/**
* This function will get AT device by device name.
*
* @param type the name type
* @param name the device name or the client name
*
* @return the AT device structure pointer
*/
struct at_device *at_device_get_by_name(int type, const char *name)
{
rt_base_t level;
rt_slist_t *node = RT_NULL;
struct at_device *device = RT_NULL;
RT_ASSERT(name);
level = rt_hw_interrupt_disable();
for (node = &(at_device_list->list); node; node = rt_slist_next(node))
{
device = rt_slist_entry(node, struct at_device, list);
if (device)
{
if (((type == AT_DEVICE_NAMETYPE_DEVICE) || (type == AT_DEVICE_NAMETYPE_NETDEV)) &&
(rt_strncmp(device->name, name, rt_strlen(name)) == 0))
{
rt_hw_interrupt_enable(level);
return device;
}
else if ((type == AT_DEVICE_NAMETYPE_CLIENT) &&
(rt_strncmp(device->client->device->parent.name, name, rt_strlen(name)) == 0))
{
rt_hw_interrupt_enable(level);
return device;
}
}
}
rt_hw_interrupt_enable(level);
return RT_NULL;
}
#ifdef AT_USING_SOCKET
/**
* This function will get AT device by ip address.
*
* @param ip_addr input ip address
* network
* @return != NULL: network interface device object
* NULL: get failed
*/
struct at_device *at_device_get_by_ipaddr(ip_addr_t *ip_addr)
{
rt_base_t level;
rt_slist_t *node = RT_NULL;
struct at_device *device = RT_NULL;
level = rt_hw_interrupt_disable();
for (node = &(at_device_list->list); node; node = rt_slist_next(node))
{
device = rt_slist_entry(node, struct at_device, list);
if (device && ip_addr_cmp(ip_addr, &(device->netdev->ip_addr)))
{
rt_hw_interrupt_enable(level);
return device;
}
}
rt_hw_interrupt_enable(level);
return RT_NULL;
}
#endif /* AT_USING_SOCKET */
/**
* This function will perform a variety of control functions on AT devices.
*
* @param device the pointer of AT device structure
* @param cmd the command sent to AT device
* @param arg the argument of command
*
* @return = 0: perform successfully
* < 0: perform failed
*/
int at_device_control(struct at_device *device, int cmd, void *arg)
{
if (device->class->device_ops->control)
{
return device->class->device_ops->control(device, cmd, arg);
}
else
{
LOG_W("AT device(%s) not support control operations.", device->name);
return RT_EOK;
}
}
/**
* This function registers an AT device class with specified device class ID.
*
* @param class the pointer of AT device class structure
* @param class_id AT device class ID
*
* @return 0: register successfully
*/
int at_device_class_register(struct at_device_class *class, uint16_t class_id)
{
rt_base_t level;
RT_ASSERT(class);
/* Fill AT device class */
class->class_id = class_id;
/* Initialize current AT device class single list */
rt_slist_init(&(class->list));
level = rt_hw_interrupt_disable();
/* Add current AT device class to list */
if (at_device_class_list == RT_NULL)
{
at_device_class_list = class;
}
else
{
/* Tail insertion */
rt_slist_append(&(at_device_class_list->list), &(class->list));
}
rt_hw_interrupt_enable(level);
return RT_EOK;
}
/* Get AT device class by client ID */
static struct at_device_class *at_device_class_get(uint16_t class_id)
{
rt_base_t level;
rt_slist_t *node = RT_NULL;
struct at_device_class *class = RT_NULL;
if (at_device_class_list == RT_NULL)
{
return RT_NULL;
}
level = rt_hw_interrupt_disable();
/* Get AT device class by class ID */
for (node = (&at_device_class_list->list); node; node = rt_slist_next(node))
{
class = rt_slist_entry(node, struct at_device_class, list);
if (class && class->class_id == class_id)
{
rt_hw_interrupt_enable(level);
return class;
}
}
rt_hw_interrupt_enable(level);
return RT_NULL;
}
/**
* This function registers an AT device with specified device name and AT client name.
*
* @param device the pointer of AT device structure
* @param device_name AT device name
* @param at_client_name AT device client name
* @param class_id AT device class ID
* @param user_data user-specific data
*
* @return = 0: register successfully
* < 0: register failed
*/
int at_device_register(struct at_device *device, const char *device_name,
const char *at_client_name, uint16_t class_id, void *user_data)
{
rt_base_t level;
int result = 0;
static int device_counts = 0;
char name[RT_NAME_MAX] = {0};
struct at_device_class *class = RT_NULL;
RT_ASSERT(device);
RT_ASSERT(device_name);
RT_ASSERT(at_client_name);
class = at_device_class_get(class_id);
if (class == RT_NULL)
{
LOG_E("get AT device class(%d) failed.", class_id);
result = -RT_ERROR;
goto __exit;
}
/* Fill AT device object*/
#ifdef AT_USING_SOCKET
device->sockets = (struct at_socket *) rt_calloc(class->socket_num, sizeof(struct at_socket));
if (device->sockets == RT_NULL)
{
LOG_E("no memory for AT Socket number(%d) create.", class->socket_num);
result = -RT_ENOMEM;
goto __exit;
}
/* create AT device socket event */
rt_snprintf(name, RT_NAME_MAX, "at_se%d", device_counts++);
device->socket_event = rt_event_create(name, RT_IPC_FLAG_FIFO);
if (device->socket_event == RT_NULL)
{
LOG_E("no memory for AT device(%s) socket event create.", device_name);
result = -RT_ENOMEM;
goto __exit;
}
#endif /* AT_USING_SOCKET */
rt_memcpy(device->name, device_name, rt_strlen(device_name));
device->class = class;
device->user_data = user_data;
/* Initialize current AT device single list */
rt_slist_init(&(device->list));
level = rt_hw_interrupt_disable();
/* Add current AT device to device list */
if (at_device_list == RT_NULL)
{
at_device_list = device;
}
else
{
/* Tail insertion */
rt_slist_append(&(at_device_list->list), &(device->list));
}
rt_hw_interrupt_enable(level);
/* Initialize AT device */
result = class->device_ops->init(device);
if (result < 0)
{
goto __exit;
}
__exit:
if (result < 0)
{
device->is_init = RT_FALSE;
}
else
{
device->is_init = RT_TRUE;
}
return RT_EOK;
}