SIM800 не отвечает на АТ команды

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

andycat пишет:

а весь ваш взятый код с ресурса построен по принципу "отправил что то - жду команду" а в остальное время если что то надо сделать или придет другой ответ - "все пропало"

Вот тут я не поняла. 

Незапрашиваемые уведомления обрабатываются же постоянно.

if (SIM800.available())   {                         // Если модем, что-то отправил...
    _response = waitResponse();                       // Получаем ответ от модема для анализа
    _response.trim();                                 // Убираем лишние пробелы в начале и конце
    Serial.println(_response);                        // Если нужно выводим в монитор порта
    if (_response.indexOf("+CMTI:")>-1) {             // Пришло сообщение об отправке SMS
      lastUpdate = millis() -  updatePeriod;          // Теперь нет необходимости обрабатываеть SMS здесь, достаточно просто
                                                      // сбросить счетчик автопроверки и в следующем цикле все будет обработано
    }
  }
  if (Serial.available())  {                          // Ожидаем команды по Serial...
    SIM800.write(Serial.read());                      // ...и отправляем полученную команду модему
  };
}

 

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

Пожалуйста ситуация - пришла смс, но МК ждал ок от отправки смс или тупо завис - команда потерялась.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Так статус смс не изхменится с "Непрочитанное" пока контроллер его не прочитает (после того как дождется ОК или перезагрузится после зависания). Так ведь?

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

Я не знаю как вам объяснить что ваш скетч кривой и устройство на нем будет работать не предсказуемо (

TELE2RUS 7дн.15час.36мин.R118.

Вот например ответ моего устройства - обратите внимание на аптайм, и то это мало - свет в квартире включали.

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

Т е свет выключали

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Библиотекой Sim800l-master не пользовались?

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

Irinka пишет:

Библиотекой Sim800l-master не пользовались?

Нет конечно, все ручками, at командами

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Чтож...буду изучать...

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

Votmax пишет:
Мне лично статьи понравились. Человек что то сделал, у него работает. Он не зажал скетч, не стал умничать на форуме, а выложил его на всеобщее обозрение, за что ему спасибо!
Выкладывание кода на всеобщее обозрение не делает его автоматически безупречным. Это типовой говнокод. Взять хотя бы сложение с millis с последующим сравнением, чел не понимает целочисленной арифметики, тупо закладывает в свой код баг, который бездумно тиражируется на этом ресурсе.

Votmax пишет:
Понятно, что проще критиковать, но если есть реальные основания так считать, то поделитесь со всеми. Что именно вам не понравилось?
Ардуино - это игрушка для школьников. Никто в здравом уме не станет использовать её для сколь нибудь серьезных решений. Её назначение поиграться и выкинуть. Индусский код тоже шлак, но здесь он рассматривается как эталон. Среда разработки - х#ня редкостная. Топология китайская - на отъ#$%ись. Что здесь вообще может понравится?

Votmax пишет:
Выложите свои примеры работы с модулем.

Вот предсерийный образец, для сухих отапливаемых помещений, не для авто. SIM800, стек USB, шифрование, закладки на радиоканал, SD карту и RTC. Код написан в кейле для STM, выкладывать здесь нет смысла. Страшно представить, что получится, если собрать это устройство из блоков для ардуино...

Irinka, где сброс SIM800 о котором ты спрашивала, где включение...?

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

Irinka пишет:

Кто сталкивался с проблемой плохого контакта сим карты? В машине часто теряется связь.

проблема 100% не в контакте который на симкарте....

40% - качественное и правильное питание !

40% - код ! может тупо "виснуть" из-за кода..

20% - проблема использования глупых плат от совсем дешевого ардуино..

поэтому рисую свои платы на сим800С ..

Votmax
Offline
Зарегистрирован: 18.06.2016

Andy пишет:

.... Это типовой говнокод.... Ардуино - это игрушка для школьников. Индусский код тоже шлак, но здесь он рассматривается как эталон. Среда разработки - х#ня редкостная. Топология китайская - на отъ#$%ись. ....

Ахахахаха! Ответ на миллион!

Видно, что пишет профессионал)))). Хвастанул фоткой и материт все остальное. Мол, я гений, а вы тут все идиоты....

Ну давайте еще сравним промышленное серийное производство с поделками любителей.

Для многих любителей Ардуино - это хобби, где не нужно паять, тратить на это время. Минимум усилий и можно получить какую то никому не нужную, но ценную для самого себя игрушку. Таких людей никто не учит и скорее всего этому промыслу они и не учились. Для них любая информация, вроде той, которую вы поносите, ценна!

Не важно, что код работает с огрехами, главное понять принцип! А, уж, когда получилось, всегда есть возможность код исправить и переписать. 

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

Конечно, легко критиковать чужое, но не стоит, это низко.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Andy пишет:

Irinka, где сброс SIM800 о котором ты спрашивала, где включение...?

Сброс происходит 

 Serial.println("Timeout..."); в этом месте кода

Serial.println ("Error answer");  и здесь

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Ввиду большого количества заинтересованных лиц, давайте сделаем хороший, стабильный пример* работы с SIM800.

PS ввиду моих небольших познаний, с меня тестирование XD

Votmax
Offline
Зарегистрирован: 18.06.2016

Irinka пишет:

Ввиду большого количества заинтересованных лиц, давайте сделаем хороший, стабильный пример* работы с SIM800.

PS ввиду моих небольших познаний, с меня тестирование XD

Я только ЗА!

Я делал так. Набросал такой код:

#include <SoftwareSerial.h>
SoftwareSerial ss1(2, 3);


void setup()
{
  Serial.begin(9600);
  ss1.begin(9600);
}

void loop()
{

if (ss1.available())
    Serial.write(ss1.read());
  if (Serial.available())
    ss1.write(Serial.read());

}

И стал проверять АТ команды, посылая их на модуль, и анализируя ответы на них.  Команды брал тут: http://www.2150692.ru/faq/47-at-komandy-a6

Там описание модуля SIM900, но по работе с СМС подходит. Все вполне логично работает. А зная, какие приходят ответы, не сложно дальше развивать свой проект. Сейчас я только начал заниматься обработкой входящих смс, вроде ничего сложно, но нужно время. Как что то получится путное, сброшу.

P.S. Конечно, наш великий критик, Анди, скажет, что это ***код)))), куда нам до него...)))

 

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Я сделала вот так:

Читаю только последнее полученное смс и удаляю все.

Добавила проверку при запуске - если модуль не отвечает на AT - перезагрузка.

И раз в x времени проверку на готовность модуля совершать звонки (возможно нужно заменить на проверку уровня сигнала сети или что-то другое)

#define DEBUG //Если отладка не нужна, закомментировать
#ifdef DEBUG
#define DEBUG_PRINT(x)       Serial.print (x)
#define DEBUG_PRINTLN(x)     Serial.println (x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif

#include <avr/wdt.h>
#include <SoftwareSerial.h>
SoftwareSerial SIM800(6, 7);//RX, TX
#define _pinreset A3//Пин ресета
long vrstatusa = 0;
String _buffer = "";

//Функция перезагрузки Nano и SIM800
void(* resetFunc) (void) = 0;
void restart() {
DEBUG_PRINTLN("Modul negotov. Reset");
digitalWrite(_pinreset, LOW); 
delay(1000);          
digitalWrite(_pinreset, HIGH);   
resetFunc();
}




