GSM модем отвечает слишком поздно
У меня есть модем GSM, что настройка:
1. BaudRate 9600
2. Базы данных 8
3. Нет паритета
4. Стопбит 1
5. Нет управления потоком
И мой Os - Ubuntu. После отправки AT-команды я написал sleep(2) секунды, чтобы получить ответ. Но почему это слишком поздно? И как я могу решить это?
это мой код для чтения данных:
string PDUSMS::readstring(int fd)
{
int n = 0,
spot = 0;
char buf = '\0';
/* Whole response*/
char response[1024];
memset(response, '\0', sizeof response);
n=read(fd,&response,1024);
//---------------------------
if (n < 0) {
std::cout << "Error reading: " << strerror(errno) << std::endl;
}
else if (n == 0) {
std::cout << "Read nothing!" << std::endl;
}
else {
std::cout << "Response: " << response << std::endl;
}
string str(response);
return str;
//---------------------------------------------------
}
как сделать быстрое чтение. прочитать всю строку ответа?
это весь мой код:
int fd; /* File descriptor for the port */
/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/
int openport(void)
{
fd=open("/dev/ttyS1",O_RDWR|O_NOCTTY|O_NDELAY);
if (fd==-1)
{
perror("open_port: unable to open port\n");
return -1;
}
else
{
printf("open_port: succesfully open port /dev/ttyUSB0\n");
fcntl(fd,F_SETFL,0);
return 1;
}
}
//========================================================================
void closeport(void)
{
close(fd);
}
void configport(void)
{
struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);
/* Error Handling */
if ( tcgetattr ( fd, &tty ) != 0 ) {
std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
}
/* Save old tty parameters */
tty_old = tty;
/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B9600);
cfsetispeed (&tty, (speed_t)B9600);
/* Setting other Port Stuff */
tty.c_cflag &= ~PARENB; // Make 8n1
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS; // no flow control
tty.c_cc[VMIN] =0;// 1; // read doesn't block
tty.c_cc[VTIME] = 2;// 5; // 0.5 seconds read timeout
tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
/* Make raw */
cfmakeraw(&tty);
/* Flush Port, then applies attributes */
tcflush( fd, TCIFLUSH );
if ( tcsetattr ( fd, TCSANOW, &tty ) != 0) {
std::cout << "Error " << errno << " from tcsetattr" << std::endl;
}
}
//------------------------------------------------------------
string PDUSMS::SendandReciveData(string s,int fd)
{
int i;
string o,e,t;
try
{
cout<<" we had sent:"<<s<<"\n";
SendString(s,fd);
sleep(1);
o=readstring(fd);
// for(int i=0;i<3;i++)
// if (o.find(s)!=-1)
// {
// sleep(1.5);
// o=readstring(fd);
// }
cout<< " we got :"<<o<<"\n";
i = StateStr(o, s); //remove source command from the beging of string
if (i >= 0) //-becasause the command return back to us
o = copy(o, s.length(), o.length() - s.length()); //return command to caller
}
catch(const std::exception&)
{
o = " ";
}
return o;
}
void PDUSMS::SendString(string s,int fd)
{
char buf[255];
strcpy(buf,s.c_str());
write(fd, buf, s.length());
// usleep(500);
}
string PDUSMS::readstring(int fd)
{
int n = 0,
spot = 0;
char buf = '\0';
/* Whole response*/
char response[1024];
memset(response, '\0', sizeof response);
n=read(fd,&response,1024);
//---------------------------
if (n < 0) {
std::cout << "Error reading: " << strerror(errno) << std::endl;
}
else if (n == 0) {
std::cout << "Read nothing!" << std::endl;
}
else {
std::cout << "Response: " << response << std::endl;
}
string str(response);
return str;
//---------------------------------------------------
}
bool PDUSMS::SendSMS(int fd,string Num,string Text,int MR,int CMR,int SMS_PART,int sms_id,int &sms_index,bool Delivery,bool MagicSMS,bool &Deliverd)
{
string c, o, id;
int i, l, Curr_PART, R_MR;
string SNum, SDate, STime, PDU_Data, SMSC_Num, RTime, RDate, num1;
ReceievedMessageKind PDU_Data_Type;
bool sent, deliv;
string Temp;
MagicSMS=false;
string result=" ";
result=SendandReciveData("AT+CSMP=49,167,0,0\r",fd);
result=SendandReciveData("AT+CNMI=2,2,0,1,0\r",fd);
c = "AT+CMGS="; // at commmand for s} SMS
o = EncodePDU(Num, Text, MR, CMR, SMS_PART, sms_id, Delivery, MagicSMS);
c = c + IntToStr(o.length()/ 2 - 1); //Adding length of Pdu to at command
c += "\r"; //adding <CR> to at comm &&
Temp = SendandReciveData(c,fd); //send at command to phone
o += (char)26; //add <CTRL-Z> to the PDU Text
Temp = SendandReciveData(o,fd); //S} Text To The Phone
}
это мой вывод без сна:
open_port: успешно открыт порт /dev/ttyUSB0, который мы отправили: AT Ответ: AT мы получили: ATAT, который мы послали: AT Ответ:
у нас есть:
мы отправили: AT ответ: O мы получили: O OO мы отправили: AT ответ: K мы получили: K KK мы отправили: AT ответ:
у нас есть:
мы отправили: AT Ответ: A мы получили: A AA мы послали: AT Ответ: T мы получили: T TT мы отправили: AT Ответ: мы получили: мы отправили: AT Ответ: A мы получили: A AA мы отправил: AT Ответ: T мы получили: T TT мы отправили: AT Ответ: Awe получил: A мы отправили: AT Ответ: T мы получили: T TT мы отправили: ATssponse: ATe получили: мы отправили: Ответ AT: A, который мы получили: AA, который мы послали: AT Ответ: T, который мы получили: T, который мы послали: AT Ответ: OK
мы получили: ОК
Хорошо
ОК, мы отправили:AT+CSMP=49 167,0,0 Ответ: мы получили: мы отправили:AT+CNMI=2,2,0,1,0 Ответ:
у нас есть:
мы отправили:AT+CMGS=20 Ответ: ОК
ОК, мы получили: ОК
ОК, мы отправили:0031010c918939881454270000AA06f3701bce2e03 Ответ: мы получили: Ответ:
O Ответ: KA ATsponse: T Aesponse: AT Ответ: T ATsponse: T Ответ: T ATsponse: T ATsponse: Ответ: AT + CS Ответ: MP=49 Ответ:,167, Aesponse: 0,0 Ответ: T+ Ответ CN: MI=2, Ответ: 2,0,1 Ответ:,0 Ответ: +CMGS Ответ: =20 Ответ: 00310 Ответ: 10c91893 Ответ: 98 Ответ: 81454 Ответ: 2700 Ответ: 00AA0 Ответ: 6f370 Ответ: 1bce2 Ответ: e03 Ответ: ОК Ответ:
Ответ: ОК Ответ:
ОК Ответ:
Ответ: ОК
Ответ: ОК
Ответ: ОК Ответ:
ОК Ответ:
Ответ: ОК
Ответ: ОК
Ответ: ОК Ответ:
ОК Ответ:
Ответ: ОК
Ответ: ОК
Отклик:
Ответ: Ответ: +CUSD Ответ:: 0,"Ответ: Хазин Ответ: e SM Ответ: S: 2 Ответ: 0 Ответ: 9 Ria Ответ: 1. Et Ответ: ebar Ответ: asl Ответ: i Ответ:: 13623 Rial. Shegeftzad Ответ: eh sh Ответ: avid Ответ:! Ответ: Ответ: Ba s Ответ: homar Ответ: например Ответ: i Ответ: ry c Ответ: o Ответ: de*44 Ответ: 44*1# Ответ: tarh Ответ: e v Ответ: i Ответ: je kh Ответ: od r Ответ: a Ответ: dar Ответ: y Ответ: aft k Ответ: oni Ответ: d Ответ: ",15 Ответ:
Ответ: + CM Ответ: G Ответ: S: 21 Ответ: 8
O Ответ: K Ответ: Ответ:
Отклик:
Ответ: +CUSD: Ответ: 2
Отклик:
Ответ: +CDS: Ответ: 25
Ответ: 0 Ответ: 006D Ответ: A Ответ: 0C9 Ответ: 1 Ответ: 8939 Ответ: 8 Ответ: 8145 Ответ: 4 Ответ: 2751 Ответ: 1 Ответ: 16131 Ответ: 016 Ответ: 3 Ответ: 4151 Ответ: 1 Ответ: 1613 Ответ: 1 Ответ: 0183 Ответ: 4 Ответ: 100 Ответ:
1 ответ
Вывод, кажется, указывает на наличие команды echo. Либо отключите эхо на модеме, либо будьте готовы прочитать 2 строки на каждую написанную команду.
У вас есть настройка последовательного порта для неканонического (он же необработанного) режима. Необработанное чтение заканчивается количеством байтов и / или временем, которое ненадежно для чтения строки. Пока модем находится в командном режиме, модем будет отправлять свои ответы в виде строк.
Таким образом, ваша программа должна прочитать строку (которая является каноническим вводом) с модема. Либо (a) поместите read() в цикл, который объединяет входные данные, пока не будет получен разделитель строки, ИЛИ (b) настройте канонический ввод вместо raw.
Сырой режим
Чтобы надежно читать строки, используя неканонический режим, программа должна справиться с наихудшим сценарием завершения строки, полученным в середине возвращенного буфера (а не тривиальным случаем последнего полученного символа). Чтобы справиться с этим, между системными вызовами чтения должен поддерживаться статический буфер для хранения частично полученной строки и для сохранения ввода после разделителя строки для "следующей" строки.
static char response[1024] = {0};
static int offset = 0;
string PDUSMS::readline(int fd)
{
int n;
char line[1024];
char *nlp;
while ((nlp = strpbrk(&response[offset], "\n\r")) == NULL) {
n = read(fd, &response[offset], sizeof(response) - offset - 1);
if (n < 0) {
std::cout << "Error reading: " << strerror(errno) << std::endl;
continue;
}
offset += n;
response[offset] = '\0';
if (offset >= sizeof(response) - 1) {
nlp = &response[offset - 1];
break;
}
}
std::cout << "Response: " << response << std::endl;
/* extract a line from the buffer */
strncpy(line, response, nlp - response + 1);
line[nlp - response + 1] = '\0';
/* move remnant string to beginning */
strcpy(response, nlp + 1);
offset = strlen(response);
string str(line);
return str;
}
Примечание: код не проверен и по сути является C. Я не знаю C++.
Канонический режим
Согласно справочной странице по Linux для termios(3)
В каноническом режиме:
Ввод сделан доступным построчно. Строка ввода доступна, когда введен один из разделителей строки (NL, EOL, EOL2; или EOF в начале строки). За исключением случая EOF, разделитель строк включается в буфер, возвращаемый read(2).
Разрешено редактирование линии (ERASE, KILL; и если установлен флаг IEXTEN: WERASE, REPRINT, LNEXT). Read (2) возвращает максимум одну строку ввода; если read (2) запросил меньше байтов, чем доступно в текущей строке ввода, то будет прочитано только столько байтов, сколько запрошено, а остальные символы будут доступны для чтения в будущем (2).
Чтобы настроить последовательный порт для канонического режима для модема в командном режиме (вместо ввода терминала общего назначения), в вашем configport() удалите три оператора (с их комментариями):
tty.c_cc[VMIN] =0;// 1; // read doesn't block
tty.c_cc[VTIME] = 2;// 5; // 0.5 seconds read timeout
/* Make raw */
cfmakeraw(&tty);
(Обязательно сохраните настройки CREAD | CLOCAL.)
И вставить новые заявления:
tty.c_iflag |= ICRNL | IGNBRK;
tty.c_iflag &= ~(IXON | IXOFF | IXANY | INLCR);
tty.c_lflag |= ICANON | ISIG | IEXTEN;
tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ECHOKE);
Вызов read() в вашей readstring() вернет полную строку ввода (включая символ '\n'). Если модем завершает свою строку и "\ n", и "\ r", имейте в виду, что эта конфигурация будет содержать пустую строку (потому что каждый "\ r" будет преобразован в "\ n").
Обратите внимание, что канонический режим может быть неуместным, когда ваша программа переводит модем из командного режима в прозрачный режим. Если данные не являются чистым текстом ASCII, но содержат двоичные значения, то программа должна переключить порт в необработанный режим, когда модем переключает режим.
Справочные руководства см. В Руководстве по последовательному программированию для операционных систем POSIX.
и установка режимов терминала правильно.
Несколько советов относительно AT-команд в целом, которые полезно знать перед написанием одной строки кода:
У модемов по умолчанию включено эхо. Примите это во внимание при разработке приложения, особенно если вы решите читать построчно, а не выполнять обычное чтение, время ожидания которого истекло.
Чтобы отключить эхо, подайте команду "ATE0" модему
AT+CMGS имеет следующее поведение:
- Хост отправляет "AT+CMGS="
- Модем немедленно отправляет подсказку ('>')
- Хост отправляет данные либо PDU, либо текст, оканчивающийся CTRL + Z char
- !! Некоторое время в зависимости от сети!
- Хорошо
Таким образом, не предполагайте, чтобы получить ОК "немедленно". Это может занять несколько десятков секунд!
- Вы тратите некоторые усилия на отправку SMS в режиме PDU, но также можно использовать режим TEXT:
- AT + CMGF = 1 (для переключения в текстовый режим)
- AT+CMGS=
'>' текст для отправки