Sim900 + Длинное Смс

Тема в разделе "Arduino & Shields", создана пользователем KorPaEv, 28 янв 2016.

  1. KorPaEv

    KorPaEv Нерд

    Доброго времени суток, уважаемые!
    Собственно, есть вот такой вот примерчик отсюда
    http://wiki.amperka.....8C:gprs-shield

    Меня интересует прием смс
    Код (C++):
    #include <SoftwareSerial.h>

    SoftwareSerial gprsSerial(7, 8);

    //для зелёного светодиода будем использовать второй цифровой вход,
    //а для жёлтого - третий
    int greenPin = 2;
    int yellowPin = 3;

    void setup()
    {
            gprsSerial.begin(19200);
            pinMode(greenPin, OUTPUT);
            pinMode(yellowPin, OUTPUT);

            // Настраиваем приём сообщений с других устройств
            // Между командами даём время на их обработку
            gprsSerial.print("AT+CMGF=1\r");
            delay(300);
            gprsSerial.print("AT+IFC=1, 1\r");
            delay(300);
            gprsSerial.print("AT+CPBS="SM"\r");
            delay(300);
            gprsSerial.print("AT+CNMI=1,2,2,1,0\r");
            delay(500);
    }

    String currStr = "";
    // Переменная принимает значение True, если текущая строка является сообщением
    boolean isStringMessage = false;

    void loop()
    {
            if (!gprsSerial.available())
                    return;

            char currSymb = gprsSerial.read();
            if ('\r' == currSymb) {
                    if (isStringMessage) {
                            //если текущая строка - SMS-сообщение,
                            //отреагируем на него соответствующим образом
                            if (!currStr.compareTo("Green on")) {
                                    digitalWrite(greenPin, HIGH);
                            } else if (!currStr.compareTo("Green off")) {
                                    digitalWrite(greenPin, LOW);
                            } else if (!currStr.compareTo("Yellow on")) {
                                    digitalWrite(yellowPin, HIGH);
                            } else if (!currStr.compareTo("Yellow off")) {
                                    digitalWrite(yellowPin, LOW);
                            }
                            isStringMessage = false;
                    } else {
                            if (currStr.startsWith("+CMT")) {
                                    //если текущая строка начинается с "+CMT",
                                    //то следующая строка является сообщением
                                    isStringMessage = true;
                            }
                    }
                    currStr = "";
            } else if ('\n' != currSymb) {
                    currStr += String(currSymb);
            }
    }
    Тут все очевидно...
    Читаем сериал GSM, собираем строку и отлавливаем по окончании строки флажок - СМС сообщение (+CMT)
    Далее читаем продолжение этого самого смс сообщения...Но вот в чем проблема - Когда приходит первая часть смс в виде

    +CMT: "+79999999999","NAME","16/01/28,00:36:38+24"

    эта самая часть видимо умещается в 64 символа и все бы ничего, если бы вторая часть сообщения умещалась в эти же самые 64 символа. Не знаю почему, но модуль не умеет скорее всего принимать все 160 символов разрешенные на одну смс и пересылает 160 символов частями по 64...
    Поэтому запустив данный пример и отослав просто сообщение длинной в 140 символов ничего кроме вышеупомянутой строки в мониторе порта я не увидел. Отослав же сообщение длинной в 60 символов мы видим все красиво

    +CMT: "+79999999999","NAME","16/01/28,00:36:38+24"
    qwerty qwerty qwerty qwerty

    Вопрос - каким образом можно собрать строку из этих частей оставшихся, если сообщение больше 64 символов?
    Пробовал таким способом, непосредственно с вики

    Код (C++):
    if (gprsSerial.available()) // if date is comming from softwareserial port ==> data is comming from gprs shield
    {
       while (gprsSerial.available()) // reading data into char array
       {
             bufGsm[countBufGsm++] = gprsSerial.read(); // writing data into array
             if (countBufGsm == 64)
             {
               break;
             }
       }
            Serial.write(buffer, count);
       clearBufferArray(); // call clearBufferArray function to clear the storaged data from the array
       countBufGsm = 0; // set counter of while loop to zero
    }
    И вроде даже в Serial.write(buffer, count); выводится полное сообщение но опять же каким образом тут можно собрать строку?
    Пытался сделать следующее
    Код (C++):
    char bufGsm[64]; // buffer array for data recieve over serial port
    String inputGsmStr = ""; //входящая строка с gsm модема
    int countBufGsm = 0;

    void Manage()
    {
    if (gprsSerial.available()) // if date is comming from softwareserial port ==> data is comming from gprs shield
      {
            while (gprsSerial.available()) // reading data into char array
            {
              bufGsm[countBufGsm++] = gprsSerial.read(); // writing data into array
              if (countBufGsm == 64)
              {
                    break;
              }
            }
             inputGsmStr +=   bufGsm;
            clearBufferArray(); // call clearBufferArray function to clear the storaged data from the array
            countBufGsm = 0; // set counter of while loop to zero
      }
    }

    void clearBufferArray() // function to clear buffer array
    {
      for (int i = 0; i < countBufGsm; i++)
      {
            bufGsm[i] = NULL;
      }
    }
    Но выходит что каждый раз когда мы отрезаем часть по 64 и дописываем в строку, то в inputGsmStr записываются те самые части по 64, потом 128 и т.д..она растет постоянно до тех пор пока не получим полную строку. В таком случае функция проверки, что это СМС сообщение запустится столько раз, сколько у нас частей по 64...

    Какие идеи?
     
  2. KorPaEv

    KorPaEv Нерд

    Победил проблему, может кому и понадобится...
    А смысл вот в чем. Как сказано выше gsm посылает любую смс - будь то одинарная или составная превышающая 160 для латиницы или 64 для кириллицы символов, частями по 64 символа...
    Поэтому ваша смс придет в виде - заголовок + основное тело смс.
    Основное тело смс может опять же состоять из нескольких строк, если встретился символ конца строки то его надо добавить в строку которую мы собираем и будем дальше анализировать.
    Например пришла смс в виде

    \r\n +CMT: "+79999999999","NAME","16/01/28,00:36:38+24" \r\n
    1234567890 \n

    Что произойдет? Сначала я собираю всю строку с заголовком и телом смс.
    Далее бегу по каждому символу строки и собираю подстроку. Как только отлавливаю конец строки, дописываю кего в подстроку и получаю подстроку вида

    +CMT: "+79999999999","NAME","16/01/28,00:36:38+24" \r\n

    Анализирую то что в ней лежит и поднимаю флажок, что дальше будет тело самой СМС..
    Бежим дальше по циклу, отловили опять конец строки - собрали новую подстроку вида

    1234567890 \n

    Соответственно флажок поднят и мы анализируем уже, то что лежит в теле самомй СМС - в моем случае подстрока состояла из нескольких строк, поэтому я внутри уже разбивал данную подстроку на отдельные строки...
    Как то так.
    Код (C++):
    #include <SoftwareSerial.h>
    SoftwareSerial gprsSerial(10, 11); //На меге работает у меня на 10 и 11 пинах, потому как 7 и 8 занят прерываниями

    //инициализация
    void setup(void)
    {
      // Стартуем порт COM
      Serial.begin(9600);
    //Стартуем GSM
      InitGprs();
    }

    //Инициализация и старт GSM модуля
    void InitGprs()
    {
      gprsSerial.begin(9600);
      gprsSerial.print("AT+CMGF=1\r");
      delay(300);
      gprsSerial.print("AT+IFC=1, 1\r");
      delay(300);
      gprsSerial.print("AT+CPBS=\"SM\"\r");
      delay(300);
      gprsSerial.print("AT+CNMI=1,2,2,1,0\r");
      delay(500);
      //Включаем GPRS Shield, эмулируя нажатие кнопки POWER
      pinMode(9, OUTPUT);
      digitalWrite(9, HIGH);  // Подаем High на пин 9
      delay(3000);  // на 3 секунды
      digitalWrite(9, LOW);  // и отпускаем в Low.
      delay(5000);  // Ждём 5 секунд для старта шилда
    }

    void loop(void)
    {
      GsmShieldManage();
    }

    //Обработка смс приемника-передатчика
    //----------------------------------
    boolean isStringMessage = false;
    String currentNumber = ""; //Текущий номер с которого пришло смс
    char bufGsm[64]; // Буфер для данных из смс поому что модуль GSM посылает кусками любую смс
    String inputGsmFullStr = ""; //входящая строка с gsm модема - полная
    String lineFullStr = ""; // Входная строка построчно из inputGsmFullStr
    int countBufGsm = 0; // счетчик символов для буфера
    //----------------------------------
    void GsmShieldManage()
    {
      // Если что то начало падать в GSM
      if (gprsSerial.available())
      {
      // Читаем данные частями в буфер
      while (gprsSerial.available())
      {
      // Пишем буфер по 64 символа
      bufGsm[countBufGsm++] = gprsSerial.read();
      if (countBufGsm == 64)
      {
      break;
      }
      }
      // Собираем строку полную - в том числе и заголовок
      inputGsmFullStr += bufGsm;
      // Функция очистки буфера
      clearBufferArray();
      // Сбрасываем счетчик символов
      countBufGsm = 0;
      }
      // иначе если прекращена передача данных то строка собрана
      else
      {
      //Если строка не пустая
      if (inputGsmFullStr != "")
      {
      //Очистили подстроку главной строки
      lineFullStr = "";
    Serial.println("Curr str = " + inputGsmFullStr);
      //Читаем посимвольно нашу полную СМС и выдергиваем оттуда подстроки
      //А вид она имеет следующий  \r\n +CMT: "+79999999999","NAME","16/01/28,00:36:38+24" \r\n
      //  1234567890 \n
      //  1234567890 \r\n
      for (int i = 0; i < inputGsmFullStr.length(); i++)
      {
      //Если находим символ возврата каретки
      if (inputGsmFullStr.charAt(i) == '\r')
      {
    Serial.println("SEPARATOR END LINE (R) FOUND");
      // если это продолжение полной смс - само тело без заголовка - 1234567890 \n - то обрабатываем то что внутри (команды)
      if (isStringMessage)
      {  
    Serial.println("IS STR MESSAGE OK: " + lineFullStr);
      //Обработка команд
      if (!lineFullStr.compareTo("BAL"))
      {
      // делаем запрос баланса (мтс), а ответ ловится в блоке ниже...
      gprsSerial.print("ATD#100#;\r");
      }
      //Если входная строка содержит "ADD" значит там команда может  быть составной
      //Она имеет вид ADDNUM;1;+79999999999;1;1;1
      //  ADDINF;Ard001; SomeTEXT
      else if (StringContains(lineFullStr, "ADD"))
      {
      //Ищем количество разделителей в lineFullStr (Тело смс) - это может быть и несколько строк
      byte countStr = 0;
      for (int j = 0; j < lineFullStr.length(); j++)
      {
      if (lineFullStr.charAt(j) == '\n')
      countStr++;
      }
      String subLineFullStr = ""; // отдельная подстрока lineFullStr
      for (int i = 0; i < countStr; i++)
      {
      //Нашли строку по символу конца строки
      subLineFullStr = splitString(lineFullStr, '\n', i);
      //Смотрим команду
      if (StringContains(subLineFullStr, "ADDNUM"))
      {
      //ЧТО ТО ДЕЛАЕМ
      }
      //Если строка содержит "ADDINF"
      if (StringContains(currSubStr, "ADDINF"))
      {
      //ЧТО ТО ДЕЛАЕМ
      }
      }
      }
      isStringMessage = false;
      }
      else
      {
      // если это текстовое сообщение
      if (StringContains(lineFullStr, "+CMT"))
      {  
    Serial.println("IS MESSAGE: " + lineFullStr);  
      // читаем текущий номер с которого смс пришло
      currentNumber = lineFullStr.substring(lineFullStr.indexOf("\"") + 1, lineFullStr.indexOf(",") - 1);
    Serial.println(currentNumber);  
      //Подняли флажок что это текстовое сообщение  
      isStringMessage = true;
      }
      // если это звонок
      if (lineFullStr.startsWith("+CLIP"))
      {
    Serial.println("CALL");
      //считали текущий номер и просто ждем 3 сек и скидываем
      currentNumber = lineFullStr.substring(lineFullStr.indexOf("\""), lineFullStr.indexOf(","));
    Serial.println(currentNumber);
      delay(3000);
      gprsSerial.println("ATH0");
      }
      // этот блок отлавливает ответ на запрос баланса и отправляет его смской
      if (lineFullStr.startsWith("+CUSD"))
      {
      lineFullStr = lineFullStr.substring(lineFullStr.indexOf("Balance"), lineFullStr.indexOf("r"));
      delay(1500);
      lineFullStr += " is your balance of number " + currentNumber;
      SendSms(currentNumber, lineFullStr);
      }
      }
      lineFullStr = "";
      } // end if (inputGsmFullStr.charAt(i) == '\r')
      else if ('\n' != inputGsmFullStr.charAt(i))
      {
      lineFullStr += inputGsmFullStr.charAt(i);
      }
      else if ('\n' == inputGsmFullStr.charAt(i))
      {
      lineFullStr += '\n';
      }
      }
      }
      inputGsmFullStr = ""; //Очищаем то что пришло с GSM
      }
    }

    void clearBufferArray() // function to clear buffer array
    {
      for (int i = 0; i < countBufGsm; i++)
      {
      bufGsm[i] = NULL;
      }
    }
     
    rico нравится это.
  3. lerik2703

    lerik2703 Гик

    а вот здесь надо запастись большим шаманским бубном :)
     
  4. KorPaEv

    KorPaEv Нерд

    Пришлось повозиться как видите))
     
  5. lerik2703

    lerik2703 Гик

    не компилируется
     
  6. KorPaEv

    KorPaEv Нерд

    Естественно не компилируется потому что я в исходник не добавлял функцию отправки смс...

    SendSms(currentNumber, lineFullStr);

    Если надо то вот она..
    Код (C++):
    // Функция отправки смс
    void SendSms(String number, String text)
    {
    Serial.println(number + " ; " + text);
      delay(1500);
      gprsSerial.print("AT+CMGS="); // send the SMS the number
      gprsSerial.print((char)34); // передача в порт символа "
      gprsSerial.print(number); // передача номера телефона
      gprsSerial.print((char)34); // передача в порт символа "
      gprsSerial.print((char)13);
      delay(1500);
      gprsSerial.println(text); // передача текста сообщения
      delay(500);
      gprsSerial.print((char)26); // передача ^Z
      gprsSerial.print((char)13); // передача Enter
    }
     
  7. KorPaEv

    KorPaEv Нерд

    + функция которая разбивает строку на подстроки по указанному символу...
    + функция поиска подстроки в строке..

    Я выкладывал для общего ознакомления принцип работы, а не весь исходник)))

    splitString(lineFullStr, '\n', i);

    Код (C++):
    //Функция разбивает строку по заданному символу и возвращает указанный индекс
    String splitString(String string, char separator, int index)
    {
      int found = 0;
      int strIndex[] = {0, -1};
      int maxIndex = string.length() - 1;
      for (int i = 0; i <= maxIndex && found <= index; i++)
      {
        if (string.charAt(i) == separator || i == maxIndex)
        {
          found++;
          strIndex[0] = strIndex[1] + 1;
          strIndex[1] = (i == maxIndex) ? i + 1 : i;
        }
      }
      return found > index ? string.substring(strIndex[0], strIndex[1]) : "";
    }

    //Функция разбивает строку по заданному символу и возвращает указанный индекс
    String splitString(String string, char separator, int index)
    {
      int found = 0;
      int strIndex[] = {0, -1};
      int maxIndex = string.length() - 1;
      for (int i = 0; i <= maxIndex && found <= index; i++)
      {
        if (string.charAt(i) == separator || i == maxIndex)
        {
          found++;
          strIndex[0] = strIndex[1] + 1;
          strIndex[1] = (i == maxIndex) ? i + 1 : i;
        }
      }
      return found > index ? string.substring(strIndex[0], strIndex[1]) : "";
    }
     
  8. lerik2703

    lerik2703 Гик

    я про UDH )
    ясненко))