void setup() {
wdt_disable();
Serial.begin(9600);
SIM800.begin(9600);    
pinMode(_pinreset, OUTPUT);
digitalWrite(_pinreset, HIGH);                                     
DEBUG_PRINTLN("Start!");
delay(5000); 
if (sendATCommand("AT", true).indexOf("OK") > -1) {
DEBUG_PRINTLN("Modul gotov");
if (sendATCommand("AT+CLIP=1", true).indexOf("OK") > -1)  DEBUG_PRINTLN("AON vcliuchen");
if (sendATCommand("AT+CMGF=1", true).indexOf("OK") > -1)  DEBUG_PRINTLN("Tekstovy rezhim sms vcliuchen");
if (sendATCommand("AT+CMGDA=\"DEL ALL\"", true).indexOf("OK") > -1)  DEBUG_PRINTLN("Sms udaleny");
DEBUG_PRINTLN("Rabotaem");
vrstatusa = millis();
}else{
restart();
}
wdt_enable (WDTO_8S);
}





String sendATCommand(String cmd, bool waiting) {
String _response = "";
//DEBUG_PRINTLN(cmd);
SIM800.println(cmd);
if (waiting) {
_response = waitResponse();
if (_response.startsWith(cmd)) {
_response = _response.substring(_response.indexOf("\r\n", cmd.length()) + 2);
}
//DEBUG_PRINTLN(_response);
return _response;
}
return "";
}

String waitResponse() {
String _buffer;
long _timeout = millis() + 10000;
while (!SIM800.available() && millis() < _timeout)  {};
if (SIM800.available()) {
_buffer = SIM800.readString();
return _buffer;
}else{
DEBUG_PRINTLN("Timeout...");
}
return "";
}



void getActionBySMS(String msg) {  
  String msgheader  = "";
  String msgbody    = "";
  String msgphone   = "";

  msg = msg.substring(msg.indexOf("+CMGR: "));
  msgheader = msg.substring(0, msg.indexOf("\r"));            // Выдергиваем телефон

  msgbody = msg.substring(msgheader.length() + 2);
  msgbody = msgbody.substring(0, msgbody.lastIndexOf("OK"));  // Выдергиваем текст SMS
  msgbody.trim();

  int firstIndex = msgheader.indexOf("\",\"") + 3;
  int secondIndex = msgheader.indexOf("\",\"", firstIndex);
  msgphone = msgheader.substring(firstIndex, secondIndex);

  Serial.println("Phone: " + msgphone);                       // Выводим номер телефона
  Serial.println("Message: " + msgbody);  
}

void loop() {
wdt_reset();



//Проверяем готовность модуля совершать звонки (если не может - перезагрузка)
if (millis()-vrstatusa>60000 ){
_buffer = sendATCommand("AT+CCALR?", true);
if (_buffer.indexOf("+CCALR: 1") > -1) {  
DEBUG_PRINTLN("Status-Ok");
vrstatusa = millis();
}else{
DEBUG_PRINTLN("Status-Error"); 
restart();
}
}


if (SIM800.available()){
wdt_reset();  
String msg = waitResponse();
msg.trim();
DEBUG_PRINTLN(".. " + msg);

//Сбрасываем все входящие вызовы
if (msg.startsWith("RING")) {
sendATCommand("ATH", true);
DEBUG_PRINTLN("Vyzov sbroshen");
}

//Если есть какая-то ошибка
else if (msg.startsWith("ERROR")) {
DEBUG_PRINTLN("Error");
}
//Если пришло входящее сообщение
else if (msg.startsWith("+CMTI:")) {
  _buffer = sendATCommand("AT+CMGL=\"REC UNREAD\",1", true);// Отправляем запрос чтения непрочитанных сообщений
if (_buffer.indexOf("+CMGL: ") > -1) { //Если есть непрочитанное сообщение             
_buffer = sendATCommand("AT+CMGR=1,1", true); //Читаю последнее полученное сообщение
_buffer.trim(); 
if (_buffer.endsWith("OK")) {
getActionBySMS(_buffer);    
}
}
sendATCommand("AT+CMGDA=\"DEL ALL\"", true);
}
}
}

 

Votmax
Offline
Зарегистрирован: 18.06.2016

Irinka пишет:

....на проверку уровня сигнала сети....)


Кстати, хорошая идея, нужно воспользоваться! Если нет сигнала, то и нет смысла отправлять смс! 

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

Irinka пишет:

Ввиду большого количества заинтересованных лиц, давайте сделаем хороший, стабильный пример* работы с SIM800.

PS ввиду моих небольших познаний, с меня тестирование XD

 

А для модема a6  нужен?

