Странная проблемма с SIM800

Belka_hohol
Offline
Зарегистрирован: 30.12.2020

Здравствуйте господа. Зарание спасибо за помощь. Суть проблеммы в следующем. У меня Arduino и GSM SIM800, они отправляют смс на русском, и раньше они это даже делали, но появиласьб проюлемма... Модем пререстал отвечать на некоторые комманды (фото ниже). Как вы думаете что это и как это решить.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Скетч секретный? И на какую команду модем не отвечает?

Belka_hohol
Offline
Зарегистрирован: 30.12.2020

Скетч конечно не секретный, тем более что большая часть взята с сайта: https://codius.ru/articles/GSM_%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C_SIM800L_%D1%87%D0%B0%D1%81%D1%82%D1%8C_3.

#include <SoftwareSerial.h>

#define Restart_GSM_pin  16
#define Rx_out  3                                 // принимающая линия UART для работы GSM модуля (A1)
#define Tx_out  2                                 // передающая линия UART для работы GSM модуля (A0)
#define number_lim  5                             // предел для создания номеров не больше 9
#define Stop_number "+00000000000"                // условно пустой  (не сушествуюший) номер


String Phone_numbers [number_lim];

// Подключение GSM 
SoftwareSerial SIM800(Tx_out, Rx_out); 

String _response = "";

extern int __bss_end;
extern void *__brkval;
// Функция, возвращающая количество свободного ОЗУ (RAM)
int memoryFree()
{
   int freeValue;
   if((int)__brkval == 0)
      freeValue = ((int)&freeValue) - ((int)&__bss_end);
   else
      freeValue = ((int)&freeValue) - ((int)__brkval);
   return freeValue;
}

//========================================================= GSM funcs ======================================================================
String byteToHexString(byte i) { // Функция преобразования числового значения байта в шестнадцатиричное (HEX)
  String hex = String(i, HEX);
  if (hex.length() == 1) hex = "0" + hex;
  hex.toUpperCase();
  return hex;
}

unsigned int getCharSize(unsigned char b) { // Функция получения количества байт, которыми кодируется символ
  // По правилам кодирования UTF-8, по старшим битам первого октета вычисляется общий размер символа
  // 1  0xxxxxxx - старший бит ноль (ASCII код совпадает с UTF-8) - символ из системы ASCII, кодируется одним байтом
  // 2  110xxxxx - два старших бита единицы - символ кодируется двумя байтами
  // 3  1110xxxx - 3 байта и т.д.
  // 4  11110xxx
  // 5  111110xx
  // 6  1111110x

  if (b < 128) return 1;             // Если первый байт из системы ASCII, то он кодируется одним байтом

  // Дальше нужно посчитать сколько единиц в старших битах до первого нуля - таково будет количество байтов на символ.
  // При помощи маски, поочереди исключаем старшие биты, до тех пор пока не дойдет до нуля.
  for (int i = 1; i <= 7; i++) {
    if (((b << i) & 0xFF) >> 7 == 0) {
      return i;
    }
  }
  return 1;
}

unsigned int symbolToUInt(const String& bytes) {  // Функция для получения DEC-представления символа
  unsigned int charSize = bytes.length();         // Количество байт, которыми закодирован символ
  unsigned int result = 0;
  if (charSize == 1) {
    return bytes[0]; // Если символ кодируется одним байтом, сразу отправляем его
  }
  else  {
    unsigned char actualByte = bytes[0];
    // У первого байта оставляем только значимую часть 1110XXXX - убираем в начале 1110, оставляем XXXX
    // Количество единиц в начале совпадает с количеством байт, которыми кодируется символ - убираем их
    // Например (для размера 2 байта), берем маску 0xFF (11111111) - сдвигаем её (>>) на количество ненужных бит (3 - 110) - 00011111
    result = actualByte & (0xFF >> (charSize + 1)); // Было 11010001, далее 11010001&(11111111>>(2+1))=10001
    // Каждый следующий байт начинается с 10XXXXXX - нам нужны только по 6 бит с каждого последующего байта
    // А поскольку остался только 1 байт, резервируем под него место:
    result = result << (6 * (charSize - 1)); // Было 10001, далее 10001<<(6*(2-1))=10001000000

    // Теперь у каждого следующего бита, убираем ненужные биты 10XXXXXX, а оставшиеся добавляем к result в соответствии с расположением
    for (int i = 1; i < charSize; i++) {
      actualByte = bytes[i];
      if ((actualByte >> 6) != 2) return 0; // Если байт не начинается с 10, значит ошибка - выходим
      // В продолжение примера, берется существенная часть следующего байта
      // Например, у 10011111 убираем маской 10 (биты в начале), остается - 11111
      // Теперь сдвигаем их на 2-1-1=0 сдвигать не нужно, просто добавляем на свое место
      result |= ((actualByte & 0x3F) << (6 * (charSize - 1 - i)));
      // Было result=10001000000, actualByte=10011111. Маской actualByte & 0x3F (10011111&111111=11111), сдвигать не нужно
      // Теперь "пристыковываем" к result: result|11111 (10001000000|11111=10001011111)
    }
    return result;
  }
}

String StringToUCS2(String s)
{
  String output = "";                                               // Переменная для хранения результата

  for (int k = 0; k < s.length(); k++) {                            // Начинаем перебирать все байты во входной строке
    byte actualChar = (byte)s[k];                                   // Получаем первый байт
    unsigned int charSize = getCharSize(actualChar);                // Получаем длину символа - кличество байт.

    // Максимальная длина символа в UTF-8 - 6 байт плюс завершающий ноль, итого 7
    char symbolBytes[charSize + 1];                                 // Объявляем массив в соответствии с полученным размером
    for (int i = 0; i < charSize; i++)  symbolBytes[i] = s[k + i];  // Записываем в массив все байты, которыми кодируется символ
    symbolBytes[charSize] = '\0';                                   // Добавляем завершающий 0

    unsigned int charCode = symbolToUInt(symbolBytes);              // Получаем DEC-представление символа из набора байтов
    if (charCode > 0)  {                                            // Если все корректно преобразовываем его в HEX-строку
      // Остается каждый из 2 байт перевести в HEX формат, преобразовать в строку и собрать в кучу
      output += byteToHexString((charCode & 0xFF00) >> 8) +
                byteToHexString(charCode & 0xFF);
    }
    k += charSize - 1;                                              // Передвигаем указатель на начало нового символа
    if (output.length() >= 280) break;                              // Строка превышает 70 (4 знака на символ * 70 = 280) символов, выходим
  }
  return output;                                                    // Возвращаем результат
}

String getDAfield(String *phone, bool fullnum) {
  String result = "";
  for (int i = 0; i <= (*phone).length(); i++) {  // Оставляем только цифры
    if (isDigit((*phone)[i])) {
      result += (*phone)[i];
    }
  }
  int phonelen = result.length();                 // Количество цифр в телефоне
  if (phonelen % 2 != 0) result += "F";           // Если количество цифр нечетное, добавляем F

  for (int i = 0; i < result.length(); i += 2) {  // Попарно переставляем символы в номере
    char symbol = result[i + 1];
    result = result.substring(0, i + 1) + result.substring(i + 2);
    result = result.substring(0, i) + (String)symbol + result.substring(i);
  }

  result = fullnum ? "91" + result : "81" + result; // Добавляем формат номера получателя, поле PR
  result = byteToHexString(phonelen) + result;    // Добавляем длиу номера, поле PL

  return result;
}

