原文地址::https://blog.csdn.net/qq_35570735/article/details/87180048?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.edu_weight
相关文章
1、移远 EC20 模组(4G通信模组)AT指令测试 TCP 通信过程----https://blog.csdn.net/Mculover666/article/details/105877183?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight
2、EC20 HTTP 图片传输----https://blog.csdn.net/m0_37777700/article/details/104696505?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.edu_weight
我们公司和移远有合作所以得到了他们EC20的二次开发的开发板。下面是我开发4G图像传输的总结(工作方式有两种,一种AT一种socket)。
下面先是AT命令
一.在linux下解压文件
解压如下文件
二.安装SDK
每次开新的终端进行编译都要进行环境变量的设置
三.进入历程
四.修改历程
#include "ql_oe.h"
#include
#include
#include
#include
#include
#include
#include
#include
//those define was added by myself
#define MAXLINE 1024 //一次读取最大的字节数
#define QUEC_AT_PORT "/dev/smd8"
static int smd_fd = -1;
int Ql_SendAT(char* atCmd, char* finalRsp, long timeout_ms, int should_Add);
int main(int argc, char* argv[])
{
FILE *from_file; //need send to server
char buf[MAXLINE];
char receive_buf[100];
int read_len;
char str[10];
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);
Ql_SendAT("AT+COPS?", "OK", 1000, 0); //检查网络
if((from_file=open("timg.jpg",O_RDWR))==NULL) //打开要读取的文件
{
printf("Can't open the file \n");
exit(1);
}
Ql_SendAT("AT+QIOPEN=1,2,\"UDP SERVICE\",\"127.0.0.1\",0,3030,0", "OK", 1000, 0); //建立UDP服务
Ql_SendAT("AT+QISTATE=0,1", "OK", 1000, 0);
while ( (read_len = read(from_file, buf, MAXLINE)) > 0 ) //读取文件知道读完
{
printf("read_len = %d\n", read_len);
sprintf(str,"%d",read_len); //将int型整数转为char型
sprintf(receive_buf, "AT+QISEND=2,%s,\"148.70.42.237\",3333", str); //合成发送命令
Ql_SendAT(receive_buf, "OK", 1000, 0); //发送发送命令
Ql_SendAT(buf, "SEND OK", 5000, read_len); //添加发送的数据
bzero(receive_buf, 100); //清空数组
bzero(buf, MAXLINE);
}
printf("read_len = %d\n", read_len);
Ql_SendAT("AT+QICLOSE=2", "OK", 1000, 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)//should_Add是判断是否末尾要加入\r\n,如果不添加则还代表atCmd数据的长度
{
int iRet;
int iLen;
fd_set fds;
int rdLen;
#define lenToRead 100
char strAT[100];
char strFinalRsp[100];
char strResponse[100];
struct timeval timeout = {0, 0};
boolean bRcvFinalRsp = FALSE;
sprintf(strFinalRsp, "\r\n%s", finalRsp);
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = timeout_ms % 1000;
// Send AT
if( should_Add != 0 )
{
iRet = write(smd_fd, atCmd, should_Add);
printf("iLen = %d", should_Add);
}
else
{
memset(strAT, 0x0, sizeof(strAT));
iLen = sizeof(atCmd);
strncpy(strAT, atCmd, iLen);iLen = strlen(atCmd);
if ((atCmd[iLen-1] != '\r') && (atCmd[iLen-1] != '\n'))
{
iLen = sprintf(strAT, "%s\r\n", atCmd);
strAT[iLen] = 0;
}
iRet = write(smd_fd, strAT, iLen);
}
//printf(">>Send AT: \"%s\", iRet=%d\n", atCmd, iRet);
// Wait for the response
while (1)
{
FD_ZERO(&fds);
FD_SET(smd_fd, &fds);
//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))
//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(strResponse, 0x0, sizeof(strResponse));
rdLen = read(smd_fd, strResponse, lenToRead);
//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
|| strstr(strResponse, "+CME ERROR:") // +CME ERROR
|| strstr(strResponse, "+CMS ERROR:") // +CMS ERROR
|| strstr(strResponse, "ERROR")) // Unknown ERROR
{
//printf("\n< match >\n");
bRcvFinalRsp = TRUE;
}else{
printf("\n< not final rsp >\n");
}
}
} while ((rdLen > 0) && (lenToRead == rdLen));
}else{
printf("FD is missed\n");
}
break;
}
// Found the final response , return back
if (bRcvFinalRsp)
{
break;
}
}
return 0;
}
然后make编译 注意字符串格式获取长度少用strlen,因为0x00是字符串的结束符。如果发送二进制遇到0x13,0x10这些出问题可以修改串口的属性。
五.进入开发板的linux系统
六.上传文件
使用Zmoden上传文件。注意移远给的系统只有usrdata是可写的。然后修改权限为可执行。
chmod 777 example_at
七.运行程序
./example_at
下面是socket程序,运行方法和上方一样
先要拨号,不然无法socket运行起来,下面是拨号程序
#include
#include
#include
#include
#include
#include
#include "ql_wwan_v2.h"
static void data_call_state_callback(ql_data_call_state_s *state)
{
printf("profile id %d ", state->profile_idx);
printf("IP family %s ", QL_DATA_CALL_TYPE_IPV4 == state->ip_family ? "v4" : "v6");
if(QL_DATA_CALL_CONNECTED == state->state) {
printf("is Connected\n");
printf("Interface Name: %s\n", state->name);
if(QL_DATA_CALL_TYPE_IPV4 == state->ip_family) {
printf("IP address: %s\n", inet_ntoa(state->v4.ip));
printf("Gateway address: %s\n", inet_ntoa(state->v4.gateway));
printf("Primary DNS address: %s\n", inet_ntoa(state->v4.pri_dns));
printf("Second DNS address: %s\n", inet_ntoa(state->v4.sec_dns));
} else {
char ipv6_buffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, (void *)&state->v6.ip, ipv6_buffer, INET6_ADDRSTRLEN);
printf("IP address: %s\n", ipv6_buffer);
inet_ntop(AF_INET6, (void *)&state->v6.gateway, ipv6_buffer, INET6_ADDRSTRLEN);
printf("Gateway address: %s\n", ipv6_buffer);
inet_ntop(AF_INET6, (void *)&state->v6.pri_dns, ipv6_buffer, INET6_ADDRSTRLEN);
printf("Primary DNS address: %s\n", ipv6_buffer);
inet_ntop(AF_INET6, (void *)&state->v6.sec_dns, ipv6_buffer, INET6_ADDRSTRLEN);
printf("Second DNS address: %s\n", ipv6_buffer);
}
printf("\n");
} else {
printf("is disconnected, and reason code 0x%x\n", state->err);
}
}
int main(int argc, char **argv)
{
int retry = 10;
int loop;
ql_data_call_s data_call[3];
ql_data_call_info_s data_call_info;
ql_data_call_error_e err = QL_DATA_CALL_ERROR_NONE;
memset(&data_call, 0, sizeof(data_call));
/*
* The dialup API relies on the Quectel Manager service. If the program is not initialized successfully,
* debugging these API interfaces will fail, so here is judged whether the service is started normally.
*/
while(0 != QL_Data_Call_Init_Precondition() && 0 != retry) {
printf("The Quectel manager service is not initialized, about 500ms try again.\n");
usleep(500*1000);
retry--;
}
if(0 == retry) {
printf("Data call failure\n");
exit(0);
}
if(QL_Data_Call_Init(data_call_state_callback)) {
printf("Initialization data call failure\n");
exit(0);
}
for(loop = 0; loop < sizeof(data_call)/sizeof(data_call[0]); loop++) {
memset(&data_call_info, 0, sizeof(data_call_info));
err = QL_DATA_CALL_ERROR_NONE;
data_call[loop].profile_idx = loop+1;
/*
* If your data call program is coredump, the data call status will not disappear automatically.
* When your program is start again, you need to call the QL_Data_Call_Info_Get interface to get
* the data call status. Because it is already in the data call state, you call QL_Data_Call_Start
* again without callback function
*/
if(QL_Data_Call_Info_Get(data_call[loop].profile_idx, QL_DATA_CALL_TYPE_IPV4, &data_call_info, &err)) {
printf("get profile index %d information failure: errno 0x%x\n", data_call[loop].profile_idx, err);
continue;
}
if(QL_DATA_CALL_CONNECTED == data_call_info.v4.state) {
printf("the profile index %d is already connected, don't up\n", data_call[loop].profile_idx);
continue;
}
data_call[loop].ip_family = QL_DATA_CALL_TYPE_IPV4;
data_call[loop].reconnect = true;
err = QL_DATA_CALL_ERROR_NONE;
if(QL_Data_Call_Start(&data_call[loop], &err)) {
printf("the profile index %d start data call failure: 0x%x\n", data_call[loop].profile_idx, err);
}
printf("the profile index %d start data call success\n", data_call[loop].profile_idx);
usleep(500*1000);
}
sleep(3600);
return 0;
}
然后soket传输图像
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 3333
#define MAXLINE 1024
int main(int argc,char **argv)
{
//new add
/*int retry = 10;
int loop;
ql_data_call_s data_call[3];
ql_data_call_info_s data_call_info;
ql_data_call_error_e err = QL_DATA_CALL_ERROR_NONE;
memset(&data_call, 0, sizeof(data_call));*/
int sockfd;
int read_len;
FILE *from_file; //need send to server
char buf[MAXLINE];
struct sockaddr_in cliaddr;
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(3333);
cliaddr.sin_addr.s_addr = inet_addr("148.70.42.237");
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
fprintf(stderr,"Socket Error:%s\n",strerror(errno));
exit(1);
}
if((from_file=open("timg.jpg",O_RDWR))==NULL)
{
printf("Can't open the file \n");
exit(1);
}
while ( (read_len = read(from_file, buf, MAXLINE)) > 0 )
{
sendto(sockfd, buf ,read_len, 0 , (struct sockaddr*)&cliaddr, sizeof(cliaddr));
bzero(buf, MAXLINE);
}
close(from_file);
close(sockfd);
}
程序也是先运行拨号的再运行socket