ЗЫ. Интересно как определить стабильность надёжность проекта (

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

andycat пишет:

А для модема a6  нужен?

ЗЫ. Интересно как определить стабильность надёжность проекта (

Что такое аб?

Стабильность определить, наверное, временем работы без косяков*

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017
#include <avr/wdt.h>
#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"
#include <OneWire.h>
#include <DallasTemperature.h>
#include <RCSwitch.h>

#define max_time_read_sms 120000UL // 2min
#define max_time_read_command 10000UL // 10sec
#define startEEPROMmessages 128 // 0 position = byte - current save posifion from 0
#define endEEPROMmessages 383
#define max_count_users_phone 6
#define max_len_sms 252
#define keypad_pin A0
#define min_time_period_key_press 1000
#define alarm_pin A1
#define rc_reciv_pin 2
#define ds18b20_pin 3
#define period_get_internal_temp 317947 // ~ 5 min
#define pos_eeprom_relay1_mode 456 // 0 or FF - off 12 -on
#define pos_eeprom_relay2_mode 458 // 0 or FF - off 21 -on
#define pos_eeprom_count_reset_timeout 454

OneWire oneWire(ds18b20_pin);
DallasTemperature sensors(&oneWire);
RCSwitch mySwitch = RCSwitch();

unsigned long time_read_sms;
unsigned long time_read_command;
unsigned long time_unpress_key;
byte gsmmode, submode;
char OPSname[] = "Russia00";
char OPSbalance[] = "*99999#";
char* UnitCommands[] = {"addphone", "delphone", "getuptime", "getbalance", "gettemp", "resetmodem", "relay1on", "relay1off", "relay2on", "relay2off", "getrelay", "resetdevice"};
char sender[24];
char smsmsg[max_len_sms];
char begstr[] = "~NEWsSMS~";
char endstr[] = "~SUCCSMS~";
boolean sw_lcd_light;
unsigned long time_sw_lcd_light;
boolean req_temp;
byte internal_temp;
byte external_temp;
unsigned long time_get_internal_temp;
unsigned long time_req_internal_temp;
word mas_str_temp[10];
byte med_str_pos = 0;
byte med_str_cnt = 0;
boolean minus_str_temp = false;

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

void loop() {
  // put your main code here, to run repeatedly:
  wdt_reset();
  unsigned long current_millis = millis();
  // lcd backlight control
  if (!sw_lcd_light) {
    word palm = analogRead(alarm_pin);
    if (palm < 500) {
      time_sw_lcd_light = current_millis;
      sw_lcd_light = true;
      digitalWrite(10, HIGH);
    }
  } else {
    if ((current_millis - time_sw_lcd_light) >= 30000) { // 30 sec - lcd light on
      sw_lcd_light = false;
      digitalWrite(10, LOW);
    }
  }
  // end lcd backlight control
  // get ext temp
  if (mySwitch.available()) {
    unsigned long receivedCode =  mySwitch.getReceivedValue();
    mySwitch.resetAvailable();
    if ((receivedCode >= 11500UL) && (receivedCode <= 14750UL)) {
      receivedCode -= 11500;
      if (((receivedCode >= 2000UL) && (receivedCode <= 2600UL)) || ((receivedCode >= 0UL) && (receivedCode <= 400UL))) {
        if ((receivedCode <= 400UL)) {
          receivedCode = 2000UL - receivedCode;
        }
        // correct temp
        if (receivedCode <= 1900UL) {
          receivedCode -= 12UL; // 1.2 degree
        } else {
          if (receivedCode <= 1950UL) {
            receivedCode -= 8UL; // 0.8 degree
          }
          else {
            if (receivedCode <= 1999UL) {
              receivedCode -= 5UL; // 0.5 degree
            }
          }
        }
        // --
        mas_str_temp[med_str_pos] = (word)(receivedCode);
        if ((++med_str_pos) >= 10) {
          med_str_pos = 0;
        }
        if ((++med_str_cnt) >= 10) {
          med_str_cnt = 10;
        }
        word sum_temp = 0;
        for (int i = 0; i < med_str_cnt; ++i) {
          sum_temp += mas_str_temp[i];
        }
        sum_temp /= med_str_cnt;
        if (sum_temp < 2000) {
          minus_str_temp = true;
          sum_temp = 2000 - sum_temp;
        } else {
          minus_str_temp = false;
          sum_temp -= 2000;
        }
        sum_temp /= 10;
        external_temp = (byte)(sum_temp);
      }
    }
  }
  // end get ext temp
  byte result = a6work();
  if ((result == 1) || (result == 2)) {
    if (result == 1) {
      domainmode();
    } else {
      //timeout
      byte br = EEPROM.read(pos_eeprom_count_reset_timeout);
      if (br == 0xFF) br = 0;
      ++br;
      EEPROM.write(pos_eeprom_count_reset_timeout, br);
      wdt_disable();
      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", "", "", true) == 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
    // read key pad
    if (gsmmode == 1) {
      if ((current_millis - time_unpress_key) >= min_time_period_key_press) {
        word kp = analogRead(keypad_pin);
        byte key_mode;
        if ((kp > 600) && (kp < 700)) {
          key_mode = 0;
        } else {
          if ((kp > 370) && (kp < 450)) {
            key_mode = 1;
          } else {
            if ((kp > 80) && (kp < 120)) {
              key_mode = 2;
            } else {
              if ((kp > 216) && (kp < 296)) {
                key_mode = 3;
              } else {
                if (kp < 20) {
                  key_mode = 4;
                } else {
                  key_mode = 5; // not press
                }
              }
            }
          }
        }
        if (key_mode < 5) {
          time_unpress_key = current_millis;
          switch (key_mode) {
            case 0: {
                char strtemp[4];
                SendStrToLCD("Int=");
                strtemp[0] = '0' + internal_temp / 10;
                strtemp[1] = '0' + internal_temp % 10;
                strtemp[2] = 0;
                SendStrToLCD(strtemp);
                SendStrToLCD(" Ext=");
                if (minus_str_temp) strtemp[0] = '-'; else strtemp[0] = '+';
                strtemp[1] = '0' + external_temp / 10;
                strtemp[2] = '0' + external_temp % 10;
                strtemp[3] = 0;
                SendStrToLCD(strtemp);
                SendStrToLCD(" ");
                break;
              }
            case 1: {
                if (a6sendcmd("AT+CREG?", "OK", "", "", true) == 1) {
                  gsmmode = 3; submode = 1;
                }
                break;
              }
            case 2: {
                byte i = 0;
                while ((smsmsg[i] = OPSname[i]) != 0 ) ++i;
                smsmsg[i] = ' '; ++i;
                i = addTextUptime(i);
                smsmsg[i] = 0;
                SendStrToLCD(smsmsg);
                break;
              }
            case 3: {
                gsmmode = 100;
                // show phones from eeprom
                byte pc = EEPROM.read(0);
                if (pc == 0xFF) pc = 0;
                for (byte i = 0; ((i < pc) && (i < max_count_users_phone)); ++i) {
                  byte possend = 0; boolean fle = true;
                  word startpos = 1 + i * 14;
                  if (EEPROM.read(startpos) == 'A') sender[possend] = 'A'; else sender[possend] = 'D';
                  ++possend; sender[possend] = ' '; ++possend;
                  byte j = 0;
                  while (EEPROM.read(startpos + j + 1) != 0) {
                    sender[possend] = EEPROM.read(startpos + j + 1);
                    ++j; ++possend;
                  }
                  sender[possend] = ' '; ++possend; sender[possend] = 0;
                  SendStrToLCD(sender);
                  unsigned long delp = millis(); while ((millis() - delp) <= 2000);
                  wdt_reset();
                }
                // end show phones
                resetModeAndClearRespBuf();
                break;
              }
            case 4: {
                if (a6sendcmd("AT+CSQ", "OK", "", "", true) == 1) {
                  gsmmode = 3; submode = 1;
                }
                break;
              }
            default: {
              }
          }
        }
      }
    }
    // end key pad
    // req temp
    if ((current_millis - time_req_internal_temp) >= period_get_internal_temp) {
      if ((gsmmode == 1) && (!req_temp)) {
        time_req_internal_temp = current_millis;
        sensors.requestTemperatures();
        req_temp = true;
        time_get_internal_temp = current_millis;
      }
    }
    // end req temp -> get temp
    if ((current_millis - time_get_internal_temp) >= 1200) {
      if ((gsmmode == 1) && (req_temp)) {
        internal_temp = sensors.getTempCByIndex(0);
        req_temp = false;
      }
    }
    // end get internal temp
  }
}

void domainmode() {
  switch (gsmmode) {
    case 0: { // init modem
        switch (submode) {
          case 0: { // wait reponse first command AT
              if (a6sendcmd("ATE0", "OK", "", "", true) == 1) submode = 1; // no echo
              break;
            }
          case 1: { // wait reponse command ATE0
              if (a6sendcmd("ATV1", "OK", "", "", true) == 1) submode = 2; // get text for error
              break;
            }
          case 2: { // wait reponse command ATV1
              if (a6sendcmd("AT+CMEE=2", "OK", "", "", true) == 1) submode = 3; // get full text error
              break;
            }
          case 3: { // wait reponse command cmee
              if (a6sendcmd("AT+CLIP=1", "OK", "", "", true) == 1) submode = 4; // on aon
              break;
            }
          case 4: { // wait reponse command clip
              if (a6sendcmd("ATS0=3", "OK", "", "", true) == 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", "", true) == 1) submode = 6; // true registration
              //if (a6sendcmd("AT+CREG?", "OK", "", "", true) == 1) submode = 6; // registration
              break;
            }
          case 6: { // wait registration
              if (a6sendcmd("AT+CMGF=0", "OK", "", "", true) == 1) submode = 7; // mode SMS = PDU
              break;
            }
          case 7: { // wait mode sms
              if (a6sendcmd("AT+CMGD=1,4", "OK", "", "", true) == 1) submode = 8; // delete all sms
              break;
            }
          case 8: { // wait del sms
              if (a6sendcmd("AT+COPS?", "+COPS:", "OK", "", true) == 1) submode = 9; // load operator data
              break;
            }
          case 9: { // decode operator data
              a6getopsname(OPSname, OPSbalance);
              resetModeAndClearRespBuf(); // init ok
              digitalWrite(LED_BUILTIN, HIGH);
              SendByteToLCD(32, true);
              SendByteToLCD(223, false);
              SendByteToLCD(32, true);
              SendByteToLCD(226, false);
              SendByteToLCD(32, true);
              SendByteToLCD(241, false);
              SendByteToLCD(229, false);
              SendByteToLCD(242, false);
              SendByteToLCD(232, false);
              SendByteToLCD(32, true);
              SendByteToLCD('!', true);
              SendByteToLCD(32, true);
              wdt_enable(WDTO_8S);
              break;
            }
        }
        break;
      }
    case 1: { // main loop
        break;
      }
    case 2: { // mode read sms
        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", "", "", true) == 1) submode = 1;
              } else {
                resetModeAndClearRespBuf(); // no sms
              }
              break;
            }
          default: {
              resetModeAndClearRespBuf();
            }
        }
        break;
      }
    case 3: { // mode send sms and mode test commands
        switch (submode) {
          case 0: {
              if (a6sendcmd(smsmsg, "+CMGS:", "OK", "", false) == 1) submode = 1;
              break;
            }
          default: {
              resetModeAndClearRespBuf();
            }
        }
        break;
      }
    case 4: { // mode get balance
        switch (submode) {
          case 0: {
              // decode USSD
              // send SMS text from USSD
              resetModeAndClearRespBuf(); // temp text, need submode = 1
              break;
            }
          default: {
              resetModeAndClearRespBuf();
            }
        }
        break;
      }
    default: {
        a6clearrespbuf();
      }
  }
}

void firststart() {
  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);
  pinMode(10, OUTPUT);
  pinMode(rc_reciv_pin, INPUT);
  digitalWrite(10, LOW); // LCD backlight control
  sensors.begin();
  time_req_internal_temp = 0;
  req_temp = false;
  sw_lcd_light = false;
  gsmmode = 0; submode = 0; // start
  time_read_sms = 0UL; time_read_command = 0UL;
  mySwitch.enableReceive(0);
  pinMode(A2, OUTPUT); // relay 1
  pinMode(A3, OUTPUT); // relay 2
  // on off relay
  if (EEPROM.read(pos_eeprom_relay1_mode) == 12) digitalWrite(A2, LOW); else digitalWrite(A2, HIGH);
  if (EEPROM.read(pos_eeprom_relay2_mode) == 21) digitalWrite(A3, LOW); else digitalWrite(A3, HIGH);
  // end on off relay
  pinMode(A4, OUTPUT); digitalWrite(A4, LOW);
  pinMode(A5, OUTPUT); digitalWrite(A5, LOW);
  a6initmodem(9600); // work - hard uart
}

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 < 12; ++i) { // 12 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", true);
          gsmmode = 4; submode = 0;
          break;
        }
      case 5: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          // prepare send text
          char strtemp[] = "Int=";
          i = 0;
          while ((smsmsg[i] = strtemp[i]) != 0 ) ++i;
          strtemp[0] = '0' + internal_temp / 10;
          strtemp[1] = '0' + internal_temp % 10;
          strtemp[2] = 0;
          j = 0;
          while ((smsmsg[i] = strtemp[j]) != 0 ) {
            ++i; ++j;
          }
          smsmsg[i] = ','; ++i;
          strtemp[0] = 'E'; strtemp[1] = 'x'; strtemp[2] = 't'; strtemp[3] = '='; strtemp[4] = 0;
          j = 0;
          while ((smsmsg[i] = strtemp[j]) != 0 ) {
            ++i; ++j;
          }
          if (minus_str_temp) strtemp[0] = '-'; else strtemp[0] = '+';
          strtemp[1] = '0' + external_temp / 10;
          strtemp[2] = '0' + external_temp % 10;
          strtemp[3] = 0;
          j = 0;
          while ((smsmsg[i] = strtemp[j]) != 0 ) {
            ++i; ++j;
          }
          smsmsg[i] = ','; ++i;
          i = addTextUptime(i);
          smsmsg[i] = 0;
          sendSMS();
          break;
        }
      case 6: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          byte br = EEPROM.read(pos_eeprom_count_reset_timeout);
          if (br == 0xFF) br = 0;
          ++br;
          EEPROM.write(pos_eeprom_count_reset_timeout, br);
          wdt_disable();
          a6resetmodem();
          unsigned long delp = millis(); while ((millis() - delp) <= 20000); // boot modem
          firststart();
          break;
        }
      case 7: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          // relay 1 ON
          EEPROM.write(pos_eeprom_relay1_mode, 12);
          digitalWrite(A2, LOW);
          unsigned long delp = millis(); while ((millis() - delp) <= 1000);
          // relay 2 ON
          EEPROM.write(pos_eeprom_relay2_mode, 21);
          digitalWrite(A3, LOW);
          break;
        }
      case 8: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          // relay 1 OFF
          EEPROM.write(pos_eeprom_relay1_mode, 00);
          digitalWrite(A2, HIGH);
          unsigned long delp = millis(); while ((millis() - delp) <= 1000);
          // relay 2 OFF
          EEPROM.write(pos_eeprom_relay2_mode, 00);
          digitalWrite(A3, HIGH);
          break;
        }
      case 9: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          // relay 2 ON
          EEPROM.write(pos_eeprom_relay2_mode, 21);
          digitalWrite(A3, LOW);
          unsigned long delp = millis(); while ((millis() - delp) <= 1000);
          // relay 1 ON
          EEPROM.write(pos_eeprom_relay1_mode, 12);
          digitalWrite(A2, LOW);
          break;
        }
      case 10: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          // relay 2 OFF
          EEPROM.write(pos_eeprom_relay2_mode, 00);
          digitalWrite(A3, HIGH);
          unsigned long delp = millis(); while ((millis() - delp) <= 1000);
          // relay 1 OFF
          EEPROM.write(pos_eeprom_relay1_mode, 00);
          digitalWrite(A2, HIGH);
          break;
        }
      case 11: {
          if (getAllowCommandPhone(sender) == 0) return; // command for any user
          // prepare send text
          char strtemp[] = "Rl1";
          i = 0;
          while ((smsmsg[i] = strtemp[i]) != 0 ) ++i;
          if (EEPROM.read(pos_eeprom_relay1_mode) == 12) {
            strtemp[0] = 'o'; strtemp[1] = 'n'; strtemp[2] = 0;
          } else {
            strtemp[0] = 'o'; strtemp[1] = 'f'; strtemp[2] = 'f'; strtemp[3] = 0;
          }
          j = 0;
          while ((smsmsg[i] = strtemp[j]) != 0 ) {
            ++i; ++j;
          }
          smsmsg[i] = ','; ++i;
          strtemp[0] = 'R'; strtemp[1] = 'l'; strtemp[2] = '2'; strtemp[3] = 0;
          j = 0;
          while ((smsmsg[i] = strtemp[j]) != 0 ) {
            ++i; ++j;
          }
          if (EEPROM.read(pos_eeprom_relay2_mode) == 21) {
            strtemp[0] = 'o'; strtemp[1] = 'n'; strtemp[2] = 0;
          } else {
            strtemp[0] = 'o'; strtemp[1] = 'f'; strtemp[2] = 'f'; strtemp[3] = 0;
          }
          j = 0;
          while ((smsmsg[i] = strtemp[j]) != 0 ) {
            ++i; ++j;
          }
          smsmsg[i] = ','; ++i;
          i = addTextUptime(i);
          smsmsg[i] = 0;
          sendSMS();
          break;
        }
      case 12: {
          if (getAllowCommandPhone(sender) != 2) return; // command for admin only
          // test WDT
          unsigned long delp = millis(); while ((millis() - delp) <= 20000); // reboot device after 8 sec
          break;
        }
      default: {
        }
    }
  } 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 == 0xFF) pc = 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) {
    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;
  // add count timeout reset
  smsmsg[firstpos] = 'R'; ++firstpos;
  byte br = EEPROM.read(pos_eeprom_count_reset_timeout);
  smsmsg[firstpos] = '0' + (br / 100); ++firstpos;
  smsmsg[firstpos] = '0' + ((br % 100) / 10); ++firstpos;
  smsmsg[firstpos] = '0' + ((br % 100) % 10); ++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, ">", "", "", false);
  gsmmode = 3; submode = 0;
}

