SIM900 Как прочитать длинное SMS с SIM карты?

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Добрый день!

Подскажите пожалуйста, как прочитать длинное SMS с SIM карты?

Есть код

void setup() {
  Serial.begin(115200);
  // SIM900
  Serial1.begin(115200);
  String vGSMStr = "";

  Serial.println("Start");
  // подавляем эхо
  Serial1.println("ATE0");
  delay(200);
  // выключаем текстовый текстовый режим потому как SMS в PDU
  Serial1.println("AT+CMGF=1");
  delay(200);
  // чистим буфер
  while (Serial1.available()) {
    Serial1.read();
  }
  // читаем первую СМС
  Serial1.println("AT+CMGR=1,1");
  delay(500);
  while (Serial1.available()) {
    // чиаем побайтово
    char inChar = (char)Serial1.read();
    vGSMStr += inChar;
  }
  // выводим в  Serial
  Serial.println("1 SMS");
  Serial.println(vGSMStr);
  vGSMStr = "";
  // читаем вторую СМС
  Serial1.println("AT+CMGR=2,1");
  delay(500);
  while (Serial1.available()) {
    // чиаем побайтово
    char inChar = (char)Serial1.read();
    vGSMStr += inChar;
  }
  // выводим в  Serial
  Serial.println("\n\n2 SMS");
  Serial.println(vGSMStr);
  vGSMStr = "";
}

void loop(void) {

}

На SIM карте есть 2 SMS. Пробую их читать командой "AT+CMGR"

Но получаю такую картину, СМС не выводится полностью, только статус, номер и дата. Самого текста СМС нет.

Порылся на форумах, нашел решение. Надо увеличить буфер для чтения в файле HardwareSerial.h
было 64, поставил 256.

Все заработало, теперь выводит и текст и сообщение,  но решение меня не совсем устраивает, так как солидно кушает память.

Буфер 256 - Глобальные переменные используют 1 193 байт (14%) динамической памяти  
Буфер 64 -  Глобальные переменные используют 425 байт (5%) динамической памятил

Подскажите может кто сталкивался с подобной проблемой, можно както решить не внося изменения в стандартные библиотеки?

Araris
Offline
Зарегистрирован: 09.11.2012

Смею утверждать, что других решений не существует (хотя рад бы ошибиться).

Jatixo
Offline
Зарегистрирован: 13.01.2016

Во-первых, я думаю необязательно оба буфера увеличивать, можно только RX.

Во-вторых, дело в том, что сначала отправляется команда прочитать такую-то смс, а потом delay(500), пока это время идет, вся смс уже отправлена на вход микроконтроллера, а места в буфере не хватает, так как надо считывать постоянно в loop, без delay.

Можно придумать еще вариантик, что-то типа:
 







  Serial1.println("AT+CMGR=1,1");
  for(byte i=0; i<255; i++)
  {
	  delay(1);
	  if(Serial1.available())
		  break;
  }
  while (Serial1.available()) {
    // читаем побайтово
    char inChar = (char)Serial1.read();
    vGSMStr += inChar;
  }

Теоретически должно сработать, но вариант так себе, если не сработает, можно попробовать скорость поставить меньше, например Serial1.begin(9600); еще можно почитать про функции readBytesUntil() и readStringUntil() и т.п., там есть timeout, может тоже что получится.

Araris
Offline
Зарегистрирован: 09.11.2012

Jatixo пишет:

Во-первых, я думаю необязательно оба буфера увеличивать, можно только RX.

Именно так, размер буфера передачи (TX) никоим образом не связан с обсуждаемой проблемой. 

Jatixo пишет:

Во-вторых, дело в том, что сначала отправляется команда прочитать такую-то смс, а потом delay(500), пока это время идет, вся смс уже отправлена на вход микроконтроллера, а места в буфере не хватает, так как надо считывать постоянно в loop, без delay.

...

Теоретически должно сработать, но вариант так себе, если не сработает, можно попробовать скорость поставить меньше, например Serial1.begin(9600); еще можно почитать про функции readBytesUntil() и readStringUntil() и т.п., там есть timeout, может тоже что получится.

Я делал ряд попыток пойти этим путем, успехом не увенчалось. А потом как-то прочитал внятную статью о том, как работает последовательный порт и его буферизация. Увы.

Jatixo
Offline
Зарегистрирован: 13.01.2016

Странно, я считывал смс в loop без изменений конф. файлов, правда через SoftwareSerial стандартным примером, и там вроде буфер тоже 64, если только он по-другому работает...

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Jatixo пишет:

еще можно почитать про функции readBytesUntil() и readStringUntil() и т.п., там есть timeout, может тоже что получится.

 

Спасибо огромное, вот этот код работает

// читаем вторую СМС
  Serial1.println("AT+CMGR=2,1");

String strR = Serial1.readStringUntil(1310);
  Serial.println("2 SMS");
  Serial.println(strR);

Но компилятор пишет предупреждение

C:\Users\ASK\Documents\Arduino\ReadSMS\ReadSMS.ino: In function 'void setup()':

C:\Users\ASK\Documents\Arduino\ReadSMS\ReadSMS.ino:33:43: warning: overflow in implicit constant conversion [-Woverflow]

 String strR = Serial1.readStringUntil(1310);

                                           ^

можно так

strR = Serial1.readStringUntil(10);

но тогда надо читать 3 строки.

с readBytesUntil() пока не получилось, вместо сообщения возвращает "яяяяяяяяяяяяяяяяя...."

Но главное направление верное! Буду копать!

Jatixo
Offline
Зарегистрирован: 13.01.2016

А с циклом for не работает?

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Jatixo пишет:

А с циклом for не работает?

 не захотел. да и логики я не понял

переписал так

Serial1.println("AT+CMGR=1,1");
for(byte i=0; i<255; i++)
{
 delay(1);
 if(Serial1.available())
  {
    char inChar = (char)Serial1.read();
    vGSMStr += inChar;
  }
}

выводит гдето 80 символов и все. останавливается

Araris
Offline
Зарегистрирован: 09.11.2012

Andrey12 пишет:

Спасибо огромное, вот этот код работает

// читаем вторую СМС
  Serial1.println("AT+CMGR=2,1");

String strR = Serial1.readStringUntil(1310);
  Serial.println("2 SMS");
  Serial.println(strR);

Интересно-интересно, эту функцию не пробовал, тоже займусь. 

UPD: Хотя в исходнике её ничего неожиданного не вижу:

String Stream::readStringUntil(char terminator)
{
  String ret;
  int c = timedRead();
  while (c >= 0 && c != terminator)
  {
    ret += (char)c;
    c = timedRead();
  }
  return ret;
} 

 

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Araris пишет:

Интересно-интересно, эту функцию не пробовал, тоже займусь. 

UPD: Хотя в исходнике её ничего неожиданного не вижу:

String Stream::readStringUntil(char terminator)
{
  String ret;
  int c = timedRead();
  while (c >= 0 && c != terminator)
  {
    ret += (char)c;
    c = timedRead();
  }
  return ret;
} 

 

Araris Если не трудно протестируйте. 

  // читаем вторую СМС
  Serial1.println("AT+CMGR=2,1");

  String strR = Serial1.readStringUntil(0);
  Serial.println("2 SMS");
  Serial.println(strR);

 

Вот такой код работает отлично, то есть в конце строка выдает 0 в принципе условие

while (c >= 0 && c != terminator)

как ни странно срабатывает нормально.

Jatixo
Offline
Зарегистрирован: 13.01.2016

Andrey12 пишет:

 не захотел. да и логики я не понял

переписал так









Serial1.println("AT+CMGR=1,1");
for(byte i=0; i<255; i++)
{
 delay(1);
 if(Serial1.available())
  {
    char inChar = (char)Serial1.read();
    vGSMStr += inChar;
  }
}

выводит гдето 80 символов и все. останавливается

Странно, так то точно неправильно будет работать, попробуйте так:







Serial1.println("AT+CMGR=1,1");
for(int i=0; i<500; i++)
{
  if(Serial1.available())
    break;
  delay(1);
}
while (Serial1.available()) {
  // читаем побайтово
  char inChar = (char)Serial1.read();
  vGSMStr += inChar;
}

Точно должно заработать (правда если модуль шлет данные не останавливаясь, любой затык и выход из while, в таком случае в цикл while можно добавить delay(1) или больше, но это все неправильно, лучше той функцией). Логика простая, делим delay(500) на 500 по 1 мс, и каждый раз проверяем не пришли ли данные, если пришли, останавливаем цикл и дальше начинаем читать =)

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Jatixo пишет:

Точно должно заработать =) Логика простая, делим delay(500) на 500 по 1 мс, и каждый раз проверяем не пришли ли данные, если пришли, останавливаем цикл и дальше начинаем читать =)

Вот код

void setup() {
  Serial.begin(115200);
  // SIM900
  Serial1.begin(115200);
  String vGSMStr = "";

  Serial.println("Start");
  // подавляем эхо
  Serial1.println("ATE0");
  delay(200);
  // выключаем текстовый текстовый режим потому как SMS в PDU
  Serial1.println("AT+CMGF=0");
  delay(200);
  // чистим буфер
  while (Serial1.available()) {
    Serial1.read();
  }
  // читаем первую СМС
  Serial1.println("AT+CMGR=2,1");
  delay(500);
  while (Serial1.available()) {
    // чиаем побайтово
    char inChar = (char)Serial1.read();
    vGSMStr += inChar;
  }
  // выводим в  Serial
  Serial.println("1 SMS");
  Serial.println(vGSMStr);

  vGSMStr = "";

  // читаем вторую СМС
  Serial1.println("AT+CMGR=2,1");

  vGSMStr = Serial1.readStringUntil(0);
  Serial.println("2 SMS");
  Serial.println(vGSMStr);


  Serial1.println("AT+CMGR=2,1");
  for (int i = 0; i < 500; i++)
  {
    if (Serial1.available())
      break;
    delay(1);
  }
  while (Serial1.available()) {
    // читаем побайтово
    char inChar = (char)Serial1.read();
    vGSMStr += inChar;
  }
  Serial.println("3 SMS");
  Serial.println(vGSMStr);

}

void loop(void) {

}

Вот что в терминале

1 SMS

+CMGR: 0,"",159
07919761980614F8400B919762113392F30008612032

2 SMS

+CMGR: 0,"",159
07919761980614F8400B919762113392F30008612032819521218C05000386030204400434044604300020002D00200433043E0440044F044704350433043E002100200414044004430437043504390020002D0020043204350440043D044B04450021002004140435043D043504330020002D0020043F043E0431043E043B044C0448043500210020041F043E04370434044004300432043B044F044E002004410020043C0443

OK

3 SMS

+CMG

Если ставлю скорость

Serial1.begin(9600);

То ничего не выводит. Скорость у модуля SIM900 стоит в АВТО.

Jatixo
Offline
Зарегистрирован: 13.01.2016

Ясно, спасибо за проверку, значит все таки микроконтроллер быстрее модуля хочет считать данные, что в принципе логично, несколько МГц все таки, думаю с delay в while заработает, но как уже написал это неправильно, надо ведь ждать только когда нет данных, нет смысла ждать когда они еще есть.