void getPDUPack(String *phone, String *message, String *result, int *PDUlen)
{
  // Поле SCA добавим в самом конце, после расчета длины PDU-пакета
  *result += "01";                                // Поле PDU-type - байт 00000001b
  *result += "00";                                // Поле MR (Message Reference)
  *result += getDAfield(phone, true);             // Поле DA
  *result += "00";                                // Поле PID (Protocol Identifier)
  *result += "08";                                // Поле DCS (Data Coding Scheme)
  //*result += "";                                // Поле VP (Validity Period) - не используется

  String msg = StringToUCS2(*message);            // Конвертируем строку в UCS2-формат

  *result += byteToHexString(msg.length() / 2);   // Поле UDL (User Data Length). Делим на 2, так как в UCS2-строке каждый закодированный символ представлен 2 байтами.
  *result += msg;

  *PDUlen = (*result).length() / 2;               // Получаем длину PDU-пакета без поля SCA
  *result = "00" + *result;                       // Добавляем поле SCA
}

String waitResponse() {                         // Функция ожидания ответа и возврата полученного результата
  String _resp = "";                            // Переменная для хранения результата
  long _timeout = millis() + 10000;             // Переменная для отслеживания таймаута (10 секунд)
  while (!SIM800.available() && millis() < _timeout)  {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то...
  if (SIM800.available()) {                     // Если есть, что считывать...
    _resp = SIM800.readString();                // ... считываем и запоминаем
  }
  else {                                        // Если пришел таймаут, то...
    Serial.println("Timeout...");               // ... оповещаем об этом и...
  }
  return _resp;                                 // ... возвращаем результат. Пусто, если проблема
}

String sendATCommand(String cmd, bool waiting) {
  String _resp = "";                            // Переменная для хранения результата
  Serial.println(cmd);                          // Дублируем команду в монитор порта
  SIM800.println(cmd);                          // Отправляем команду модулю
  if (waiting) {                                // Если необходимо дождаться ответа...
    _resp = waitResponse();                     // ... ждем, когда будет передан ответ
    // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать
    if (_resp.startsWith(cmd)) {                // Убираем из ответа дублирующуюся команду
      _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2);
    }
    Serial.println(_resp);                      // Дублируем ответ в монитор порта
  }
  return _resp;                                 // Возвращаем результат. Пусто, если проблема
}

void sendSMSinPDU(String phone, String message)
{
  Serial.println("Отправляем сообщение: " + message);

  // ============ Подготовка PDU-пакета =============================================================================================
  // В целях экономии памяти будем использовать указатели и ссылки
  String *ptrphone = &phone;                                    // Указатель на переменную с телефонным номером
  String *ptrmessage = &message;                                // Указатель на переменную с сообщением

  String PDUPack;                                               // Переменная для хранения PDU-пакета
  String *ptrPDUPack = &PDUPack;                                // Создаем указатель на переменную с PDU-пакетом

  int PDUlen = 0;                                               // Переменная для хранения длины PDU-пакета без SCA
  int *ptrPDUlen = &PDUlen;                                     // Указатель на переменную для хранения длины PDU-пакета без SCA

  getPDUPack(ptrphone, ptrmessage, ptrPDUPack, ptrPDUlen);      // Функция формирующая PDU-пакет, и вычисляющая длину пакета без SCA

  Serial.println("PDU-pack: " + PDUPack);
  Serial.println("PDU length without SCA:" + (String)PDUlen);

  // ============ Отправка PDU-сообщения ============================================================================================
  sendATCommand("AT+CMGF=0", true);                             // Включаем PDU-режим
  sendATCommand("AT+CMGS=" + (String)PDUlen, true);             // Отправляем длину PDU-пакета
  sendATCommand(PDUPack + (String)((char)26), true);            // После PDU-пакета отправляем Ctrl+Z
}

// =================================== Блок декодирования UCS2 в читаемую строку UTF-8 =================================
unsigned char HexSymbolToChar(char c) {
  if      ((c >= 0x30) && (c <= 0x39)) return (c - 0x30);
  else if ((c >= 'A') && (c <= 'F'))   return (c - 'A' + 10);
  else                                 return (0);
}

String UCS2ToString(String s) {                       // Функция декодирования UCS2 строки
  String result = "";
  unsigned char c[5] = "";                            // Массив для хранения результата
  for (int i = 0; i < s.length() - 3; i += 4) {       // Перебираем по 4 символа кодировки
    unsigned long code = (((unsigned int)HexSymbolToChar(s[i])) << 12) +    // Получаем UNICODE-код символа из HEX представления
                         (((unsigned int)HexSymbolToChar(s[i + 1])) << 8) +
                         (((unsigned int)HexSymbolToChar(s[i + 2])) << 4) +
                         ((unsigned int)HexSymbolToChar(s[i + 3]));
    if (code <= 0x7F) {                               // Теперь в соответствии с количеством байт формируем символ
      c[0] = (char)code;
      c[1] = 0;                                       // Не забываем про завершающий ноль
    } else if (code <= 0x7FF) {
      c[0] = (char)(0xC0 | (code >> 6));
      c[1] = (char)(0x80 | (code & 0x3F));
      c[2] = 0;
    } else if (code <= 0xFFFF) {
      c[0] = (char)(0xE0 | (code >> 12));
      c[1] = (char)(0x80 | ((code >> 6) & 0x3F));
      c[2] = (char)(0x80 | (code & 0x3F));
      c[3] = 0;
    } else if (code <= 0x1FFFFF) {
      c[0] = (char)(0xE0 | (code >> 18));
      c[1] = (char)(0xE0 | ((code >> 12) & 0x3F));
      c[2] = (char)(0x80 | ((code >> 6) & 0x3F));
      c[3] = (char)(0x80 | (code & 0x3F));
      c[4] = 0;
    }
    result += String((char*)c);                       // Добавляем полученный символ к результату
  }
  return (result);
}

// =================================== Блок кодирования строки в представление UCS2 =================================

void restart_GSM(){                                       // Функция перезагрузки модема
    digitalWrite(Restart_GSM_pin, LOW);
    delay(500);
    digitalWrite(Restart_GSM_pin, HIGH);
    delay(500);
}

void number_list_creating(){                              // создание списка номеров в микро контроллере
  int pos = 0;
  String string_to_mode = "";
  int index = 0;
  while (index < number_lim){
     //Serial.println("AT+CPBR="+String(index));
     SIM800.println("AT+CPBR="+String(index));
     string_to_mode = String(SIM800.readString());
     string_to_mode.replace("\n","");
     string_to_mode.replace(" ","");
     string_to_mode.remove(string_to_mode.indexOf("+"),1);

     if (string_to_mode.indexOf("+") != -1){
      pos = string_to_mode.indexOf("+");
      string_to_mode = string_to_mode.substring(pos,pos+12);
     }else{

      pos = string_to_mode.indexOf("8");
      string_to_mode = string_to_mode.substring(pos,pos+11);
      pos = string_to_mode.indexOf("8");
      string_to_mode[pos] = '7';
     }
     if (index > 0){
       Phone_numbers[index] = string_to_mode;
     }
     index++;
     SIM800.flush();
     Serial.flush();
  }
}


void number_creating_inSIM(){                             // создание номеров на сим карте при необходимости
  int index_of_numbers = 1;
  SIM800.println("AT+CPBR=1");
  if( SIM800.find('+') == false and SIM800.find('8') == false){
    while (index_of_numbers < number_lim){
      Serial.println(sendATCommand("AT+CPBW="+String(index_of_numbers)+","+'"'+Stop_number+'"'+","+"145,"+'"'+"Operator "+String(index_of_numbers)+'"'+"\r\n", true));
      index_of_numbers+=1;
    }
  }
  SIM800.flush();
  Serial.flush();
}