void resetMode() {
  gsmmode = 1; submode = 0; // main loop
}

void resetModeAndClearRespBuf() {
  resetMode();
  a6clearrespbuf();
}

 

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

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];
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";
byte IdxLCD = 0; // 0..31 - char on LCD

LiquidCrystal_1602_RUS LCD(8, 9, 4, 5, 6, 7 );//For LCD Keypad Shield

// 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) {
  // init LCD
  LCD.begin(16, 2);
  LCD.clear();
  LCD.setCursor(0, 0);
  IdxLCD = 0;
  SendByteToLCD(209, false);
  SendByteToLCD(242, false);
  SendByteToLCD(224, false);
  SendByteToLCD(240, false);
  SendByteToLCD(242, false);
  SendByteToLCD(32, true);
  SendStrToLCD("Start ");
  LCD.cursor();
  LCD.blink();
  // init modem
  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);
  Serial.begin(a6uartspeed);
  // adaptive speed a6 module
  unsigned long curr_millis = millis();
  while ((millis() - curr_millis) <= 4000UL) Serial.println("AT");
  curr_millis = millis();
  while ((millis() - curr_millis) <= 5000UL) if (Serial.available()) while (Serial.available()) Serial.read();
  // -
  wait_response = false;
  a6sendcmd("AT", "OK", "", "", true); // 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, boolean show_cmd) {
  if (wait_response) return 0;
  Serial.println(txtcmd);
  if (show_cmd) SendStrToLCD(txtcmd);
  cmd_started_time = millis();
  a6clearrespbuf();
  byte pp = 0;
  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 (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 (_rsp_pos > 0) {
      if (_rsp_pos <= 32) SendStrToLCD(_rsp_buf);
    }
  }
  // wait response for sent at command
  if (wait_response) {
    if ((millis() - cmd_started_time) >= RESPONSE_MAX_TIME) {
      wait_response = false;
      return 2; // timeout
    } else {
      if (a6detectresp(_resp_detect1, _resp_detect2, _resp_detect3)) {
        wait_response = false;
        return 1; // OK
      }
    }
  }
  return 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 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;
  }
}

