基于嵌入式Linux系统,通过4G模块发送文件至云服务器端
/*
基于嵌入式linux系统,利用4G模块Quectel EC20,发送文件到云服务器下的FTP服务器中
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/select.h>
#include <stdint.h>
#include <getopt.h>
#include <signal.h>
#include <string.h>
#define MAXLINE 32 //一次读取最大的字节数,此处太大容易掉包,改为32;
#define lenToRead 1000 //lenTORead不能太小
#define QUEC_AT_PORT "/dev/ttyUSB2" //设备名
static int smd_fd = -1;
void delay(unsigned int xms);
void serial_init(int fd); //串口初始化函数声明
int Ql_SendAT(char* atCmd, char* finalRsp, long timeout_ms, int should_Add); //函数声明,AT指令发送函数
int main(int argc, char* argv[]) //主函数
{
FILE* from_file;
char buf[MAXLINE];
char receive_buf[1000];
int read_len;
char str[10];
char strsl[1000];
int rl_len;
int rdlen2;
printf("< Quectel OpenLinux: AT example >\n");
smd_fd = open(QUEC_AT_PORT, O_RDWR | O_NONBLOCK | O_NOCTTY); //打开虚拟串口,设置成非阻塞,读写模式;其中第一个参数为要打开的设备或者文件,如果文件打开成功,返回文件的文件描述符
//printf("< open(\"%s\")=%d >\n", QUEC_AT_PORT, smd_fd);
serial_init(smd_fd); //串口初始化
Ql_SendAT("AT+QICSGP=1,1,\"CMNET\",\"\",\"\",1", "OK", 5500, 0); //配置PDP上下文(PDP Context),CMNET代表中国移动,若手机卡为联通的,则换成UNINET;
//当手机需要访问网络时,需要获取一个PDP地址,你可以把它看成移动的私网地址;PDP(分组报文协议简写)
Ql_SendAT("AT+QIACT=1","OK",5500,0); //激活PDP场景
Ql_SendAT("AT+QFTPCFG=\"contextid\",1","OK",5500,0); //将PDP上的ID配置为1
Ql_SendAT("AT+QFTPCFG=\"account\",\"uftp\",\"123456\"","OK",5500,0); //设置用户名和密码,云服务器上FTP服务器的用户名uftp和密码123456
Ql_SendAT("AT+QFTPCFG=\"filetype\",0","OK",5500,0); //设置传输数据类型
Ql_SendAT("AT+QFTPCFG=\"transmode\",1","OK",5500,0); //被动模式,FTP服务器在端口上侦听数据连接
Ql_SendAT("AT+QFTPCFG=\"rsptimeout\",90","OK",5500,0); //设置响应超时值
Ql_SendAT("AT+QFTPOPEN=\"106.15.74.16\",21","OK",7500,0); //登录到FTP服务器,云服务器的IP为106.15.74.16
Ql_SendAT("AT+QFTPCWD=\"/home/uftp\"","OK",7500,0); //设置当前目录,上传的文件存储到/home/uftp目录下
if(((from_file)=open("/extp/Arm_can/can1.txt",O_RDWR))==NULL) //打开要读取的文件
{
printf("Can't open the file \n");
exit(1);
}
printf("from_file=%d\n",from_file);
Ql_SendAT("AT+QFTPPUT=\"can1.txt\",\"COM:\",0", "CONNECT", 8000, 0);
while((read_len = read(from_file, buf, MAXLINE))>0) //数据读取到buf中,MAXLINE为要读取的字节数;
//读取成功的话返回读取到的字节数;如果返回 0 表示读取到了文件末尾;如果返回负值,表示读取失败
{
// printf("read_len=%d\n",read_len);
rl_len = write(smd_fd, buf, read_len); //把文件写入ttyUSB2设备中
// printf("rl_len=%d\n",rl_len);
bzero(buf, MAXLINE);
}
sleep(1);
write(smd_fd,"+++",3); //退出数据传输模式,前后的sleep(1)很关键
sleep(1);
sleep(6);
rdlen2 = read(smd_fd, strsl, lenToRead);
//printf(">>Read response/urc, len=%d, content:\n%s\n", rdlen2, strsl);
memset(strsl, 0, sizeof(strsl));
//printf("read_len = %d\n", read_len);
Ql_SendAT("AT+QIDEACT=1", "OK", 8500, 0); //关闭场景
close(from_file); //关闭文件
close(smd_fd); //关闭虚拟串口
printf("< Quectel OpenLinux: AT example end >\n\n");
return 0;
}
int Ql_SendAT(char* atCmd, char* finalRsp, long timeout_ms, int should_Add) //AT指令发送函数,should_Add是判断是否末尾要加入\r\n,如果不添加则还代表atCmd数据的长度
{
int iRet;
int iLen;
fd_set fds;
int rdLen;
char strAT[1000]; //存放AT指令
char strFinalRsp[100]; //存放写入的符号,例如”OK“
char strResponse[1000]; //存放串口收到的信息
struct timeval timeout = {0, 0};
int bRcvFinalRsp = 0;
memset(strFinalRsp, 0, sizeof(strFinalRsp));
sprintf(strFinalRsp, "%s", finalRsp); //sprintf的作用是将一个格式化的字符串输出到一个目的字符串中,而printf是将一个格式化的字符串输出到屏幕。
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = timeout_ms % 1000;
// Send AT
if( should_Add != 0 ) //如果末尾不需要回车换行
{ delay(1000);
iRet = write(smd_fd, atCmd, should_Add);
delay(1000);
// printf("iLen = %d", should_Add);
}
else
{
memset(strAT, 0, sizeof(strAT)); //初始化strAT[]的内存空间
memset(strResponse, 0, sizeof(strResponse));
iLen = sizeof(atCmd);
strncpy(strAT, atCmd, iLen); //将字符串atCmd的前iLen个字符复制到数组strAT中
iLen = strlen(atCmd); //求字符串的长度
if ((atCmd[iLen-1] != '\r') && (atCmd[iLen-1] != '\n'))
{
iLen = sprintf(strAT, "%s\r\n", atCmd); //返回写入字符的总数,即strAT字符个数+2
strAT[iLen] = '\0'; //相当于=0,是字符串的结尾标志
//printf("进入判断\n");
}
iRet = write(smd_fd, strAT, iLen); //若成功,write函数返回写入的字节数;返回0表示没有写入任何数据;返回负数表示写入失败
delay(2000);
}
printf(">>Send AT: \"%s\", iRet=%d\n", atCmd, iRet);
//printf("strAT:\"%s\", iLen=%d\n",strAT,iLen);
// Wait for the response
while (1)
{
FD_ZERO(&fds); //清空集合,都设置为0
FD_SET(smd_fd, &fds); //将一个给定文件描述符加入集合之中
memset(strAT, 0, sizeof(strAT));
memset(strResponse, 0, sizeof(strResponse));
//printf("timeout.tv_sec=%d, timeout.tv_usec= %d \n", (int)timeout.tv_sec, (int)timeout.tv_usec);
switch (select((smd_fd+1), &fds, NULL, NULL, &timeout)) // 函数原型int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
//switch (select(smd_fd + 1, &fds, NULL, NULL, NULL)) // block mode
{
case -1:
{
printf("< select error >\n");
return -1;
}
case 0:
{
printf("< time out >\n");
return 1;
}
default:
if (FD_ISSET(smd_fd, &fds)) //检查集合中指定的文件描述符是否可以读写
{
do {
memset(strAT, 0, sizeof(strAT));
memset(strResponse, 0, sizeof(strResponse));
rdLen = read(smd_fd, strResponse, lenToRead); //strResponse存放串口收到的信息
printf(">>Read response/urc, len=%d, content:\n%s\n", rdLen, strResponse);
//printf("rcv:%s", strResponse);
//printf("final rsp:%s", strFinalRsp);
if ((rdLen > 0) && (strstr(strResponse, strFinalRsp)))
{
if (strstr(strResponse, strFinalRsp) // final OK response 在字符串strResponse中查找第一次出现字符串 strFinalRsp 的位置,不包含终止符 '\0'。若找到返回该位置,若没找到返回NULL。
|| strstr(strResponse, "+CME ERROR:") // +CME ERROR
|| strstr(strResponse, "+CMS ERROR:") // +CMS ERROR
|| strstr(strResponse, "ERROR")) // Unknown ERROR
{
printf("\n< match >\n");
bRcvFinalRsp = 1;
}
else
{
// printf("\n< not final rsp >\n");
}
}
} while((rdLen > 0) && (strstr(strResponse, strFinalRsp))); //while ((rdLen > 0) && (lenToRead == rdLen));
//printf("读到了传回的数据\n");
}
else
{
printf("FD is missed\n");
}
break;
}
// Found the final response , return back
if (bRcvFinalRsp)
{
memset(strResponse, 0, sizeof(strResponse));
break; //跳出while循环
}
}
return 0;
}
void serial_init(int fd) //串口初始化函数
{
struct termios options;
tcgetattr(fd, &options);
//CLOCAL和CREAD分别用于本地连接和接受使能,
//因此,首先要通过位掩码的方式激活这两个选项
options.c_cflag |= ( CLOCAL | CREAD );
//设置数据位为八位
options.c_cflag &= ~CSIZE;
options.c_cflag &= ~CRTSCTS;
options.c_cflag |= CS8;
options.c_cflag &= ~CSTOPB;
options.c_iflag |= IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
cfsetispeed(&options, B9600); //波特率设置
cfsetospeed(&options, B9600);
//cfsetispeed(&options, B115200);
//cfsetospeed(&options, B115200);
tcsetattr(fd,TCSANOW,&options);
}
void delay(unsigned int xms) // 延时函数
{
unsigned int x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}