float getFloatFromString(String str) {            // Функция извлечения цифр из сообщения - для парсинга баланса из USSD-запроса
  bool   flag     = false;
  String result   = "";
  str.replace(",", ".");                          // Если в качестве разделителя десятичных используется запятая - меняем её на точку.
  for (int i = 0; i < str.length(); i++) {
    if (isDigit(str[i]) || (str[i] == (char)46 && flag)) {        // Если начинается группа цифр (при этом, на точку без цифр не обращаем внимания),
      if (result == "" && i > 0 && (String)str[i - 1] == "-") {   // Нельзя забывать, что баланс может быть отрицательным
        result += "-";                            // Добавляем знак в начале
      }
      result += str[i];                           // начинаем собирать их вместе
      if (!flag) flag = true;                     // Выставляем флаг, который указывает на то, что сборка числа началась.
    }
    else  {                                       // Если цифры закончились и флаг говорит о том, что сборка уже была,
      if (str[i] != (char)32) {                   // Если порядок числа отделен пробелом - игнорируем его, иначе...
        if (flag) break;                          // ...считаем, что все.
      }
    }
  }
  return result.toFloat();                        // Возвращаем полученное число.
}

//===========================================================================================================================================
// 1 - Работа насоса 1 восcтановлена, 2 - Работа насоса 2 восcтановлена, 3 - Работа насосв не требуется
// 4 - Насосы запущены в режиме чередования, 5 - Запущено два насоса одновременно, 
// 6 - Авария насоса 1, 7 - Авария насоса 2, 8 - Авария насоса 1 и 2
void SMS_sender(int choose) {                             // Функция отправки СМС по событию
  int index_for_send = 1;
  while (index_for_send < number_lim) {
    if (Phone_numbers[index_for_send] != Stop_number) {
      if (choose == 1) {
          sendSMSinPDU(Phone_numbers[index_for_send], F("Работа 1 восcтанов."));
      }
      if (choose == 2) {
          sendSMSinPDU(Phone_numbers[index_for_send], F("Работа 2 восcтанов."));
      }
      if (choose == 3) {
          sendSMSinPDU(Phone_numbers[index_for_send], F("Работа не требуется"));
      }
      if (choose == 4) {
          sendSMSinPDU(Phone_numbers[index_for_send], F("Насосы чередуются"));
      }
      if (choose == 5) {
          sendSMSinPDU(Phone_numbers[index_for_send], F("Запущено 2 насоса"));
      }
      if (choose == 6) {
          sendSMSinPDU(Phone_numbers[index_for_send], F("Авария насоса 1"));
      }
      if (choose == 7) {
          sendSMSinPDU(Phone_numbers[index_for_send], F("Авария насоса 2"));
      }
      if (choose == 8) {
          sendSMSinPDU(Phone_numbers[index_for_send], F("Авария насоса 1 и 2"));
      }
    }
  index_for_send += 1;
  }
}


void setup() {
  
  Serial.begin(9600);                                 // Скорость обмена данными с компьютером
  
  SIM800.begin(9600);                                 // Скорость обмена данными с модемом

  while ( sendATCommand("AT", true).indexOf("OK") == -1){           // продолжит выполнение если AT вернет True
    //Serial.println("GSM not responsing, restart");
    Serial.println(sendATCommand("AT", true).indexOf("OK"));
    restart_GSM();
  }

  Serial.println("Start!");

  // number_creating_inSIM();
  // number_list_creating();
  
  // Serial.println(Phone_numbers[0]);
  // Serial.println(Phone_numbers[1]);
  // Serial.println(Phone_numbers[2]);
  // Serial.println(Phone_numbers[3]);
  // Serial.println(Phone_numbers[4]);

  sendATCommand("AT+CIMI",true);
  sendATCommand("ATI",true);
  sendATCommand("AT+CNUM",true);
  sendATCommand("ATX1",true);

}

void loop() {

}

 

Если конкретно нет ответа на команды AT+CNUM (AT+CNUM=?), ATX1, но главное что не работает отпрака СМС в PDU формате, после отправки PDU пакета модем не отвечает.