// send win1251 byte to LCD
void SendByteToLCD(byte inByte, boolean ET) {
  if (inByte == 13) {
    LCD.print(" ");
  } else {
    if (inByte < 0x20) return;
    if ((ET == true) && (inByte >= 0x7F)) return;
    if (ET == false) {
      if ((inByte == 168) || (inByte == 184) || ((inByte >= 192) && (inByte <= 255))) {
        LCD.print(LCD.asciiutf8(inByte));
      } else {
        LCD.print(char(inByte));
      }
    } else {
      LCD.print(char(inByte));
    }
  }
  switch (IdxLCD) {
    case 15: {
        LCD.setCursor(0, 1);
        ++IdxLCD;
        break;
      }
    case 31: {
        LCD.setCursor(0, 0);
        IdxLCD = 0;
        break;
      }
    default : {
        ++IdxLCD;
      }
  }
}

void SendStrToLCD(char* latinStr) {
  byte i = 0;
  while (latinStr[i] != 0) {
    SendByteToLCD(latinStr[i], false);
    ++i;
  }
}

 



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

#define _a6_power_pin 12
#define _a6_reset_pin 11

#define RESPONSE_BUF_SIZE 252
#define RESPONSE_DETECT_SIZE 16
#define RESPONSE_MAX_TIME 30000
#define ADMIN_PHONE "+7920xxxxx46"

void a6initmodem(word a6uartspeed);
byte a6sendcmd(char* txtcmd, char* resp1, char* resp2, char* resp3, boolean show_cmd);
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 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);
void SendByteToLCD(byte inByte, boolean ET);
void SendStrToLCD(char* latinStr);

 

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

Такая проблема:

