EC20简单介绍一下哈
EC20 是移远通信推出的 LTE Cat 4 无线通信模块,采用 LTE 3GPP Rel.11 技术,支持最大下行速率 150Mbps 和 最大上行速率 50Mbps ;同时在封装上兼容移远通信 UMTS/HSPA+ UC20 模块以及移远通信多网络制式 LTE Cat 3 模 块,实现了 3G 网络与 4G 网络之间的无缝切换。
注意:rk高端芯片一般制作开发板的合作厂商都会配置好了有关ec20的驱动,如若没有或有问题请参考一下内容
一、内核源码修改部分
1.1:添加VID和PID
文件名
[KERNEL]/drivers/usb/serial/option.c
添加如下内容(有Quectel EC20便可,其他可以不要)
static const struct usb_device_id option_ids[] = {
#if 1 //Added by Quectel
{ USB_DEVICE(0x2C7C, 0x0125) }, /* Quectel EC20 R2.0/EC20 R2.1/EC25/EG25-G/EM05 */ { USB_DEVICE(0x2C7C, 0x0121) }, /* Quectel EC21/EG21-G */
{ USB_DEVICE(0x2C7C, 0x0191) }, /* Quectel EG91 */
{ USB_DEVICE(0x2C7C, 0x0195) }, /* Quectel EG95 */
{ USB_DEVICE(0x2C7C, 0x0306) }, /* Quectel EG06/EP06/EM06 */
{ USB_DEVICE(0x2C7C, 0x0512) }, /* Quectel EG12/EM12/EG18 */
{ USB_DEVICE(0x2C7C, 0x0296) }, /* Quectel BG96 */
{ USB_DEVICE(0x2C7C, 0x0700) }, /* Quectel BG95/BG77/BG600L-M3/BC69 */ { USB_DEVICE(0x2C7C, 0x0435) }, /* Quectel AG35 */
{ USB_DEVICE(0x2C7C, 0x0415) }, /* Quectel AG15 */
{ USB_DEVICE(0x2C7C, 0x0452) }, /* Quectel AG520R */
{ USB_DEVICE(0x2C7C, 0x0455) }, /* Quectel AG550R */
{ USB_DEVICE(0x2C7C, 0x0620) }, /* Quectel EG20 */
{ USB_DEVICE(0x2C7C, 0x0800) }, /* Quectel RG500Q/RM500Q/RG510Q/RM510Q */
#endif
}
1.2:添加零包机制(选择性添加)
文件名 [KERNEL]/drivers/usb/serial/usb_wwan.c.
修改内容
static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
int dir, void *ctx, char *buf, int len,void (*callback) (struct urb *))
{
……
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev, endpoint) | dir,buf, len, callback, ctx);
#if 1 //Added by Quectel for zero packet
if (dir == USB_DIR_OUT) {
struct usb_device_descriptor *desc = &serial->dev->descriptor;
if (desc->idVendor == cpu_to_le16(0x2C7C))
urb->transfer_flags |= URB_ZERO_PACKET;
}
#endif
return urb;
}
1.3:添加复位机制(选择性添加)
文件位置
[KERNEL]/drivers/usb/serial/option.c.
修改内容
static struct usb_serial_driver option_1port_device = {
……
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
#if 1 //Added by Quectel
.reset_resume = usb_wwan_resume,
#endif
#endif
};
1.4:使用MBIM, GobiNet or QMI_WWAN驱动
文件位置
[KERNEL]/drivers/usb/serial/option.c
修改内容
static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) {
struct usb_wwan_intf_private *data;
……
#if 1 //Added by Quectel
//Quectel modules’s interface 4 can be used as USB network device
if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
//some interfaces can be used as USB Network device (ecm, rndis, mbim)
if (serial->interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
return -ENODEV;
}
//interface 4 can be used as USB Network device (qmi)
else if (serial->interface->cur_altsetting->desc.bInterfaceNumber >= 4) {
return -ENODEV;
}
}
#endif
/* Store device id so we can use it during attach. */
usb_set_serial_data(serial, (void *)id);
return 0;
}
二、内核配置
Device Drivers --->
[*] USB support --->
<*> USB Serial Converter support --->
<*> USB driver for GSM and CDMA modems //选上
[*] Device Drivers →
-*- Network device support
USB Network Adapters
{*} Multi-purpose USB Networking Framework
[*] Device Drivers →
-*- Network device support →
USB Network Adapters →
{*} Multi-purpose USB Networking Framework QMI_WWAN driver for Qualcomm MSM based 3G and LTE modems
<*>QMI_WWAN driver for Qualcomm MSM based 3G and LTE modems
Device Drivers --->
[*] Network device support --->
// 将这里面所有的ppp 的支持多选上
Device Drivers --->
Network device support --->
USB Network Adapters --->
<*> Multi-purpose USB NetworkingFramework
<*> CDC Ethernet support (smart devices such ascable modems) (NEW)
-*- CDC NCM support
<*> Huawei NCM embedded AT channel support
<*> Simple USB Network Links (CDC Ethernetsubset) (NEW)
三、内核修改
3.1:添加usb自动支持
文件位置:[KERNEL]/drivers/usb/serial/option.c
修改内容
static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) {
struct usb_wwan_intf_private *data;
……
#if 1 //Added by Quectel
//For USB Auto Suspend
if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000);
usb_enable_autosuspend(serial->dev);
}
#endif
/* Store device id so we can use it during attach. */
usb_set_serial_data(serial, (void *)id);
return 0;
}
3.2:添加usb远程唤醒功能支持(选择性添加)
文件位置
[KERNEL]/drivers/usb/serial/option.c
修改内容
static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) {
struct usb_wwan_intf_private *data;
……
#if 1 //Added by Quectel
//For USB Remote Wakeup
if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup
}
#endif
/* Store device id so we can use it during attach. */
usb_set_serial_data(serial, (void *)id);
return 0;
}
四、其他操作
4.1 编译内核
可根据环境不同选用不同的命令编译适合自己开发板的内核
4.2 烧录内核
可以看到/dev下有这几个串口文件
4.3 编译拨号工具
解压原厂quectel-CM源码,放在系统的任意路径下,它里面有Makefile文件,我们先指定硬件架构和交叉编译链,再make,在当前目录下便可生成执行文件quectel-CM。例如rv1126
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
指定完成之后执行make命令即可编译拨号工具
4.4 执行拨号
quectel-CM &
执行完成之后 会自动拿到ip ping百度能ping同则表示4G模块移植成功
五、连接移动onenet物联网平台
在完成以上操作后/dev/ttyusb*节点(该类设备节点为usb转串口,作为开发人员可直接当作串口来操作它)
本文采用mqtt协议连接物联网平台,下面是常用AT指令(简单例程附在文章最后):
// 配置EC20模块连接到网络
send_at_command("AT+CGATT=1"); // 激活移动设备附着
// 配置MQTT连接参数
send_at_command("AT+QMTOPEN=0,\"mqtt.eclipse.org\",1883"); // 打开MQTT连接
// 认证并连接到MQTT代理
send_at_command("AT+QMTCONN=0,\"client_id\",\"username\",\"password\""); // 连接到MQTT代理
// 发布数据到MQTT代理
char data_to_publish[] = "Hello from EC20!";
send_at_command("AT+QMTPUBEX=0,1,0,0,\"topic\",0"); // 发布消息
send_at_command(data_to_publish);
// 断开MQTT连接
send_at_command("AT+QMTDISC=0"); // 断开MQTT连接
// 关闭移动设备附着
send_at_command("AT+CGATT=0");
以上是上传数据的基本流程send_at_command函数为自己编写的写入功能函数,这里需要注意的是上传设备数据时需转换为json格式。
Onenet的基本操作不在赘述
需要注意的点在于passwd需要进行简单转换;根据密钥生成,为便于开发者开发,平台提供Token生成工具
Token生成工具:
时间戳(Unix timestamp)转换工具 - 在线工具Unix时间戳转换可以把Unix时间转成北京时间https://tool.lu/timestamp/
六、数据上传示例代码(代码仅供参考)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h>
#define SERIAL_PORT "/dev/ttyUSB2" // 根据实际情况更改串口路径
#define BAUD_RATE B115200
int serial_fd;
pthread_mutex_t serial_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t response_cond = PTHREAD_COND_INITIALIZER;
int response_ready = 0;
void init_serial() {
// ... (与之前相同的初始化代码)
serial_fd = open(SERIAL_PORT, O_RDWR);
if (serial_fd == -1) {
perror("Error opening serial port");
exit(EXIT_FAILURE);
}
struct termios tty;
if (tcgetattr(serial_fd, &tty) != 0) {
perror("Error from tcgetattr");
exit(EXIT_FAILURE);
}
cfsetospeed(&tty, BAUD_RATE);
cfsetispeed(&tty, BAUD_RATE);
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tcflush(serial_fd, TCIFLUSH);
if (tcsetattr(serial_fd, TCSANOW, &tty) != 0) {
perror("Error from tcsetattr");
exit(EXIT_FAILURE);
}
}
void send_at_command(const char *command) {
//pthread_mutex_lock(&serial_mutex);
write(serial_fd, command, strlen(command));
write(serial_fd, "\r\n", 2);
printf("Send: %s\r\n", command);// pthread_mutex_unlock(&serial_mutex);
// 等待回复完成
while (!response_ready) {
// pthread_cond_wait(&response_cond, &serial_mutex);
}
printf("Re001234\r\n");
// 重置状态
response_ready = 0;
}
void *receive_thread(void *arg) {
char buffer[256];
ssize_t n;
while (1) {
pthread_mutex_lock(&serial_mutex);
// 读取串口缓冲区
n = read(serial_fd, buffer, sizeof(buffer) - 1);
pthread_mutex_unlock(&serial_mutex);
if (n > 0) {
buffer[n] = '\0';
printf("Received: %s\r\n", buffer);
// 通知主线程回复已经准备好
response_ready = 1;
//pthread_cond_signal(&response_cond);
}
}
return NULL;
}
int main() {
pthread_t tid;
init_serial();
// 创建接收线程
if (pthread_create(&tid, NULL, receive_thread, NULL) != 0) {
perror("Error creating receive thread");
exit(EXIT_FAILURE);
}
// 配置EC20模块连接到网络
send_at_command("AT+CGATT=1");
printf("Re00123\r\n");
send_at_command("AT+QMTCFG=\"version\",0,4\r\n");
// 配置MQTT连接参数
send_at_command("AT+QMTOPEN=0,\"mqtts.heclouds.com\",1883\r\n");
printf("Re001\r\n");
usleep(1000000);
// 认证并连接到MQTT代理
send_at_command("AT+QMTCONN=0,\"test\",\"559vYG4tGp\",\"version=2018-10-31&res=products%2F559vYG4tGp%2Fdevices%2Ftest&et=1735796532&method=sha1&sign=4fp%2Bxw97YRu0g%2BGCS6shUncq1mw%3D\"\r\n"); // 连接到MQTT代理
printf("Re022\r\n");//
usleep(1000000);
send_at_command("AT+QMTSUB=0,1,\"$sys/559vYG4tGp/test/thing/property/set\",1\r\n");
usleep(1000000);
send_at_command("AT+QMTSUB=0,1,\"$sys/559vYG4tGp/test/thing/property/reply\",1\r\n");
usleep(1000000);
// 发布数据到MQTT代理
char data_to_publish[1024] = {0};
send_at_command("AT+QMTPUBEX=0,0,0,0,\"$sys/559vYG4tGp/test/thing/property/post\",50\r\n");
usleep(1000000);
send_at_command(data_to_publish);
// 断开MQTT连接
send_at_command("AT+QMTDISC=0");
// 关闭移动设备附着
send_at_command("AT+CGATT=0");
// 等待接收线程结束
pthread_join(tid, NULL);
// 关闭串口
close(serial_fd);
return 0;
}
七、连接onenet终端log
成功后就可以在onenet上看到设备在线了