Ну и мне кажется что дела не в скетче а в SIM или модеме (

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Belka_hohol пишет:

конкретно нет ответа на команды AT+CNUM (AT+CNUM=?)

не каждый оператор /  SIM поддерживает работоспособность данной команды + зависит от конкретного места, настроек оператора, фазы луны и еще куча всего....

и зачем вообще скетчу знать свой номер? все равно же оператор/кто-то шлет команды устройству, тогда уж привязку железки рекомендую к IMEI делать

Belka_hohol пишет:

 мне кажется что дела не в скетче 

Откуда такая уверенность? разбираться в сотнях строк скопированного с интернета кода нет ни времени ни желания.
В качестве рекомендации - поставьте вывод информационных сообщений в Serial в разных местах и понять в каком месте конкретно зависает, с большой вероятностью не хватает памяти + проверить стабильность питания модема.
Так же советую разобраться самостоятельно в формате PDU, и соотвественно самим написать отправку, на форуме это уже было:
sim900 отправка больших СМС на русском в PDU формате | Аппаратная платформа Arduino
GSM A6 Mini смс в PDU формате | Аппаратная платформа Arduino
http://arduino.ru/forum/apparatnye-voprosy/vse-o-sim800l-i-vse-chto-s-nim-svyazano?page=1#comment-376729

вот еще нашел древний код для A6 модема (суть та же), что то типа температурной сигнализации, шлет SMS в PDU О времени работы и температуре

#include <EEPROM.h>
// address 0 = count control users phone
// address 1.... control users phones, first char - A - active D - deleted, last char = 0
#include "a6modem.h"

#define max_time_without_response 14497537UL // ~ 4 hour
#define max_time_read_sms 120000UL // 2min
#define max_time_read_command 10000UL // 10sec
#define startEEPROMmessages 128 // 0 and 1 position = word - current save posifion from 0
#define endEEPROMmessages 383
#define max_count_users_phone 6
#define max_len_sms 128

unsigned long last_time_get_response;
unsigned long time_read_sms;
unsigned long time_read_command;
byte gsmmode, submode;
char OPSname[] = "Russia00";
char OPSbalance[] = "*99999#";
char* UnitCommands[] = {"addphone", "delphone", "getuptime", "getbalance"};
char sender[24];
char smsmsg[max_len_sms];
char begstr[] = "~NEWsSMS~";
char endstr[] = "~SUCCSMS~";

void setup() {
  // put your setup code here, to run once:
  firststart();
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long current_millis = millis();
  if (a6work() == 1) {
    last_time_get_response = current_millis; // clear watch dog timer
    domainmode();
  } else {
    // watch dog timer for modem
    if ((current_millis - last_time_get_response) >= max_time_without_response) {
      a6resetmodem();
      unsigned long delp = millis(); while ((millis() - delp) <= 20000); // boot modem
      firststart();
    } else {
      // other action
      // read sms mode
      if ((current_millis - time_read_sms) >= max_time_read_sms) {
        if (gsmmode == 1) {
          if (a6sendcmd("AT+CMGL=4", "OK", "", "") == 1) { // read SMS, =0 unread, =4 all
            gsmmode = 2; submode = 0; // mode read sms
            time_read_sms = current_millis; // after sucess send command read sms
          }
        }
      }
      // end read sms mode
      // read sms from eeprom for execute comand
      if ((current_millis - time_read_command) >= max_time_read_command) {
        if (gsmmode == 1) {
          time_read_command = current_millis;
          readSMSforCommand();
        }
      }
      // end read command
    }
  }
}

void domainmode() {
  if (a6getreturnedwork(0) == 2) { // timeout
    gsmmode = 1; submode = 0; // main loop
    a6clearrespbuf();
    return;
  }
  switch (gsmmode) {
    case 0: { // init modem
        if (a6getreturnedwork(0) == 1) {
          switch (submode) {
            case 0: { // wait reponse first command AT
                if (a6sendcmd("ATE0", "OK", "", "") == 1) submode = 1; // no echo
                break;
              }
            case 1: { // wait reponse command ATE0
                if (a6sendcmd("ATV1", "OK", "", "") == 1) submode = 2; // get text for error
                break;
              }
            case 2: { // wait reponse command ATV1
                if (a6sendcmd("AT+CMEE=2", "OK", "", "") == 1) submode = 3; // get full text error
                break;
              }
            case 3: { // wait reponse command cmee
                if (a6sendcmd("AT+CLIP=1", "OK", "", "") == 1) submode = 4; // on aon
                break;
              }
            case 4: { // wait reponse command clip
                if (a6sendcmd("ATS0=3", "OK", "", "") == 1) submode = 5; // 3 ring
                break;
              }
            case 5: { // wait reponse command ats
                unsigned long delp = millis(); while ((millis() - delp) <= 30000); //delay for registration
                if (a6sendcmd("AT+CREG?", "+CREG: 1,1", "OK", "") == 1) submode = 6; // true registration
                //if (a6sendcmd("AT+CREG?", "OK", "", "") == 1) submode = 6; // registration
                break;
              }
            case 6: { // wait registration
                if (a6sendcmd("AT+CMGF=0", "OK", "", "") == 1) submode = 7; // mode SMS = PDU
                break;
              }
            case 7: { // wait mode sms
                if (a6sendcmd("AT+CMGD=1,4", "OK", "", "") == 1) submode = 8; // delete all sms
                break;
              }
            case 8: { // wait del sms
                if (a6sendcmd("AT+COPS?", "+COPS:", "OK", "") == 1) submode = 9; // load operator data
                break;
              }
            case 9: { // decode operator data
                a6getopsname(OPSname, OPSbalance);
                gsmmode = 1; submode = 0; // init ok
                digitalWrite(LED_BUILTIN, HIGH);
                a6clearrespbuf();
                break;
              }
          }
        } else {
          a6clearrespbuf();
        }
        break;
      }
    case 1: { // main loop
        if (a6getreturnedwork(2) == 1) { // ring
          // ring by admin
        }
        if ((a6getreturnedwork(1) == 1) || (a6getreturnedwork(3) == 1)) { // problem
          a6resetmodem();
          unsigned long delp = millis(); while ((millis() - delp) <= 20000); // boot modem
          firststart();
          return;
        }
        a6clearrespbuf();
        break;
      }
    case 2: { // mode read sms
        if (a6getreturnedwork(0) == 1) {
          switch (submode) {
            case 0: {
                byte countsms = a6getcountsms();
                if (countsms > 0) {
                  while (countsms > 0) { // loop by sms
                    a6readsms(countsms, sender, smsmsg);
                    if ((getAllowCommandPhone(sender) == 1) || (getAllowCommandPhone(sender) == 2)) {
                      // save sms to eeprom
                      saveSMStoEEPROM(sender, smsmsg);
                    }
                    // - end loop sms
                    --countsms;
                  }
                  // delete all sms
                  if (a6sendcmd("AT+CMGD=1,4", "OK", "", "") == 1) submode = 1;
                } else {
                  a6clearrespbuf(); gsmmode = 1; submode = 0; // no sms
                }
                break;
              }
            default: {
                a6clearrespbuf(); gsmmode = 1; submode = 0;
              }
          }
        } else {
          a6clearrespbuf();
        }
        break;
      }
    case 3: { // mode send sms
        if (a6getreturnedwork(0) == 1) {
          switch (submode) {
            case 0: {
                if (a6sendcmd(smsmsg, ">", "+CMGS:", "OK") == 1) submode = 1;
                break;
              }
            default: {
                a6clearrespbuf(); gsmmode = 1; submode = 0;
              }
          }
        } else {
          a6clearrespbuf();
        }
        break;
      }
    case 4: { // mode get balance
        if (a6getreturnedwork(0) == 1) {
          switch (submode) {
            case 0: {
                // decode USSD
                // send SMS text from USSD
                a6clearrespbuf(); gsmmode = 1; submode = 0; // temp text, seed submode = 1
                break;
              }
            default: {
                a6clearrespbuf(); gsmmode = 1; submode = 0;
              }
          }
        } else {
          a6clearrespbuf();
        }
        break;
      }
    default: {
        a6clearrespbuf();
      }
  }
}

void firststart() {
  a6initmodem(9600, 1, 9600); // test init soft uart
  //a6initmodem(38400, 0, 0); // work - hard uart
  gsmmode = 0; submode = 0; // start
  last_time_get_response = millis();
  time_read_sms = 0UL; time_read_command = 0UL;
}

void SerialPrintWin1251(char* textsms) {
  byte i = 0;
  while (textsms[i] != 0) {
    byte wb = textsms[i];
    if (wb <= 128) {
      Serial.write(wb);
    } else {
      switch (wb) {
        case 168: {
            Serial.write(208); Serial.write(101);
            break;
          }
        case 184: {
            Serial.write(209); Serial.write(145);
            break;
          }
        default: {
            if (wb < 192) {
              Serial.write(wb);
            } else {
              if (wb < 240) {
                Serial.write(208); Serial.write(wb - 48);
              } else {
                Serial.write(209); Serial.write(wb - 112);
              }
            }
          }
      }
    }
    ++i;
  }
}

byte getAllowCommandPhone(char* sender) {
  if (strPos(sender, ADMIN_PHONE) >= 0) return 2;
  word rb = FindPhomeFromUsersList(sender);
  if ((rb > 1000) && (rb < 2000)) {
    return 1;
  } else {
    return 0;
  }
}

void saveSMStoEEPROM(char* sender, char* textsms) {
  byte l1 = strlen(sender); byte l2 = strlen(textsms);
  word lastpos = EEPROM.read(startEEPROMmessages);
  if ((lastpos == 0xFF) || ((lastpos + l1 + l2 + 12) >= (endEEPROMmessages - startEEPROMmessages))) lastpos = 0;
  lastpos += (startEEPROMmessages + 1); byte i = 0;
  while (begstr[i] != 0) {
    EEPROM.write(lastpos + i, begstr[i]); ++i;
  }
  EEPROM.write(lastpos + i, 0);
  lastpos += (i + 1); i = 0;
  while (sender[i] != 0) {
    EEPROM.write(lastpos + i, sender[i]); ++i;
  }
  EEPROM.write(lastpos + i, 0);
  lastpos += (i + 1); i = 0;
  while (textsms[i] != 0) {
    EEPROM.write(lastpos + i, textsms[i]); ++i;
  }
  EEPROM.write(lastpos + i, 0);
  lastpos += (i + 1);
  EEPROM.write(startEEPROMmessages, (lastpos - startEEPROMmessages - 1));
}

void readSMSforCommand() {
  word lastpos = startEEPROMmessages + 1;
  word i, j, k;
m3:  i = 0; j = 0; k = 0;
m1:  byte eb = EEPROM.read(lastpos + i);
  if ((eb == begstr[i]) || (eb == endstr[i])) {
    if (eb == begstr[i]) ++j; if (eb == endstr[i]) ++k;
    if ((++i) >= 9) goto m2;
    goto m1;
  } else {
    return;
  }
m2:  if (k == 9) {
    // skip -> new sms
    lastpos += (k + 1);
    // find 0 and 0
    i = 0; while (EEPROM.read(lastpos + i) != 0) ++i;
    lastpos += (i + 1);
    if (lastpos >= endEEPROMmessages) return;
    i = 0; while (EEPROM.read(lastpos + i) != 0) ++i;
    lastpos += (i + 1);
    if (lastpos >= endEEPROMmessages) return;
    goto m3;
  }
  if (j == 9) {
    // save to eeprom ~SUCCSMS~
    i = 0;
    while ((endstr[i]) != 0) {
      EEPROM.write((lastpos + i), endstr[i]);
      ++i;
    }
    // new command
    lastpos += (j + 1); i = 0;
    while ((sender[i] = EEPROM.read(lastpos + i)) != 0) ++i;
    sender[i] = 0;
    lastpos += (i + 1); i = 0;
    while ((smsmsg[i] = EEPROM.read(lastpos + i)) != 0) ++i;
    smsmsg[i] = 0;
    lastpos += (i + 1);
    // find command
    j = 0; // num found command
    for (i = 0; i < 4; ++i) { // 4 commands
      if (strPos(smsmsg, UnitCommands[i]) >= 0) {
        j = i + 1;
        break;
      }
    }
    switch (j) {
      case 1: {
          if (getAllowCommandPhone(sender) != 2) return; // command for admin only
          if (CutPhoneFromText(smsmsg) != 1) return;
          // save phone to eeprom
          AddPhoneToUsersList(smsmsg);
          break;
        }
      case 2: {
          if (getAllowCommandPhone(sender) != 2) return; // command for admin only
          if (CutPhoneFromText(smsmsg) != 1) return;
          // delete phone from eeprom
          DelPhoneToUsersList(smsmsg);
          break;
        }
      case 3: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          // prepare send text
          i = 0;
          while ((smsmsg[i] = OPSname[i]) != 0 ) ++i;
          smsmsg[i] = ' '; ++i;
          i = addTextUptime(i);
          smsmsg[i] = 0;
          sendSMS();
          break;
        }
      case 4: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          char strcmd[] = "AT+CUSD=1,";
          i = 0;
          while ((sender[i] = strcmd[i]) != 0) ++i;
          j = 0;
          while ((sender[i] = OPSbalance[j]) != 0) {
            ++i; ++j;
          }
          sender[i] = ','; ++i; sender[i] = '1'; ++i; sender[i] = '5'; ++i;
          sender[i] = 0;
          a6sendcmd(sender, "OK", "+CUSD: 2,", ",72");
          gsmmode = 4; submode = 0;
          break;
        }
      default: {
          // Serial.println(" no command");
        }
    }
  } else {
    return;
  }
}