При проверке баланса (Теле2 *105#) ардуина перезагружается либо не обрабатывает текст:

 

 

Код:

#define DEBUG //Если отладка не нужна, закомментировать
#ifdef DEBUG
#define DEBUG_PRINT(x)       Serial.print (x)
#define DEBUG_PRINTLN(x)     Serial.println (x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif

#include <avr/wdt.h>
#include <SoftwareSerial.h>
SoftwareSerial SIM800(6, 7);//RX, TX
#define _pinreset A3//Пин ресета
long vrstatusa = 0;
String _buffer = "";
String phones = "+79601809765";

//Функция перезагрузки Nano и SIM800
void(* resetFunc) (void) = 0;
void restart() {
DEBUG_PRINTLN("Modul negotov. Reset");
digitalWrite(_pinreset, LOW); 
delay(1000);          
digitalWrite(_pinreset, HIGH);   
resetFunc();
}


void setup() {
wdt_disable();
Serial.begin(9600);
SIM800.begin(9600);    
pinMode(_pinreset, OUTPUT);
digitalWrite(_pinreset, HIGH);                                     
DEBUG_PRINTLN("Start!");
//delay(5000); 
//if (sendATCommand("AT", true).indexOf("OK") > -1) {

_buffer = sendATCommand("AT", true);
_buffer.trim(); 
if (_buffer.endsWith("OK")) {  

  
DEBUG_PRINTLN("Modul gotov");
if (sendATCommand("AT+CLIP=1", true).indexOf("OK") > -1)  DEBUG_PRINTLN("AON vcliuchen");
if (sendATCommand("AT+CMGF=1", true).indexOf("OK") > -1)  DEBUG_PRINTLN("Tekstovy rezhim sms vcliuchen");
if (sendATCommand("AT+CMGDA=\"DEL ALL\"", true).indexOf("OK") > -1)  DEBUG_PRINTLN("Sms udaleny");
DEBUG_PRINTLN("Rabotaem");
vrstatusa = millis();
}else{
restart();
}
wdt_enable (WDTO_8S);
}


//Обработка полученных AT команд
String sendATCommand(String cmd, bool waiting) {
wdt_reset();  
String _response = "";
DEBUG_PRINTLN(cmd);
SIM800.println(cmd);
if (waiting) {
_response = waitResponse();
if (_response.startsWith(cmd)) {
_response = _response.substring(_response.indexOf("\r\n", cmd.length()) + 2);
}
DEBUG_PRINTLN(_response);
return _response;
}
return "";
}

String waitResponse() {
long _timeout = millis() + 10000;
while (!SIM800.available() && millis() < _timeout)  {};
if (SIM800.available()) {
_buffer = SIM800.readString();
return _buffer;
}else{
DEBUG_PRINTLN("Timeout...");
}
return "";
}


//Получаем номер и текст сообщения
void parsimSMS(String msg) {
  wdt_reset();    
  String msgheader  = "";
  String msgbody    = "";
  String msgphone   = "";
msg = msg.substring(msg.indexOf("+CMGR: "));
msgheader = msg.substring(0, msg.indexOf("\r"));
msgbody = msg.substring(msgheader.length() + 2);
msgbody = msgbody.substring(0, msgbody.lastIndexOf("OK"));
msgbody.trim();
int firstIndex = msgheader.indexOf("\",\"") + 3;
int secondIndex = msgheader.indexOf("\",\"", firstIndex);
msgphone = msgheader.substring(firstIndex, secondIndex);
DEBUG_PRINTLN("Phone: " + msgphone);
DEBUG_PRINTLN("Message: " + msgbody);

if (msgphone.length() > 6 && phones.indexOf(msgphone) > -1) {
if (msgbody.length() > 5 ) {
if (msgbody=="StartOn"){
DEBUG_PRINTLN("Vcliucheno");
}
else if (msgbody=="StartOff"){
DEBUG_PRINTLN("Vycliucheno");
}

else if (msgbody=="Balans"){
sendATCommand("AT+CUSD=1,\"*105#\"", true);
}

}
}
}

//Функция проверки готовности модуля
void Sim800Stat() {
wdt_reset();  
_buffer = sendATCommand("AT", true);
_buffer.trim(); 
if (_buffer.endsWith("OK")) {  
_buffer = sendATCommand("AT+CCALR?", true);
_buffer.trim();
if (_buffer.indexOf("+CCALR: 1") > -1) {  
DEBUG_PRINTLN("Status-Ok");
vrstatusa = millis();
}else{
DEBUG_PRINTLN("Status-Error"); 
restart();
}
}else{
DEBUG_PRINTLN("Modul ne otvechaet");
restart();
}
}

//Функция отправки смс
void sendSMS(String message)
{
  sendATCommand("AT+CMGS=\"" + phones + "\"", true);
  sendATCommand(message + "\r\n" + (String)((char)26), true);
}


void loop() {
wdt_reset();


if (Serial.available()) {
int val = Serial.read();
if (val == '2') sendATCommand("AT+CUSD=1,\"*105#\"", true);
if (val == '3') sendATCommand("AT+CUSD=1,\"*120#\"", true);


}







//Раз в минуту проверяем готовность модуля
//if (millis()-vrstatusa>10000 ){
//Sim800Stat();
//}


if (SIM800.available()){
wdt_reset();  
String msg = waitResponse();
msg.trim();
DEBUG_PRINTLN(".. " + msg);

//Сбрасываем все входящие вызовы
if (msg.startsWith("RING")) {
sendATCommand("ATH", true);
DEBUG_PRINTLN("Vyzov sbroshen");
}

//Если есть какая-то ошибка
else if (msg.startsWith("ERROR")) {
DEBUG_PRINTLN("Error");
}


// Пришло уведомление о USSD-ответе
else if (msg.startsWith("+CUSD:")) {
if (msg.indexOf("\"") > -1) {
String msgBalance = msg.substring(msg.indexOf("\"") + 1);
msgBalance = msgBalance.substring(0, msgBalance.indexOf("\""));
DEBUG_PRINTLN("USSD: " +msgBalance);
}
}



//Если пришло входящее сообщение
else if (msg.startsWith("+CMTI:")) {
_buffer = sendATCommand("AT+CMGL=\"REC UNREAD\",1", true);// Отправляем запрос чтения непрочитанных сообщений
if (_buffer.indexOf("+CMGL: ") > -1) { //Если есть непрочитанное сообщение             
_buffer = sendATCommand("AT+CMGR=1,1", true); //Читаю последнее полученное сообщение
_buffer.trim(); 
if (_buffer.endsWith("OK")) {
parsimSMS(_buffer);    
}
}
sendATCommand("AT+CMGDA=\"DEL ALL\"", true);
}
}



}

 

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

ставлю на то что памяти не хватает

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Я тоже так думаю.  Как это можно исправить? 

Как мне кажется, моя проблема при пересечении границы области, как раз в этом и заключается, что оператор присылает сообщение типа: "Теле2 желает вам приятной поездки ........ и. .... т..... п....."

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

сначала определиться дело в памяти или как

если я правильно понял код - попробовать после 78 строки поставить условие что если длина буфера больше например 20 символов то не принимать ответ

соотвественно если после этого МК не зависнет, значит да, дело в памяти - избавляться от String, парсить ответ по частям.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017


Почему не образается переменная _buffer?


String waitResponse() {
long _timeout = millis() + 10000;
while (!SIM800.available() && millis() < _timeout)  {};
if (SIM800.available()) {
_buffer = SIM800.readString();
if (_buffer.length() > 20){
DEBUG_PRINTLN("Bufer perepolnen");
_buffer = _buffer.substring(0,20);
}

return _buffer;
}else{
DEBUG_PRINTLN("Timeout...");
}
return "";
}

А здесь обрезается как надо:

if (SIM800.available()){
wdt_reset();  
String msg = waitResponse();
msg.trim();
if (msg.length() > 20){
DEBUG_PRINTLN("Bufer perepolnen");
msg = msg.substring(0,20);
}
DEBUG_PRINTLN(".. " + msg);

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Вот так работает:

#define DEBUG //Если отладка не нужна, закомментировать
#ifdef DEBUG
#define DEBUG_PRINT(x)       Serial.print (x)
#define DEBUG_PRINTLN(x)     Serial.println (x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif

#include <avr/wdt.h>
#include <SoftwareSerial.h>
SoftwareSerial SIM800(6, 7);//RX, TX
#define _pinreset A3//Пин ресета
long vrstatusa = 0;
String _buffer = "";
String phones = "+79601809765";
bool razmbuf=false;


//Функция перезагрузки Nano и SIM800
void(* resetFunc) (void) = 0;
void restart() {
DEBUG_PRINTLN("Modul negotov. Reset");
digitalWrite(_pinreset, LOW); 
delay(1000);          
digitalWrite(_pinreset, HIGH);   
resetFunc();
}


void setup() {
wdt_disable();
Serial.begin(19200);
SIM800.begin(19200);    
pinMode(_pinreset, OUTPUT);
digitalWrite(_pinreset, HIGH);                                     
DEBUG_PRINTLN("Start!");
//delay(5000); 
_buffer = sendATCommand("AT", true);
_buffer.trim(); 
if (_buffer.endsWith("OK")) {  
DEBUG_PRINTLN("Modul gotov");
if (sendATCommand("AT+CLIP=1", true).indexOf("OK") > -1)  DEBUG_PRINTLN("AON vcliuchen");
if (sendATCommand("AT+CMGF=1", true).indexOf("OK") > -1)  DEBUG_PRINTLN("Tekstovy rezhim sms vcliuchen");
if (sendATCommand("AT+CMGDA=\"DEL ALL\"", true).indexOf("OK") > -1)  DEBUG_PRINTLN("Sms udaleny");
DEBUG_PRINTLN("Rabotaem");
vrstatusa = millis();
}else{
restart();
}
wdt_enable (WDTO_8S);
}

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);
}

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);
}



//Обработка полученных AT команд
String sendATCommand(String cmd, bool waiting) {
wdt_reset();  
String _response = "";
//DEBUG_PRINTLN(cmd);
SIM800.println(cmd);
if (waiting) {
_response = waitResponse();
if (_response.startsWith(cmd)) {
_response = _response.substring(_response.indexOf("\r\n", cmd.length()) + 2);
}
//DEBUG_PRINTLN(_response);
return _response;
}
return "";
}

String waitResponse() {
long _timeout = millis() + 10000;
while (!SIM800.available() && millis() < _timeout)  {};
if (SIM800.available()) {
_buffer = SIM800.readString();
return _buffer;
}else{
DEBUG_PRINTLN("Timeout...");
}
return "";
}


