diff --git a/README.md b/README.md index eda0724..03ed40e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## 1. 简介 ## -AT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文件和示例代码组成,目前支持的 AT 设备有:ESP8266、ESP32、M26、MC20、RW007、MW31、SIM800C、W60X 、SIM76XX、A9/A9G、BC26 、AIR720系列设备等,目前上述设备都完成对 `AT socket` 功能的移植,及设备通过 AT 命令实现标准 socket 编程接口,完成 socket 通讯的功能,具体功能介绍可参考 [《RT-Thread 编程指南》](https://www.rt-thread.org/document/site/programming-manual/at/at/)AT 命令章节 。 +AT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文件和示例代码组成,目前支持的 AT 设备有:ESP8266、ESP32、M26、MC20、RW007、MW31、SIM800C、W60X 、SIM76XX、A9/A9G、BC26 、AIR720、ME3616系列设备等,目前上述设备都完成对 `AT socket` 功能的移植,及设备通过 AT 命令实现标准 socket 编程接口,完成 socket 通讯的功能,具体功能介绍可参考 [《RT-Thread 编程指南》](https://www.rt-thread.org/document/site/programming-manual/at/at/)AT 命令章节 。 ### 1.1. 目录结构 ### @@ -25,6 +25,7 @@ AT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文 | class/a9g | A9G 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | | class/bc26 | bc26 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | | class/air720 | air720 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | +| class/me3616 | me3616 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | ### 1.2 许可证 ### at_device package 遵循 LGPLv2.1 许可,详见 `LICENSE` 文件。 @@ -68,7 +69,7 @@ AT device 软件包目前已经发布多个版本,各个版本之间选项配 Version (V1.6.0) ---> - **Enable at device init by thread**: 配置开启设备网络初始化是否通过创建线程完成; -- **AT socket device modules**: AT 设备选择,目前支持 RW007、ESP8266、M26/MC20、EC20、SIM800C、SIM76XX、A9/A9G、BC26 、air720等设备; +- **AT socket device modules**: AT 设备选择,目前支持 RW007、ESP8266、M26/MC20、EC20、SIM800C、SIM76XX、A9/A9G、BC26 、air720、ME3616等设备; - **Version**: 下载软件包版本; **V2.X.X (laster) 版本配置选项介绍** @@ -101,6 +102,9 @@ RT-Thread online packages ---> [ ] Notion MW31 ---> [ ] WinnerMicro W60X ---> [ ] AiThink A9/A9G ---> + [ ] Quectel BC26 ---> + [ ] Luat air720 ---> + [ ] GOSUNCN ME3616 ---> Version (latest) ---> ``` @@ -128,6 +132,7 @@ RT-Thread online packages ---> - **AiThink A9/A9G**:开启 A9/A9G (2G 模块)设备支持; - **Quectel BC26**:开启 BC26(NB-IOT 模块)设备支持; - **Luat Air720**:开启 air720(4g 模块)设备支持; +- **GOSUNCN ME3616**:开启 ME3616(NB-IOT 模块)设备支持; - **Version** 下载软件包版本; 上面配置选项以 2G 模块和 WIFI 模块选项为例,介绍了`V2.X.X` 版本 AT device 软件包配置方式,如下几点值得注意: diff --git a/SConscript b/SConscript index 4ee29d4..90e5173 100644 --- a/SConscript +++ b/SConscript @@ -112,6 +112,15 @@ if GetDepend(['AT_DEVICE_USING_AIR720']): if GetDepend(['AT_DEVICE_AIR720_SAMPLE']): src += Glob('samples/at_sample_air720.c') +# ME3616 +if GetDepend(['AT_DEVICE_USING_ME3616']): + path += [cwd + '/class/me3616'] + src += Glob('class/bc26/at_device_me3616.c') + if GetDepend(['AT_USING_SOCKET']): + src += Glob('class/bc26/at_socket_me3616.c') + if GetDepend(['AT_DEVICE_ME3616_SAMPLE']): + src += Glob('samples/at_sample_me3616.c') + group = DefineGroup('at_device', src, depend = ['PKG_USING_AT_DEVICE'], CPPPATH = path) Return('group') diff --git a/class/me3616/at_device_me3616.c b/class/me3616/at_device_me3616.c new file mode 100644 index 0000000..c9643b7 --- /dev/null +++ b/class/me3616/at_device_me3616.c @@ -0,0 +1,941 @@ +/* + * File : at_device_me3616.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-12-30 qiyongzhong first version + */ + +#include +#include + +#include + +#define LOG_TAG "at.dev.me3616" +#include + +#ifdef AT_DEVICE_USING_ME3616 + +#ifndef ME3616_DEEP_SLEEP_EN +#define ME3616_DEEP_SLEEP_EN 0//module support deep sleep mode +#endif + +#define ME3616_WAIT_CONNECT_TIME 5000 +#define ME3616_THREAD_STACK_SIZE 2048 +#define ME3616_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) + +static int me3616_power_on(struct at_device *device) +{ + struct at_device_me3616 *me3616 = RT_NULL; + + me3616 = (struct at_device_me3616 *)device->user_data; + me3616->power_status = RT_TRUE; + + /* not nead to set pin configuration for me3616 device power on */ + if (me3616->power_pin == -1) + { + return(RT_EOK); + } + + rt_pin_write(me3616->power_pin, PIN_HIGH); + rt_thread_mdelay(500); + rt_pin_write(me3616->power_pin, PIN_LOW); + + LOG_D("power on success."); + + return(RT_EOK); +} + +static int me3616_power_off(struct at_device *device) +{ + at_response_t resp = RT_NULL; + struct at_device_me3616 *me3616 = RT_NULL; + + resp = at_create_resp(64, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for resp create."); + return(-RT_ERROR); + } + + at_obj_exec_cmd(device->client, resp, "AT+ZTURNOFF");//command response does not have "\r\n" after "OK" + /*if (at_obj_exec_cmd(device->client, resp, "AT+ZTURNOFF") != RT_EOK) + { + LOG_D("power off fail."); + at_delete_resp(resp); + return(-RT_ERROR); + }*/ + + at_delete_resp(resp); + + me3616 = (struct at_device_me3616 *)device->user_data; + me3616->power_status = RT_FALSE; + + LOG_D("power off success."); + + return(RT_EOK); +} + +static int me3616_sleep(struct at_device *device) +{ + at_response_t resp = RT_NULL; + struct at_device_me3616 *me3616 = RT_NULL; + + me3616 = (struct at_device_me3616 *)device->user_data; + if ( ! me3616->power_status)//power off + { + return(RT_EOK); + } + if (me3616->sleep_status)//is sleep status + { + return(RT_EOK); + } + + resp = at_create_resp(64, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for resp create."); + return(-RT_ERROR); + } + + if (at_obj_exec_cmd(device->client, resp, "AT+CPSMS=1,,,\"00111110\",\"00000001\"") != RT_EOK) + { + LOG_D("enable sleep fail."); + at_delete_resp(resp); + return(-RT_ERROR); + } + + #if ME3616_DEEP_SLEEP_EN + if (at_obj_exec_cmd(device->client, resp, "AT+ZSLR") != RT_EOK) + { + LOG_D("startup entry into sleep fail."); + at_delete_resp(resp); + return(-RT_ERROR); + } + #endif + + at_delete_resp(resp); + me3616->sleep_status = RT_TRUE; + + LOG_D("sleep success."); + + return(RT_EOK); +} + +static int me3616_wakeup(struct at_device *device) +{ + at_response_t resp = RT_NULL; + struct at_device_me3616 *me3616 = RT_NULL; + + me3616 = (struct at_device_me3616 *)device->user_data; + if ( ! me3616->power_status)//power off + { + LOG_E("the power is off and the wake-up cannot be performed"); + return(-RT_ERROR); + } + if ( ! me3616->sleep_status)//no sleep status + { + return(RT_EOK); + } + + resp = at_create_resp(64, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for resp create."); + return(-RT_ERROR); + } + + #if ME3616_DEEP_SLEEP_EN + if (me3616->power_pin != -1) + { + rt_pin_write(me3616->power_pin, PIN_HIGH); + rt_thread_mdelay(100); + rt_pin_write(me3616->power_pin, PIN_LOW); + rt_thread_mdelay(200); + } + #endif + + if (at_obj_exec_cmd(device->client, resp, "AT+CPSMS=0") != RT_EOK) + { + LOG_D("wake up fail."); + at_delete_resp(resp); + return(-RT_ERROR); + } + + at_delete_resp(resp); + me3616->sleep_status = RT_FALSE; + + LOG_D("wake up success."); + + return(RT_EOK); +} + +static int me3616_check_link_status(struct at_device *device) +{ + at_response_t resp = RT_NULL; + struct at_device_me3616 *me3616 = RT_NULL; + int result = -RT_ERROR; + + RT_ASSERT(device); + + me3616 = (struct at_device_me3616 *)device->user_data; + if ( ! me3616->power_status)//power off + { + LOG_D("the power is off."); + return(-RT_ERROR); + } + + #if ME3616_DEEP_SLEEP_EN + if (me3616->sleep_status)//is sleep status + { + if (me3616->power_pin != -1) + { + rt_pin_write(me3616->power_pin, PIN_HIGH); + rt_thread_mdelay(100); + rt_pin_write(me3616->power_pin, PIN_LOW); + rt_thread_mdelay(200); + } + } + #endif + + resp = at_create_resp(64, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for resp create."); + return(-RT_ERROR); + } + + result = -RT_ERROR; + if (at_obj_exec_cmd(device->client, resp, "AT+CGREG?") == RT_EOK) + { + int link_stat = 0; + if (at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %*d,%d", &link_stat) > 0) + { + if (link_stat == 1 || link_stat == 5) + { + result = RT_EOK; + } + } + } + + #if ME3616_DEEP_SLEEP_EN + if (me3616->sleep_status)//is sleep status + { + if (at_obj_exec_cmd(device->client, resp, "AT+ZSLR") != RT_EOK) + { + LOG_D("startup entry into sleep fail."); + } + } + #endif + + at_delete_resp(resp); + + return(result); +} + + +/* ============================= me3616 network interface operations ============================= */ +/* set me3616 network interface device status and address information */ +static int me3616_netdev_set_info(struct netdev *netdev) +{ +#define ME3616_INFO_RESP_SIZE 128 +#define ME3616_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; + + RT_ASSERT(netdev); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get device(%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(ME3616_INFO_RESP_SIZE, 0, ME3616_INFO_RESP_TIMO); + if (resp == RT_NULL) + { + LOG_E("no memory for resp create."); + result = -RT_ENOMEM; + goto __exit; + } + + /* set network interface device hardware address(IMEI) */ + { + #define ME3616_NETDEV_HWADDR_LEN 8 + #define ME3616_IMEI_LEN 15 + + char imei[ME3616_IMEI_LEN] = {0}; + int i = 0, j = 0; + + /* send "AT+GSN" commond to get device IMEI */ + if (at_obj_exec_cmd(device->client, resp, "AT+GSN") != RT_EOK) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args(resp, 2, "%s", imei) <= 0) + { + LOG_E("%s device prase \"AT+GSN\" cmd error.", device->name); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("%s device IMEI number: %s", device->name, imei); + + netdev->hwaddr_len = ME3616_NETDEV_HWADDR_LEN; + /* get hardware address by IMEI */ + for (i = 0, j = 0; i < ME3616_NETDEV_HWADDR_LEN && j < ME3616_IMEI_LEN; i++, j+=2) + { + if (j != ME3616_IMEI_LEN - 1) + { + netdev->hwaddr[i] = (imei[j] - '0') * 10 + (imei[j + 1] - '0'); + } + else + { + netdev->hwaddr[i] = (imei[j] - '0'); + } + } + } + + /* set network interface device IP address */ + { + #define IP_ADDR_SIZE_MAX 16 + char ipaddr[IP_ADDR_SIZE_MAX] = {0}; + + /* send "AT+CGPADDR=1" commond to get IP address */ + if (at_obj_exec_cmd(device->client, resp, "AT+CGPADDR=1") != RT_EOK) + { + result = -RT_ERROR; + goto __exit; + } + + /* parse response data "+CGPADDR: 1," */ + if (at_resp_parse_line_args_by_kw(resp, "+CGPADDR:", "+CGPADDR: %*d,\"%s\"", ipaddr) <= 0) + { + LOG_E("%s device \"AT+CGPADDR=1\" cmd error.", device->name); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("%s device IP address: %s", device->name, ipaddr); + + /* set network interface address information */ + inet_aton(ipaddr, &addr); + netdev_low_level_set_ipaddr(netdev, &addr); + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static void me3616_check_link_status_entry(void *parameter) +{ +#define ME3616_LINK_DELAY_TIME (60 * RT_TICK_PER_SECOND) + + rt_bool_t is_link_up; + 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 device(%s) failed.", netdev->name); + return; + } + + while (1) + { + is_link_up = (me3616_check_link_status(device) == RT_EOK); + + netdev_low_level_set_link_status(netdev, is_link_up); + + rt_thread_delay(ME3616_LINK_DELAY_TIME); + } +} + +static int me3616_netdev_check_link_status(struct netdev *netdev) +{ +#define ME3616_LINK_THREAD_TICK 20 +#define ME3616_LINK_THREAD_STACK_SIZE (1024 + 512) +#define ME3616_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2) + + rt_thread_t tid; + char tname[RT_NAME_MAX] = {0}; + + RT_ASSERT(netdev); + + rt_snprintf(tname, RT_NAME_MAX, "%s", netdev->name); + + /* create me3616 link status polling thread */ + tid = rt_thread_create(tname, me3616_check_link_status_entry, (void *)netdev, + ME3616_LINK_THREAD_STACK_SIZE, ME3616_LINK_THREAD_PRIORITY, ME3616_LINK_THREAD_TICK); + if (tid != RT_NULL) + { + rt_thread_startup(tid); + } + + return RT_EOK; +} + +static int me3616_net_init(struct at_device *device); + +static int me3616_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 device(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_FALSE) + { + me3616_net_init(device); + device->is_init = RT_TRUE; + + netdev_low_level_set_status(netdev, RT_TRUE); + LOG_D("network interface device(%s) set up status.", netdev->name); + } + + return RT_EOK; +} + +static int me3616_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 device(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_TRUE) + { + me3616_power_off(device); + device->is_init = RT_FALSE; + + netdev_low_level_set_status(netdev, RT_FALSE); + LOG_D("network interface device(%s) set down status.", netdev->name); + } + + return RT_EOK; +} + +#ifdef NETDEV_USING_PING +static int me3616_netdev_ping(struct netdev *netdev, const char *host, + size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) +{ +#define ME3616_PING_RESP_SIZE 256 +#define ME3616_PING_IP_SIZE 16 +#define ME3616_PING_TIMEO (10 * RT_TICK_PER_SECOND) + + rt_err_t result = RT_EOK; + int response = -1, recv_data_len, ping_time, ttl; + char ip_addr[ME3616_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 device(%s) failed.", netdev->name); + return -RT_ERROR; + } + + resp = at_create_resp(ME3616_PING_RESP_SIZE, 0, ME3616_PING_TIMEO); + if (resp == RT_NULL) + { + LOG_E("no memory for resp create"); + return -RT_ENOMEM; + } + + if (at_obj_exec_cmd(device->client, resp, "AT+EDNS=\"%s\"", host) != RT_EOK) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "IPV4:", "IPV4:%s\r", ip_addr) <= 0) + { + result = -RT_ERROR; + goto __exit; + } + + at_resp_set_info(resp, ME3616_PING_RESP_SIZE, 8, timeout); + + /* send "AT+PING=[-l/L ] [-n/N ][-w/W