byte CutPhoneFromText(char* smstxt) {
  int pp = strPos(smstxt, "+79");
  if (pp > 0) {
    byte cb = 0;
    for (byte i = 0; i < 12; ++i) { // +7 and 10 digit
      smstxt[cb] = smstxt[pp + cb]; ++cb;
    }
    smstxt[cb] = 0;
    if (cb == 12) return 1;
  }
  return 0;
}

void AddPhoneToUsersList(char* phoneNumber) {
  word rb = FindPhomeFromUsersList(phoneNumber);
  if (rb > 2000) {
    // set active number
    EEPROM.write((rb - 2000), 'A');
    return;
  }
  if (rb > 1000) {
    return;
  }
  // add new number
  byte pc = EEPROM.read(0);
  if (pc >= max_count_users_phone) pc = 5; // last number;
  word startpos = 1 + (pc * 14);
  byte i = 0;
  EEPROM.write(startpos, 'A');
  while (phoneNumber[i] != 0) {
    EEPROM.write((startpos + 1 + i), phoneNumber[i]);
    ++i;
  }
  EEPROM.write((startpos + 1 + i), 0);
  ++pc;
  EEPROM.write(0, pc);
}

void DelPhoneToUsersList(char* phoneNumber) {
  word rb = FindPhomeFromUsersList(phoneNumber);
  if (rb > 1000) {
    // set delete number
    EEPROM.write((rb - 1000), 'D');
    return;
  }
}

word FindPhomeFromUsersList(char* phoneNumber) {
  byte pc = EEPROM.read(0);
  if (pc == 0xFF) pc = 0;
  if (pc == 0) {
    EEPROM.write(0, 0);
    return 0;
  }
  byte i; boolean fla = false;
  for (i = 0; ((i < pc) && (i < max_count_users_phone)); ++i) {
    boolean fle = true;
    word startpos = 1 + i * 14;
    if (EEPROM.read(startpos) == 'A') fla = true; else fla = false;
    byte j = 0;
    while (phoneNumber[j] != 0) {
      if (phoneNumber[j] != EEPROM.read(startpos + j + 1)) {
        fle = false;
        break;
      }
      ++j;
    }
    if (fle) {
      if (fla) {
        return (1000 + startpos);
      } else {
        return (2000 + startpos);
      }
    }
  }
  return 0;
}

byte addTextUptime(byte firstpos) {
  unsigned long cml = millis() / 1000UL;
  byte days = cml / 86400;
  byte hours = (cml % 86400) / 3600;
  byte mins = ((cml % 86400) % 3600) / 60;
  if (days > 9) {
    smsmsg[firstpos] = (days / 10) + '0';
    ++firstpos;
  }
  smsmsg[firstpos] = (days % 10) + '0'; ++firstpos;
  smsmsg[firstpos] = 228; ++firstpos; smsmsg[firstpos] = 237; ++firstpos; smsmsg[firstpos] = '.'; ++firstpos;
  if (hours > 9) {
    smsmsg[firstpos] = (hours / 10) + '0';
    ++firstpos;
  }
  smsmsg[firstpos] = (hours % 10) + '0'; ++firstpos;
  smsmsg[firstpos] = 247; ++firstpos; smsmsg[firstpos] = 224; ++firstpos; smsmsg[firstpos] = 241; ++firstpos;
  smsmsg[firstpos] = '.'; ++firstpos;
  if (mins > 9) {
    smsmsg[firstpos] = (mins / 10) + '0';
    ++firstpos;
  }
  smsmsg[firstpos] = (mins % 10) + '0'; ++firstpos;
  smsmsg[firstpos] = 236; ++firstpos; smsmsg[firstpos] = 232; ++firstpos; smsmsg[firstpos] = 237; ++firstpos;
  smsmsg[firstpos] = '.'; ++firstpos;
  return firstpos;
}