//Получаем номер и текст сообщения
void parsimSMS(String msg) {
  wdt_reset();    
  String msgheader  = "";
  String msgbody    = "";
  String msgphone   = "";
msg = msg.substring(msg.indexOf("+CMGR: "));
msgheader = msg.substring(0, msg.indexOf("\r"));
msgbody = msg.substring(msgheader.length() + 2);
msgbody = msgbody.substring(0, msgbody.lastIndexOf("OK"));
msgbody.trim();
int firstIndex = msgheader.indexOf("\",\"") + 3;
int secondIndex = msgheader.indexOf("\",\"", firstIndex);
msgphone = msgheader.substring(firstIndex, secondIndex);
DEBUG_PRINTLN("Phone: " + msgphone);
DEBUG_PRINTLN("Message: " + msgbody);

if (msgphone.length() > 6 && phones.indexOf(msgphone) > -1) {
if (msgbody.length() > 5 ) {
if (msgbody=="StartOn"){
DEBUG_PRINTLN("Vcliucheno");
}
else if (msgbody=="StartOff"){
DEBUG_PRINTLN("Vycliucheno");
}

else if (msgbody=="Balans"){
sendATCommand("AT+CUSD=1,\"*105#\"", true);
}

}
}
}


//Функция проверки готовности модуля
void Sim800Stat() {
wdt_reset();  
_buffer = sendATCommand("AT+CCALR?", true);
_buffer.trim();
if (_buffer.indexOf("+CCALR: 1") > -1) {  
DEBUG_PRINTLN("Status-Ok");
vrstatusa = millis();
}else{
DEBUG_PRINTLN("Status-Error"); 
restart();
}
}

//Функция отправки смс
void sendSMS(String message)
{
  sendATCommand("AT+CMGS=\"" + phones + "\"", true);
  sendATCommand(message + "\r\n" + (String)((char)26), true);
}


void loop() {
wdt_reset();


if (Serial.available()) {
int val = Serial.read();
if (val == '2') sendATCommand("AT+CUSD=1,\"*105#\"", true);
if (val == '3') sendATCommand("AT+CUSD=1,\"*120#\"", true);


}







//Раз в минуту проверяем готовность модуля
if (millis()-vrstatusa>60000 ){
Sim800Stat();
}


if (SIM800.available()){
wdt_reset();  
razmbuf=false;
String msg = waitResponse();
msg.trim();
if (msg.length() > 50){
razmbuf=true;
DEBUG_PRINTLN("Bufer perepolnen");
msg = msg.substring(0,80);
}
DEBUG_PRINTLN(".. " + msg);

//Сбрасываем все входящие вызовы
if (msg.startsWith("RING")) {
sendATCommand("ATH", true);
DEBUG_PRINTLN("Vyzov sbroshen");
}

//Если есть какая-то ошибка
else if (msg.startsWith("ERROR")) {
DEBUG_PRINTLN("Error");
}


// Пришло уведомление о USSD-ответе
else if (msg.startsWith("+CUSD:")) {
if (msg.indexOf("\"") > -1) {
String msgBalance = msg.substring(msg.indexOf("\"") + 1);
msgBalance = msgBalance.substring(0, msgBalance.indexOf("\""));
if (razmbuf){
DEBUG_PRINTLN("USSD: " + UCS2ToString(msgBalance));
}else{
DEBUG_PRINTLN("USSD: " + msgBalance);
}

}
}



//Если пришло входящее сообщение
else if (msg.startsWith("+CMTI:")) {
_buffer = sendATCommand("AT+CMGL=\"REC UNREAD\",1", true);// Отправляем запрос чтения непрочитанных сообщений
if (_buffer.indexOf("+CMGL: ") > -1) { //Если есть непрочитанное сообщение             
_buffer = sendATCommand("AT+CMGR=1,1", true); //Читаю последнее полученное сообщение
_buffer.trim(); 
if (_buffer.endsWith("OK")) {
parsimSMS(_buffer);    
}
}
sendATCommand("AT+CMGDA=\"DEL ALL\"", true);
}
}



}

 

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

Это все костыли к сожалению. 

Добиться таким образом работы проекта можно, но задела на будущее нет(

Простейший вариант вам: на ходу обрабатывать смс о балансе и откидываьь лишнее. Ну а если по уму делать - все переписывать надо.

ЗЫ. Впрочем терять свое время / жизнь ради красоты кода - тоже хрень. Если задача выполняется - оставляйте как есть. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Votmax пишет:

Для многих любителей Ардуино - это хобби, где не нужно паять, тратить на это время. Минимум усилий и можно получить какую то никому не нужную, но ценную для самого себя игрушку. Таких людей никто не учит и скорее всего этому промыслу они и не учились. Для них любая информация, вроде той, которую вы поносите, ценна!

Не следует путать информацию с дезинформацией.

Если сделано откровенно неправильно - в стиле "образец, как не нужно делать", а преподносится наоборот, как образец для\ подражания, то вред от такой "информации" существенно перекрывает возможную пользу.

Ваше утверждение эквивалентно следующему: любое сообщение ценно, вне зависимости от того, исинно оно или ложно.

Между ложью и истиной не может быть знака равенства.

Цитата:

Нельзя говорить, что код плохой, если он работает.

Если код плохой - он плохой вне зависимости от того, работает или нет.

Более того, от плохого, но работающего (естественно, ненадежно, и не всегда так, как хотел автор) кода вреда больше, чем от неработающего.

Votmax
Offline
Зарегистрирован: 18.06.2016

andriano пишет:

Если код плохой - он плохой вне зависимости от того, работает или нет.

О, еще один умник (философ) появился.

Программа работает по заложенному алгоритму. И если программисту нравится, как работает его код, значит он своей цели достиг, по крайней мере не в промышленных образцах, а в подделках любителей.

Можно с ума сходить добиваясь идеала, я так понимаю вы этим и страдаете, но часто это и не требуется.

Вы все ополчились на человека, который разобрался в работе с модулем, создал свой блог, написал статью и делится информацией с окружающими. Как только вы его, его код и ардуино не принижаете только!

Не нравится ардуино, не пользуйтесь и не сидите на этом форуме.

Еще раз повторюсь, проще критиковать, говорить, что код плохой и тд и тп, чем вы и занимаетесь. Мне тоже он не нравится, я работаю чисто с АТ командами, но тот код помог лично мне разобраться в каких то деталях и начать реализовывать свой проект, за что ему СПАСИБО!

А вы попробуйте вместо критики создать страничку в интернете, выложить свой код, сделать комментарии к нему, да еще и отвечать на вопросы в комментах. Тогда это будет по делу, это будет в пользу!

Но хочу вас предупредить, что как только вы сделаете это, найдется очередной Анди, который назовет ваш код, как он выражается - *****кодом ))), выложит какую то фотку и будет рассказывать, как он мега крут, а вы в программировании ничего не понимаете! А еще найдется философ, вроде вас, который начнет рассказывать про информацию и дезинформацию, про то , что истина это не ложь)))) И кстати, есть истина, есть ложь, а есть статистика, не забывайте про это!

Вот, что я хочу до вас донести! Все форумы в последнее время страдают этим и это печально.

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

Votmax пишет:

 

+100500

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

Votmax пишет:
Хвастанул фоткой и материт все остальное. Мол, я гений, а вы тут все идиоты....
Ты спросил, я ответил. Покажи своё говнотворчество...

