229 lines
6.1 KiB
C++
229 lines
6.1 KiB
C++
/*
|
||
本程序主要用于初始化网络配置,使4g模块可以正常上网
|
||
*/
|
||
|
||
#include <iostream>
|
||
#include <fcntl.h>
|
||
#include <unistd.h>
|
||
#include <termios.h>
|
||
#include <poll.h>
|
||
#include <cstring>
|
||
#include <thread>
|
||
|
||
using namespace std;
|
||
|
||
// 串口设备
|
||
string Serious = "/dev/ttyUSB2";
|
||
|
||
// 设置网络
|
||
void SetNet();
|
||
|
||
int main(int argc, char *argv[])
|
||
{
|
||
|
||
this_thread::sleep_for(chrono::seconds(3));
|
||
|
||
SetNet();
|
||
return 0;
|
||
}
|
||
|
||
// 设置网络
|
||
void SetNet()
|
||
{
|
||
auto configure_serial = [](int fd) -> bool
|
||
{
|
||
struct termios options;
|
||
if (tcgetattr(fd, &options) != 0)
|
||
return false;
|
||
|
||
cfsetispeed(&options, B115200);
|
||
cfsetospeed(&options, B115200);
|
||
|
||
options.c_cflag |= (CLOCAL | CREAD);
|
||
options.c_cflag &= ~PARENB; // 无校验
|
||
options.c_cflag &= ~CSTOPB; // 1 位停止位
|
||
options.c_cflag &= ~CSIZE;
|
||
options.c_cflag |= CS8; // 8 位数据位
|
||
options.c_cflag &= ~CRTSCTS; // 关闭硬件流控
|
||
|
||
options.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控
|
||
options.c_iflag &= ~(INLCR | ICRNL);
|
||
|
||
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始模式
|
||
options.c_oflag &= ~OPOST; // 原始输出
|
||
|
||
options.c_cc[VMIN] = 0; // 非阻塞读取
|
||
options.c_cc[VTIME] = 10; // 1.0s 读超时
|
||
|
||
if (tcsetattr(fd, TCSANOW, &options) != 0)
|
||
return false;
|
||
tcflush(fd, TCIOFLUSH);
|
||
return true;
|
||
};
|
||
|
||
auto write_all = [](int fd, const std::string &data) -> bool
|
||
{
|
||
const char *p = data.c_str();
|
||
size_t left = data.size();
|
||
while (left > 0)
|
||
{
|
||
ssize_t n = write(fd, p, left);
|
||
if (n < 0)
|
||
{
|
||
if (errno == EINTR)
|
||
continue;
|
||
return false;
|
||
}
|
||
left -= static_cast<size_t>(n);
|
||
p += n;
|
||
}
|
||
return true;
|
||
};
|
||
|
||
auto send_at = [&](int fd, const std::string &cmd, std::string &resp, int timeout_ms = 3000) -> bool
|
||
{
|
||
std::string line = cmd + "\r\n";
|
||
if (!write_all(fd, line))
|
||
return false;
|
||
|
||
resp.clear();
|
||
char buf[256];
|
||
int elapsed = 0;
|
||
while (elapsed < timeout_ms)
|
||
{
|
||
struct pollfd pfd{fd, POLLIN, 0};
|
||
int pr = poll(&pfd, 1, 200);
|
||
if (pr > 0 && (pfd.revents & POLLIN))
|
||
{
|
||
ssize_t n = read(fd, buf, sizeof(buf));
|
||
if (n > 0)
|
||
{
|
||
resp.append(buf, buf + n);
|
||
if (resp.find("\r\nOK\r\n") != std::string::npos ||
|
||
resp.find("\nOK\n") != std::string::npos ||
|
||
resp.find("OK") != std::string::npos)
|
||
return true;
|
||
if (resp.find("ERROR") != std::string::npos)
|
||
return false;
|
||
}
|
||
}
|
||
elapsed += 200;
|
||
}
|
||
return false; // 超时
|
||
};
|
||
|
||
int fd = open(Serious.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||
if (fd < 0)
|
||
{
|
||
std::cerr << "无法打开串口: " << Serious << "\n";
|
||
return;
|
||
}
|
||
// 设为阻塞
|
||
int flags = fcntl(fd, F_GETFL, 0);
|
||
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
|
||
|
||
if (!configure_serial(fd))
|
||
{
|
||
std::cerr << "串口参数配置失败\n";
|
||
close(fd);
|
||
return;
|
||
}
|
||
|
||
std::string resp;
|
||
// 0) 连续握手,确保 AT 可用(最多 10 次)
|
||
{
|
||
bool ok = false;
|
||
for (int i = 1; i <= 10; ++i)
|
||
{
|
||
if (send_at(fd, "AT", resp, 500))
|
||
{
|
||
ok = true;
|
||
break;
|
||
}
|
||
usleep(500 * 1000);
|
||
}
|
||
if (!ok)
|
||
{
|
||
std::cerr << "AT 握手失败,模块未响应\n";
|
||
close(fd);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 1) 打开功能
|
||
if (!send_at(fd, "AT+CFUN=1", resp))
|
||
{
|
||
std::cerr << "AT+CFUN=1 失败: " << resp << "\n";
|
||
}
|
||
else
|
||
{
|
||
std::cout << "AT+CFUN=1 OK\n";
|
||
}
|
||
|
||
// 1.1) 等 SIM 就绪(AT+CPIN? 返回 READY,最多 30 秒)
|
||
{
|
||
bool ready = false;
|
||
for (int i = 0; i < 60; ++i)
|
||
{
|
||
if (send_at(fd, "AT+CPIN?", resp, 500) && resp.find("READY") != std::string::npos)
|
||
{
|
||
ready = true;
|
||
break;
|
||
}
|
||
usleep(500 * 1000);
|
||
}
|
||
if (!ready)
|
||
std::cerr << "SIM 未就绪,后续可能失败\n";
|
||
}
|
||
|
||
// 2) 设置 PDP 上下文:APN = ipn(请按运营商实际 APN 调整)
|
||
if (!send_at(fd, "AT+CGDCONT=1,\"IP\",\"ipn\"", resp))
|
||
{
|
||
std::cerr << "AT+CGDCONT 设置失败: " << resp << "\n";
|
||
}
|
||
else
|
||
{
|
||
std::cout << "AT+CGDCONT OK\n";
|
||
}
|
||
|
||
// 2.1) 等驻网(AT+CEREG? 返回 ,1 或 ,5,最多 60 秒)
|
||
{
|
||
auto registered = [](const std::string &s)
|
||
{ return s.find(",1") != std::string::npos || s.find(",5") != std::string::npos; };
|
||
bool ok = false;
|
||
for (int i = 0; i < 60; ++i)
|
||
{
|
||
if (send_at(fd, "AT+CEREG?", resp, 500) && registered(resp))
|
||
{
|
||
ok = true;
|
||
break;
|
||
}
|
||
usleep(1000 * 1000);
|
||
}
|
||
if (!ok)
|
||
std::cerr << "未驻网,继续尝试拨号但成功率较低\n";
|
||
}
|
||
|
||
// 3) 打开网卡/拨号(Quectel 私有指令示例)- 失败重试 5 次
|
||
{
|
||
bool ok = false;
|
||
for (int attempt = 1; attempt <= 5; ++attempt)
|
||
{
|
||
resp.clear();
|
||
if (send_at(fd, "AT+QNETDEVCTL=1,1,1", resp))
|
||
{
|
||
std::cout << "AT+QNETDEVCTL OK (attempt " << attempt << ")\n";
|
||
ok = true;
|
||
break;
|
||
}
|
||
std::cerr << "AT+QNETDEVCTL 失败 (attempt " << attempt << "/5): " << resp << "\n";
|
||
usleep(1000 * 1000); // 1s 间隔
|
||
}
|
||
if (!ok)
|
||
{
|
||
std::cerr << "AT+QNETDEVCTL 连续 5 次失败,退出本步骤\n";
|
||
}
|
||
}
|
||
|
||
close(fd);
|
||
} |