void sendSMS() {
  // convert msg to pdu format
  byte lenTextByChar = strlen(smsmsg);
  // move text to end buf
  byte i, j;
  for (i = 0; i < lenTextByChar; ++i) {
    smsmsg[max_len_sms - lenTextByChar + i] = smsmsg[i];
  }
  byte curr_pos_pdu = 0;
  smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; // SCA field
  smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '1'; ++curr_pos_pdu; // PDU type
  smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; // MR
  byte lenSenderPhone = strlen(sender);
  if (lenSenderPhone != 12) return;
  smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = 'B'; ++curr_pos_pdu; // DA-PL
  smsmsg[curr_pos_pdu] = '9'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '1'; ++curr_pos_pdu; // DA-PT
  // convert sender number
  for (i = 0; i < lenSenderPhone; ++i) {
    sender[i] = sender[i + 1];
  }
  sender[lenSenderPhone - 1] = 'F'; sender[lenSenderPhone] = 0;
  for (i = 0; i < lenSenderPhone; i += 2) {
    j = sender[i + 1];
    sender[i + 1] = sender[i];
    sender[i] = j;
  }
  for (i = 0; i < lenSenderPhone; ++i) { // DA-RP
    smsmsg[curr_pos_pdu] = sender[i];
    ++curr_pos_pdu;
  }
  smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; // PID field
  smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '8'; ++curr_pos_pdu; // DCS field
  lenTextByChar *= 2; // UDL
  byte bb = lenTextByChar / 16;
  if (bb < 10) {
    smsmsg[curr_pos_pdu] = (bb + '0'); ++curr_pos_pdu;
  } else {
    smsmsg[curr_pos_pdu] = (bb + '0' + 7); ++curr_pos_pdu;
  }
  bb = lenTextByChar % 16;
  if (bb < 10) {
    smsmsg[curr_pos_pdu] = (bb + '0'); ++curr_pos_pdu;
  } else {
    smsmsg[curr_pos_pdu] = (bb + '0' + 7); ++curr_pos_pdu;
  }
  // UD - text by UCS2
  for (i = 0; i < (lenTextByChar / 2); ++i) {
    bb = smsmsg[max_len_sms - (lenTextByChar / 2) + i];
    if (bb < 0x7F) {
      smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu;
      // convert to hex
      j = bb / 16;
      if (j < 10) {
        smsmsg[curr_pos_pdu] = (j + '0'); ++curr_pos_pdu;
      } else {
        smsmsg[curr_pos_pdu] = (j + '0' + 7); ++curr_pos_pdu;
      }
      j = bb % 16;
      if (j < 10) {
        smsmsg[curr_pos_pdu] = (j + '0'); ++curr_pos_pdu;
      } else {
        smsmsg[curr_pos_pdu] = (j + '0' + 7); ++curr_pos_pdu;
      }
    } else {
      switch (bb) {
        case 168: {
            smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '4'; ++curr_pos_pdu;
            smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '1'; ++curr_pos_pdu;
            break;
          }
        case 184: {
            smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '4'; ++curr_pos_pdu;
            smsmsg[curr_pos_pdu] = '5'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '1'; ++curr_pos_pdu;
            break;
          }
        default: {
            if (bb < 192) {
              smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu;
              // convert to hex
              j = bb / 16;
              if (j < 10) {
                smsmsg[curr_pos_pdu] = (j + '0'); ++curr_pos_pdu;
              } else {
                smsmsg[curr_pos_pdu] = (j + '0' + 7); ++curr_pos_pdu;
              }
              j = bb % 16;
              if (j < 10) {
                smsmsg[curr_pos_pdu] = (j + '0'); ++curr_pos_pdu;
              } else {
                smsmsg[curr_pos_pdu] = (j + '0' + 7); ++curr_pos_pdu;
              }
            } else {
              smsmsg[curr_pos_pdu] = '0'; ++curr_pos_pdu; smsmsg[curr_pos_pdu] = '4'; ++curr_pos_pdu;
              // convert to hex
              j = (bb - 176) / 16;
              if (j < 10) {
                smsmsg[curr_pos_pdu] = (j + '0'); ++curr_pos_pdu;
              } else {
                smsmsg[curr_pos_pdu] = (j + '0' + 7); ++curr_pos_pdu;
              }
              j = (bb - 176) % 16;
              if (j < 10) {
                smsmsg[curr_pos_pdu] = (j + '0'); ++curr_pos_pdu;
              } else {
                smsmsg[curr_pos_pdu] = (j + '0' + 7); ++curr_pos_pdu;
              }
            }
          }
      }
    }
  }
  // -
  smsmsg[curr_pos_pdu] = 26;
  smsmsg[curr_pos_pdu + 1] = 0;
  char strcmd[] = "AT+CMGS=";
  i = 0;
  while ((sender[i] = strcmd[i]) != 0) ++i;
  sender[i] = ((curr_pos_pdu / 2 - 1) / 10) + '0'; ++i;
  sender[i] = ((curr_pos_pdu / 2 - 1) % 10) + '0'; ++i;
  sender[i] = 0;
  a6sendcmd(sender, ">", "", "");
  gsmmode = 3; submode = 0;
}



// functions for a6 gsm modem
#include "Arduino.h"
#include <SoftwareSerial.h>

#define _a6_uart_rx_pin 9 // soft uart // 9 - TX Arduino (RX SIM800L)
#define _a6_uart_pin_tx 8 // soft uart // 8 - RX Arduino (TX SIM800L),
#define _a6_power_pin 10
#define _a6_reset_pin 7

#define RESPONSE_BUF_SIZE 184
#define RESPONSE_DETECT_SIZE 16
#define RESPONSE_MAX_TIME 30000
#define RESPONSE_MSG_SIZE 16
#define RESPONSE_COUNT_MSG 3
#define ADMIN_PHONE "+79206319946"
#define RING_ADMIN_PHONE "79206319946"

void a6initmodem(word a6uartspeed, byte a6softuart, word serialspeed);
byte a6sendcmd(char* txtcmd, char* resp1, char* resp2, char* resp3);
byte a6work();
byte a6detectresp(char* resp1, char* resp2, char* resp3);
char* LastPos(char *str1, char *str2);
int strPos(char *str11, char *str22);
void a6clearrespbuf();
void a6addmsgdetect(byte nummsg, char* resp1, char* resp2, char* resp3);
byte a6getreturnedwork(byte returnpos);
void a6resetmodem();
byte a6getcountsms();
byte a6readsms(byte indexsms, char* phonenumber, char* textsms);
void a6decodepdusms(byte firstcharpdu, byte counttextpdu, byte lastcharpdu, char* senderphone, char* textsms);
byte strHexTobyte(char p1, char p2);
void a6decodetextpdu7bit(byte charcount, byte firstpos, char* textsms);
void a6decodetextpduUCS2(byte bytecount, byte firstpos, char* textsms);
void a6getopsname(char* opsname, char* opsbalance);



// functions for a6 gsm modem
#include "a6modem.h"
#include "Arduino.h"
#include <SoftwareSerial.h>

byte _a6_soft_uart;
byte _use_console;
char _rsp_buf[RESPONSE_BUF_SIZE];
word _rsp_pos;
char _resp_detect1[RESPONSE_DETECT_SIZE]; char _resp_detect2[RESPONSE_DETECT_SIZE]; char _resp_detect3[RESPONSE_DETECT_SIZE];
char _msg_detect1[RESPONSE_COUNT_MSG][RESPONSE_MSG_SIZE]; char _msg_detect2[RESPONSE_COUNT_MSG][RESPONSE_MSG_SIZE]; char _msg_detect3[RESPONSE_COUNT_MSG][RESPONSE_MSG_SIZE];
byte _a6_returned_work[RESPONSE_COUNT_MSG + 1]; // position 0 = response sent at command, other position - returned msg
unsigned long cmd_started_time;
boolean wait_response;
char* OPSlist[] = {"TELE2RUS", "*105#", "25020", "Beeline", "*100#", "25099", "MegaFon", "*100#", "25002", "MTS", "*100#", "25001"};
char OPScode[] = "250000";

SoftwareSerial gsm(_a6_uart_pin_tx, _a6_uart_rx_pin);