Votmax пишет:
Ну давайте еще сравним промышленное серийное производство с поделками любителей.
Давай сравним результат твоих и моих усилий. Моя разработка с нуля до серийного производства. А что сделал ты? Соединил проводками чужие платы и написал несколько десятков строк говнокода... Всё. Я ничего не упустил?

Votmax пишет:
И если программисту нравится, как работает его код, значит он своей цели достиг
Между программистом и говнокодером огромная пропасть. Ты застрял на уровне школьного творчества, тебе до программиста, как до китая... Если твой код работает 5 минут, это еще не значит, что он будет работать круглый год, 24/7.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017
Andy, это ваша работа, не надо сравнивать проф разработку и устройство которое просто должно работать (кстати вполне возможно работать будет хорошо и круглогодично.
По поводу программтстов- лично для меня это слово ругательство, т к большинство это кодеры, которые видят только ТЗ а мозг включать не умеют и не видят всей задачи. 
Поработав за последние 20 лет в разных областях и повидав десятки программ для ПС очень дорогих, понял что программистов единицы процентов, остальные просто уроды, которых хочется посадить за их произведения поработать, например заставить вбить клиента в базу за 3 минуты по нормативу, но это невозможно, поскольку отвратительный интерфейс и никакого удобства.
 
Votmax
Offline
Зарегистрирован: 18.06.2016

Andy пишет:

Покажи своё говнотворчество...

А что сделал ты? Соединил проводками чужие платы и написал несколько десятков строк говнокода...

Между программистом и говнокодером огромная пропасть. Ты застрял на уровне школьного творчества, тебе до программиста, как до китая... Если твой код работает 5 минут, это еще не значит, что он будет работать круглый год, 24/7.

По моему, дальнейшее обуждение чего либо с этим человеком бессмысленно. Подрасти для начала)))

 

Votmax
Offline
Зарегистрирован: 18.06.2016

andycat пишет:

По поводу программтстов- лично для меня это слово ругательство.

Согласен!

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Где модераторы? Друзья, мы отклонились от темы?)

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

Irinka пишет:

Где модераторы? Друзья, мы отклонились от темы?)

а у вас был какой то вопрос :) ?

вроде все заработало? че бы не помусорить на форуме :) за удачное исполнение проекта :)

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Добрый день (по крайней мере у нас во Владимире). Есть вопрос)

Для перезагрузки SIM800 я произвожу проверку на готовность модуля совершать звонки AT+CCALR?

Если вынуть сим карту (что я и делаю для проверки), то модуль естественно возвращает +CCALR: 0

0 — модуль не готов совершать звонки

Если в какой-то момент пропадет сеть (антенка надежно припаяна, не могу проверить это условие) модуль также вернет +CCALR: 0 или нет?

Правильно ли производить такую проверку или использовать другую команду, например:

AT+CREG? - тип регистрации в сети

и если модуль ответит

1 — зарегистрирован в домашней сети

5 — зарегистрирован, в роуминге

то все нормально, иначе - перезагрузка

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

andycat пишет:
че бы не помусорить на форуме :)

Такими темпами arduino.ru превратится в forum.cxem.net где сидят старенькие дяденьки задроты (сугубо моё мнение), которым лень отвечать на вопросы новичков или, по их мнению, "элементарные" вопросы, и вместо этого начинают флуууудить да посылать читать литератуууууру (времен СССР).....воооооттт..... XD

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

я наверное не правильно понял....модем нужно перезагружать если он завис - правильно?

тогда при чем здесь регистрация в сети? у меня в деревне в принципе сети нет, но это не причина перезагрузки.

каждые две минуты проверяются входящие смс, если от АТ команды (любой) нет ответа в течении 30 секунд - перезагрузка.

 

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

На данном этапе меня интересует способность модуля принимать и совершать звонки. (наверное это выше нужно было указать) При любом входящем смс-сообщении все смски удаляются.

Если нет ответа OK от АТ команды - это однозначно перезагрузка модуля.  

Вот я и спрашиваю, какой командой лучше проверять может ли модуль принять звонок или позвонить.

 

 

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

я AT+CREG? проверяю + уровень сигнала AT+CSQ, меньше 7 - связь на грани разрыва.

 

Baks
Baks аватар
Offline
Зарегистрирован: 11.01.2016

заглядываю в тему

и вижу что люди используют симки теле2... 

я  пытался запустить sim800 на симке теле2, но не заработал, немного почитал и понял что симки и модуль не понимают друг друга из за чистоты(стандарта связи)

тут или я ошибся или у кого работает на теле2 с другого региона?

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Baks здравствуйте. У меня работает сим-карта ТЕЛЕ2 как Владимирской области, так и Нижегородской области.

andycat Спасибо.

Владимирская сим-карта при нахождении в Нижегородской области пишет +CREG: 0,1 зарегистрирован в домашней сети

 

 

 

Votmax
Offline
Зарегистрирован: 18.06.2016

Baks пишет:

я  пытался запустить sim800 на симке теле2, но не заработал, немного почитал и понял что симки и модуль не понимают друг друга из за чистоты(стандарта связи)

Да, кстати, у меня с Теле2 тоже не заработало. Использую МТС.

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

Baks пишет:

заглядываю в тему

и вижу что люди используют симки теле2... 

я  пытался запустить sim800 на симке теле2, но не заработал, немного почитал и понял что симки и модуль не понимают друг друга из за чистоты(стандарта связи)

тут или я ошибся или у кого работает на теле2 с другого региона?

а вы в каком регионе? если я правильно помню, в МСК нет 2G сети (может быть не прав), соотвественно и sim800 с данной симкой работать не будет.

Votmax
Offline
Зарегистрирован: 18.06.2016

Кстати, может не по делу, когда столкнулся с проблемой с памятью,  стал использовать F(), отбил у памяти 300 Бт )).

Вдруг пригодится.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Но всё же  если сеть пропадет, модуль оповестит +CCALR: 0?

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

Votmax пишет:

Кстати, может не по делу, когда столкнулся с проблемой с памятью,  стал использовать F(), отбил у памяти 300 Бт )).

Вдруг пригодится.

я тоже так хотел, но то ли я криворукий, то ли есть ограничение на эту штуку, но это удобно если использовать например Serial.println(F"ATE0"), а у меня все команды формируются или динамически или из массива, короче ничего не получилось, скетч выше приведенный почти 90% памяти кушает, уже ничего не впихнешь.

сейчас с нуля начал новый, буду все делать по другому, более универсальный с возможностью не только смс но и dtmf и gprs, пока с работой только смс уже 43 % памяти, но это дело не быстрое....к лету может допишу.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Votmax пишет:

Кстати, может не по делу, когда столкнулся с проблемой с памятью,  стал использовать F(), отбил у памяти 300 Бт )).

Вдруг пригодится.

А я вот так сделала:

#define DEBUG// Если отладка не нужна, эту строчку закомментировать
#ifdef DEBUG
#define DEBUG_PRINT(x)       Serial.print (x)
#define DEBUG_PRINTLN(x)     Serial.println (x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif

И в нужно месте пишем:

DEBUG_PRINTLN("Привет");
DEBUG_PRINT("Пока");

 

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

Irinka пишет:

Но всё же  если сеть пропадет, модуль оповестит +CCALR: 0?

кого оповестит? и как? вам необходимо мониторить качество и наличие сети? наверное в даташите это есть, читать надо, выдерните симку во время работы и посмотрите что нибудь модем скажет или нет.

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