// a6 init modem
// p1 - modem speed
// p2 - 1=use Software Serial UART
// p3 - Serial0 speed, use as console, =0 if dont use or modem use Serial0;
void a6initmodem(word a6uartspeed, byte a6softuart, word serialspeed) {
  pinMode(_a6_reset_pin, OUTPUT);
  pinMode(_a6_power_pin, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  digitalWrite(_a6_reset_pin, LOW);
  digitalWrite(_a6_power_pin, HIGH); // power on
  unsigned long delp = millis(); while ((millis() - delp) <= 10000);
  if (a6softuart != 0) {
    _a6_soft_uart = 1;
    gsm.begin(a6uartspeed);
    if (serialspeed > 0) {
      Serial.begin(serialspeed);
      _use_console = 1;
    } else {
      _use_console = 0;
    }
  } else {
    _a6_soft_uart = 0; _use_console = 0;
    Serial.begin(a6uartspeed);
  }
  // adaptive speed a6 module
  unsigned long curr_millis = millis();
  while ((millis() - curr_millis) <= 4000UL) {
    if (_a6_soft_uart == 1) gsm.println("AT"); else Serial.println("AT");
  }
  curr_millis = millis();
  while ((millis() - curr_millis) <= 5000UL) {
    if (_a6_soft_uart) {
      if (gsm.available())
        while (gsm.available()) gsm.read();
    } else {
      if (Serial.available())
        while (Serial.available()) Serial.read();
    }
  }
  // -
  wait_response = false;
  for (int i = 0; i < RESPONSE_COUNT_MSG; ++i) {
    _msg_detect1[i][0] = 0; _msg_detect2[i][0] = 0; _msg_detect3[i][0] = 0;
  }
  // init command
  a6addmsgdetect(0, "-VOLTAGE WARN", "", ""); // power problem
  a6addmsgdetect(1, "+CLIP:", RING_ADMIN_PHONE, "CONNECT"); // input ring
  a6addmsgdetect(2, "+CMTE: 1", "", ""); // temp problem
  a6sendcmd("AT", "OK", "", ""); // begin work
}

void a6resetmodem() {
  // reset modem
  digitalWrite(_a6_reset_pin, HIGH);
  unsigned long delp = millis(); while ((millis() - delp) <= 500);
  digitalWrite(_a6_reset_pin, LOW);
}

byte a6sendcmd(char* txtcmd, char* resp1, char* resp2, char* resp3) {
  if (wait_response) return 0;
  if (_a6_soft_uart) gsm.println(txtcmd); else Serial.println(txtcmd);
  cmd_started_time = millis();
  a6clearrespbuf();
  byte pp = 0;
  if (_use_console) Serial.println(txtcmd);
  if (resp1[0] == 0) {
    wait_response = false;
    _resp_detect1[0] = 0;
    return 1;
  } else {
    wait_response = true;
    pp = 0;
    while ((resp1[pp] != 0) && (pp < (RESPONSE_DETECT_SIZE - 1))) {
      _resp_detect1[pp] = resp1[pp];
      ++pp;
    }
    _resp_detect1[pp] = 0;
  }
  pp = 0;
  while ((resp2[pp] != 0) && (pp < (RESPONSE_DETECT_SIZE - 1))) {
    _resp_detect2[pp] = resp2[pp];
    ++pp;
  }
  _resp_detect2[pp] = 0;
  pp = 0;
  while ((resp3[pp] != 0) && (pp < (RESPONSE_DETECT_SIZE - 1))) {
    _resp_detect3[pp] = resp3[pp];
    ++pp;
  }
  _resp_detect3[pp] = 0;
  return 1;
}

byte a6work() {
  boolean getdata = false;
  if (_a6_soft_uart) {
    if (gsm.available())
      while (gsm.available())
        if (_rsp_pos < (RESPONSE_BUF_SIZE - 1)) {
          _rsp_buf[_rsp_pos] = gsm.read();
          ++_rsp_pos;
          getdata = true;
        }
  } else {
    if (Serial.available())
      while (Serial.available())
        if (_rsp_pos < (RESPONSE_BUF_SIZE - 1)) {
          _rsp_buf[_rsp_pos] = Serial.read();
          ++_rsp_pos;
          getdata = true;
        }
  }
  if (getdata) {
    _rsp_buf[_rsp_pos] = 0;
    if (_use_console) {
      if (_rsp_pos > 0) {
        Serial.println(_rsp_buf);
      }
    }
  }
  byte result = 0;
  for (byte i = 0; i <= RESPONSE_COUNT_MSG; ++i) _a6_returned_work[i] = 0;
  // find msg
  for (byte i = 0; i < RESPONSE_COUNT_MSG; ++i) {
    if (a6detectresp(_msg_detect1[i], _msg_detect2[i], _msg_detect3[i])) {
      result = 1;
      _a6_returned_work[i + 1] = 1;
    }
  }
  // wait response for sent at command
  if (wait_response) {
    if ((millis() - cmd_started_time) >= RESPONSE_MAX_TIME) {
      wait_response = false;
      result = 1;
      _a6_returned_work[0] = 2; // timeout
    } else {
      if (a6detectresp(_resp_detect1, _resp_detect2, _resp_detect3)) {
        wait_response = false;
        result = 1;
        _a6_returned_work[0] = 1; // OK
      }
    }
  }
  return result;
}

void a6addmsgdetect(byte nummsg, char* resp1, char* resp2, char* resp3) {
  if (nummsg > (RESPONSE_COUNT_MSG - 1)) return;
  if (resp1[0] == 0) {
    _msg_detect1[nummsg][0] = 0;
    return;
  }
  byte pp = 0;
  while ((resp1[pp] != 0) && (pp < (RESPONSE_MSG_SIZE - 1))) {
    _msg_detect1[nummsg][pp] = resp1[pp];
    ++pp;
  }
  _msg_detect1[nummsg][pp] = 0;
  if (resp2[0] == 0) return;
  pp = 0;
  while ((resp2[pp] != 0) && (pp < (RESPONSE_MSG_SIZE - 1))) {
    _msg_detect2[nummsg][pp] = resp2[pp];
    ++pp;
  }
  _msg_detect2[nummsg][pp] = 0;
  if (resp3[0] == 0) return;
  pp = 0;
  while ((resp3[pp] != 0) && (pp < (RESPONSE_MSG_SIZE - 1))) {
    _msg_detect3[nummsg][pp] = resp3[pp];
    ++pp;
  }
  _msg_detect3[nummsg][pp] = 0;
}

void a6clearrespbuf() {
  _rsp_pos = 0; _rsp_buf[_rsp_pos] = 0;
}

byte a6detectresp(char* resp1, char* resp2, char* resp3) {
  if (_rsp_pos > 0) {
    if (resp1[0] != 0) {
      if (strPos(_rsp_buf, resp1) >= 0) {
        if (resp2[0] == 0) {
          return 1;
        } else {
          if (strPos(_rsp_buf, resp2) >= 0) {
            if (resp3[0] == 0) {
              return 1;
            } else {
              if (strPos(_rsp_buf, resp3) >= 0) {
                return 1;
              } else {
                return 0;
              }
            }
          } else {
            return 0;
          }
        }
      } else {
        return 0;
      }
    } else {
      return 0;
    }
  } else {
    return 0;
  }
}

char* LastPos(char *str1, char *str2) {
  int L1 = strlen(str1);
  int L2 = strlen(str2);
  for (int i = L1 - L2; i >= 0; i--)
  {
    int j = 0;
    for (; j < L2; j++)
      if (str1[i + j] != str2[j])
        break;
    if (j == L2)
      return str1 + i;
  }
  return 0;
}

int strPos(char *str11, char *str22) {
  char*p = LastPos(str11, str22);
  int n = p - str11;
  return n;
}

byte a6getreturnedwork(byte returnpos) {
  return _a6_returned_work[returnpos];
}

byte a6getcountsms() {
  char findstr[] = "+CMGL:";
  if (a6detectresp(findstr, "", "") == 0) return 0;
  byte result = 0;
  int L1 = strlen(_rsp_buf);
  int L2 = strlen(findstr);
  for (int i = L1 - L2; i >= 0; i--)
  {
    int j = 0;
    for (j = 0; j < L2; j++)
      if (_rsp_buf[i + j] != findstr[j])
        break;
    if (j == L2) {
      ++result;
    }
  }
  return result;
}

byte a6readsms(byte indexsms, char* phonenumber, char* textsms) {
  phonenumber[0] = 0; textsms[0] = 0;
  byte numfirstchar;
  if (indexsms < 10) {
    char findstr[] = "+CMGL: 1"; findstr[7] = '0' + indexsms;
    numfirstchar = strPos(_rsp_buf, findstr);
    numfirstchar += (strlen(findstr) + 4); // first posifion = count byte of pdu message
  } else {
    char findstr[] = "+CMGL: 1 "; findstr[8] = '0' + (indexsms % 10);
    numfirstchar = strPos(_rsp_buf, findstr);
    numfirstchar += (strlen(findstr) + 4); // first posifion = count byte of pdu message
  }
  // get lenght pdu
  byte i = 0;
  byte countbytepdu = 0;
  while (isdigit(_rsp_buf[numfirstchar + i])) {
    if (i > 0) {
      countbytepdu = (countbytepdu * 10) + (_rsp_buf[numfirstchar + i] - '0');
    } else {
      countbytepdu = _rsp_buf[numfirstchar + i] - '0';
    }
    ++i;
  }
  numfirstchar += i;
  if ((_rsp_buf[numfirstchar] == '\r') && (_rsp_buf[numfirstchar + 1] == '\n')) { // find first CR LF
    numfirstchar += 2;
  } else {
    return;
  }
  i = 0;
  while (!((_rsp_buf[numfirstchar + i] == '\r') && (_rsp_buf[numfirstchar + 1 + i] == '\n') && (_rsp_buf[numfirstchar + i] != 0))) { // find last CR LF
    ++i;
  }
  byte numlastchar = numfirstchar + i - 1;
  a6decodepdusms(numfirstchar, countbytepdu, numlastchar, phonenumber, textsms);
}

void a6decodepdusms(byte firstcharpdu, byte counttextpdu, byte lastcharpdu, char* senderphone, char* textsms) {
  // firstcharpdu - pos TP-SCA + 1 byte size
  byte nf = strHexTobyte(_rsp_buf[firstcharpdu], _rsp_buf[firstcharpdu + 1]) + 1; // new pos =  TP-MTI & Co
  ++nf; // new pos = TP-OA
  byte lf = strHexTobyte(_rsp_buf[firstcharpdu + (nf * 2)], _rsp_buf[firstcharpdu + (nf * 2) + 1]); // lenght sender phone number
  // check format number
  ++nf;
  if ((_rsp_buf[firstcharpdu + (nf * 2)] != '9') || (_rsp_buf[firstcharpdu + (nf * 2) + 1] != '1')) return; // not digit format
  ++nf;
  senderphone[0] = '+';
  for (byte i = 0; i < lf; ++i) {
    senderphone[i + 1] = _rsp_buf[firstcharpdu + (nf * 2) + i + 1];
    senderphone[i + 2] = _rsp_buf[firstcharpdu + (nf * 2) + i];
    ++i;
  }
  nf *= 2;
  nf += lf;
  if (senderphone[lf] == 'F') senderphone[lf] = 0; else senderphone[lf + 1] = 0;
  ++nf; // next char - pos TP-PID
  nf += 2; // pos TP-DCS
  boolean flUCS2;
  if ((_rsp_buf[firstcharpdu + nf] == '0') && (_rsp_buf[firstcharpdu + nf + 1] == '0')) flUCS2 = false; else flUCS2 = true;
  nf += 2; // pos TP-SCTS
  nf += 14; // pos TP-UDL
  byte lenpdutext = strHexTobyte(_rsp_buf[firstcharpdu + nf], _rsp_buf[firstcharpdu + nf + 1]);
  nf += 2; // pos TP-UD
  if (flUCS2) {
    a6decodetextpduUCS2(lenpdutext, (firstcharpdu + nf), textsms);
  } else {
    a6decodetextpdu7bit(lenpdutext, (firstcharpdu + nf), textsms);
  }
}

byte strHexTobyte(char p1, char p2) {
  p1 -= '0';
  if (p1 > 10) (p1 -= 7);
  p1 = p1 << 4;
  p2 -= '0';
  if (p2 > 10) (p2 -= 7);
  return (p1 | p2);
}

void a6decodetextpdu7bit(byte charcount, byte firstpos, char* textsms) {
  for (byte i = 0; i < charcount; ++i) {
    byte begin7bytepart = i / 8;
    byte resultbyte = 0;
    byte numbyte = i % 8;
    byte b1 = firstpos + (begin7bytepart * 2 * 8) + (numbyte * 2) - (2 * (begin7bytepart + 1));
    byte b2 = strHexTobyte(_rsp_buf[b1 + 2], _rsp_buf[b1 + 3]);
    b1 = strHexTobyte(_rsp_buf[b1], _rsp_buf[b1 + 1]);
    byte lowb;
    if (numbyte == 0) {
      lowb = b2;
    } else {
      if (numbyte < 7) (lowb = b2 << numbyte); else (lowb = 0);
    }
    byte hib;
    if (numbyte > 0) (hib = b1 >> (8 - numbyte)); else (hib = 0);
    resultbyte = hib | lowb;
    textsms[i] = resultbyte & 0x7F;
  }
  textsms[charcount] = 0;
}

void a6decodetextpduUCS2(byte bytecount, byte firstpos, char* textsms) {
  for (byte i = 0; i < bytecount; i += 2) {
    byte b1 = firstpos + (i * 2);
    byte b2 = strHexTobyte(_rsp_buf[b1 + 2], _rsp_buf[b1 + 3]);
    b1 = strHexTobyte(_rsp_buf[b1], _rsp_buf[b1 + 1]);
    if (b1 == 0) {
      textsms[i / 2] = b2;
    } else {
      byte rb;
      if (b1 == 0x04) {
        switch (b2) {
          case 0x01: {
              rb = 168;
              break;
            }
          case 0x51: {
              rb = 184;
              break;
            }
          default: {
              if ((b2 >= 0x10) && (b2 <= 0x4F)) {
                rb = b2 + 176;
              } else {
                rb = b2;
              }
            }
        }
      } else {
        rb = b2;
      }
      textsms[i / 2] = rb;
    }
  }
  textsms[bytecount / 2] = 0;
}

void a6getopsname(char* opsname, char* opsbalance) {
  boolean fl = false;
  byte i;
  for (i = 0; i < 12; ++i) {
    if (strPos(_rsp_buf, OPSlist[i]) >= 0) {
      fl = true;
      break;
    }
  }
  if (fl) {
    byte j = 0; while ((OPScode[j] = OPSlist[i][j]) != 0) ++j; OPScode[j] = 0;
    j = 0; while ((opsname[j] = OPSlist[i - 2][j]) != 0) ++j; opsname[j] = 0;
    j = 0; while ((opsbalance[j] = OPSlist[i - 1][j]) != 0) ++j; opsbalance[j] = 0;
  }
}


 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Update:

посмотрел свой древний код, по факту вам необходима только одна функция
void sendSMS() {
 

на вход подается char строка в Win1251 кодировке
не забудьте перевести модем в режим отправки сообщений в PDU формате

 

Belka_hohol
Offline
Зарегистрирован: 30.12.2020

Спасибо большое за помощь. К вопросу зачем мене именно комманда AT+CNUM (AT+CNUM=?), да в сущности незачем, я просто птался проверить что SIM карта работает с модемом правильно и с нее можно считать информацию. Меня напрягает только тот факт, что весь код раньше работал правильно и я не знаю после чего он перестал рпботать.

Спасибо еще раз, позже посмотрю ваш код и напишу, наверное, как все прошло.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Для проверки работоспособности модема есть масса других команд, регистрация в сети, уровень сигнала, данные базовых станций.....