Как правильно работать с GSM модулем (да и любым устройством по serial) без delay?

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

Считаю, что код в стиле:













gsmSerial.println("ATH");
delay(5000);
gsmSerial.println("ATD>1;");
delay(30000);
gsmSerial.println("ATD>2;");

не совсем правильный,  хоть и прост, и правильнее было бы работать без delay, но это достаточно сложно, так как нужно сначала запросить что-то, потом ждать ответ и правильно его соотнести, так как может быть ответ сначала на другой запрос или вообще например сообщение о низком заряде аккумулятора, а ждешь статус модуля допустим, поэтому включенное ЭХО может и пригодиться (если я правильно понял его работу).

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

Тут в коде идет проверка статуса модуля и если он не готов к звонку, то положить трубку если идет разговор или запросить попозже, если модуль чем-то занят, если нет ответа более 5 сек., то еще запросить и так 5 раз, если ничего непомогло перезагрузка модуля, и всё по-новой через 20 сек. Если же модуль готов, то позвонить на первый номер, дальше нет кода, но будет уже проверка ответа на запрос звонка (занят, нет сети, нет ответа и т.д.), если что звонок на второй номер, или также перезагрузка и т.д. и т.п.





void loop() {
   static unsigned long currentMillis = millis();
   static boolean alarm = false;
   static byte alarmMode = 0;
   static unsigned long alarmPreviousMillis = 0;

   
   if(какое-то условие)
     alarm = true;


   if(gsmSerial.available())
   {
      char inChar = (char)gsmSerial.read();
      
      if(inChar == '\r')
      {
		 if(gsmString.startsWith("+CPAS:"))	// текущее состояние модуля
		 {
			if(alarmMode == 1 || alarmMode == 3 || alarmMode == 5 || alarmMode == 7 || alarmMode == 9)
			{
			   String pasString = gsmString.substring(7); // 0 – готовность к работе, 2 - ответ и выполнение команд не гарантируется, 3 - идет входящий звонок, 4 установлено голосовое соединение
			   
			   if(pasString == "0")
			   {
				  gsmSerial.println("ATD>1;");	// позвонить на номер в первой ячейке СИМ
				  alarmMode = 21;
				  alarmPreviousMillis = currentMillis;
			   }
			   else if(pasString == "3" || pasString == "4")
			   {
				  gsmSerial.println("ATH");	// завершить все соединения
				  alarmMode = 11;
				  alarmPreviousMillis = currentMillis;
			   }
			   else //2
			   {
				  if(alarmMode < 9)
				  {
					 alarmMode++;
					 alarmPreviousMillis = currentMillis;
				  }
				  else
				  {
					 pinMode(rstGSMPin, OUTPUT);	// сброс модуля
					 delay(500);
					 digitalWrite(rstGSMPin, INPUT);
					 alarmMode = 10;
					 alarmPreviousMillis = currentMillis;
				  }
			   }
			}
		 }
		 else if(gsmString == "OK")
		 {
			if(alarmMode == 11)
			{
			   gsmSerial.println("ATD>1;");	// позвонить на номер в первой ячейке СИМ
			   alarmMode = 21;
			   alarmPreviousMillis = currentMillis;
			}
		 }
		 
		 gsmString = "";
      }
      else if (inChar != '\n') 
      {
		gsmString += inChar;
      }
   
      
   }
   
   
   
   
   if(alarm)
   {
      if(alarmMode == 0)
      {
		 gsmSerial.println("AT+CPAS");	// запросить текущее состояние модуля
		 alarmMode = 1;
		 alarmPreviousMillis = currentMillis;
      }
      else if(alarmMode >= 1 && alarmMode <= 8)
      {
		 if(currentMillis - alarmPreviousMillis >= 5000)
		 {
			gsmSerial.println("AT+CPAS");	// запросить текущее состояние модуля
			alarmMode += alarmMode%2 + 1;
			alarmPreviousMillis = currentMillis;
		 }
      }
      else if(alarmMode == 9 || alarmMode == 11)
      {	 
		 if(alarmMode == 9 && currentMillis - alarmPreviousMillis >= 5000
			|| alarmMode == 11 && currentMillis - alarmPreviousMillis > 20000)
		 {
			pinMode(rstGSMPin, OUTPUT);	// сброс модуля
			delay(500);
			digitalWrite(rstGSMPin, INPUT);
			alarmMode = 10;
			alarmPreviousMillis = currentMillis;
		 }
      }
      else if(alarmMode == 10)
      {
		 if(currentMillis - alarmPreviousMillis >= 20000)
		 {
			alarmMode = 0;
			alarmPreviousMillis = currentMillis;
		 }
      }
   }
}

 

Radjah
Offline
Зарегистрирован: 06.08.2014

Вижу чтение одного байта в 14 строке и много записи.

Что такое gsmString? Вижу много работы с этим объектом на чтение, но ничиге в него не пишется.

Вот как хреновый руководитель: орать умеет, а слушать не хочет.

Кричишь в Serial команду и в цикле ждешь ответ, считая период ожидания при этом. Если нарвался на таймаут ответа, то рапортуешь об ошибке.

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

Jatixo пишет:
В общем набросал звонок на первый номер, и уже каша в коде, а это еще нет проверки ответа, занято, линия недоступна или сброшен вызов, или все ок, и т.п.
Реализация алгоритма в таком виде - путь в никуда. Нужно нарисовать граф состояний, переходов и условий.

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

Вот поэтому и спросил, потому что чувствую, что есть более правильный путь, только какой? Или все так, только граф нарисовать и по нему продолжать писать?

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

Radjah пишет:

Что такое gsmString? Вижу много работы с этим объектом на чтение, но ничиге в него не пишется.


Туда складываются байты, а когда это становится полной строкой, то разбирается, что за сообщение пришло.

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

Jatixo пишет:
только граф нарисовать и по нему продолжать писать?

Граф - это половина успеха, он легко перекладывается в код.

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

Andy, Допустим, то что написано в первом посте написано по графу, то алгоритм нормальный или Вы что-нибудь переделали? Подскажите, пожалуйста, просто хочется сразу правильно писать.

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

То что написано, к графу состояний не имеет отношения, это не более, чем реализация алгоритма из кубиков и ромбиков. Надо описать именно состояния устройства.

готовность к работе, идет входящий вызов, установлено голосовое соединение - это все состояния.

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

Если Вы имеете ввиду переменную, в которой находится значение в каком состоянии сейчас устройство до момента когда захочется позвонить, то если бы она была, допустим был входящий звонок и он отвечен, то все равно в этой переменной будет то что сейчас звонок, а завершен он или нет, неизвестно, но можно конечно следить за этим наверное, еще не проверял, но даже если можно, надежнее проверить все равно, как тут 5 раз и делается, точнее 1 раз точно, а если что-то пошло не так, то еще попытки. Как же по-вашему будет выглядеть алгоритм на словах или в коде, хотя бы первые итерации так сказать, а то непонятно что не так и что бы Вы поменяли...

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

Прмерно так:

switch (состояние)
{
case 0: if (условие) {gsmSerial.print("что-то"); состояние=1;}
        if (условие) {gsmSerial.print("что-то другое"); состояние=2; } 
        break;
case 1: if (условие | условие) {timer.Start(ххх); состояние=3;}
        break;
case 2: и т.д.
default: break
}

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

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

Так у меня тоже самое написано, только на if, потому что в данном случае гибче, типа "if(alarmMode >= 1 && alarmMode <= 8)", чем case 1: case 2: ... case 8: и вообще можно любые условия ставить.

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

Удачи.

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

Ну чтож сделал на if, но понял что с цифрами не очень удобно работать, и как раз для этого есть enum, назвал все цифры словами так сказать, стало удобнее, и переделал в итоге на switch, так как все равно других условий нет, кроме alarmMode, а также там и компилятор проверяет если что не так, например не все переменные из enum указаны.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Очень неправильный подход. Первое: сразу переделывайте на serialEvent - это сильно лучше и грамотнее. Второе - конечный автомат вас спасёт. Главное условие - никаких блокировок, это, можно сказать, залог успеха и защиты от потери данных вследствие переполнения буфера RX.

В общем случае алгоритм прост: в serialEventN (смотря какой хардварный Serial вы пользуете) вычитываете данные построчно. Как придёт строка - только тогда обрабатываете её. Обработали, поняли, что нужно сделать - сменили состояние автомата, чтобы при следующем заходе в loop выполнить требуемое действие. Псевдокод:

String buff;
void serialEvent1()
{
  char ch;
  while(Serial1.available())
  {
     ch = (char) Serial1.read();
     if(ch == '\r') continue;
     if(ch == '\n')
        { ProcessAnswer(buff); buff = ""; }
    else
      buff += ch;
  }
}

void ProcessAnswer(const String& line)
{
 if(line == "OK")
 {
   switch(machineState)
   {
     case waitingForRegistration: // ждали регистрации в сети
       commandQueue.push(wantToSendSMS); // говорим, что на следующем заходе надо послать SMS
     break;
    }
 }
}

Сходу всего не напишешь, поэтому советую почитать информацию по конечным автоматам ;)

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

Я не на меге делаю, и аппаратный Serial оставил для логирования, хотя и надо только пока тестируется, но и с программным все в принципе работает. Да и прошивать проще, можно не отключать от модуля.

Возможно Вы думаете, что serialEvent использует прерывания и поэтому рекомендуете (извините если не так), но на самом деле это просто функция вызывается каждый раз в loop, то есть, что сам напишешь, что ее вызывать, все равно, а то может и больше затрат, так как еще функция вызывается, а не сразу код. Но в любом случае в SoftwareSerial все равно нет этой функции...

Ну а по коду, я примерно так и делаю, только без вызова фукнции, а прямо в коде... И переделал с вложенных состояний в ответах, наоборот сначала проверка состояний, а потом внутри уже от ответа "плясать", посчитал так удобней. Опытным путем экономлю память, правда пока хватает, просто заранее на всякий случай, разница и в вызове фукнции, если она один раз вызывается и передача переменной по ссылке против глобальной, все влияет...

Radjah
Offline
Зарегистрирован: 06.08.2014

> Возможно Вы думаете, что serialEvent использует прерывания и поэтому рекомендуете (извините если не так), но на самом деле это просто функция вызывается каждый раз в loop, то есть, что сам напишешь

Читать до полного просветления.

https://www.arduino.cc/en/Reference/SerialEvent

https://www.arduino.cc/en/Tutorial/SerialEvent

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Jatixo пишет:

Возможно Вы думаете, что serialEvent использует прерывания и поэтому рекомендуете (извините если не так), но на самом деле это просто функция вызывается каждый раз в loop, то есть, что сам напишешь, что ее вызывать, все равно, а то может и больше затрат, так как еще функция вызывается, а не сразу код.

Я в курсе, спасибо. Но использовать serialEvent всё равно грамотнее, чем просто вычитывать самому по while и ждать, не находите? Накладные расходы грамотный компилятор, к слову, сведёт к минимуму, это раз. Два - функция сама вызывается только тогда, когда в порту есть данные - уже удобно, не надо специально ничего предпринимать. Три - использование serialEvent позволяет избавиться от головняков вида "блин, и вот тут надо проверять, есть ли данные из порта!" Ну и, наконец - использование такого подхода позволит легко и быстро, при необходимости, переписать кусок кода на прерывания, никак не затрагивая ваш конечный автомат.

Скажем, опять же, из опыта - тупой код в лоб с вычитыванием данных из одного порта до посинения, пока в другой приходят данные - приводит к потере данных из второго порта вследствие переполнения буфера. В этом контексте serialEvent можно рассматривать как таймслоты, которые делят между собой порты. Мне каицца, что именно для этого всё и было задумано ;)

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

Radjah, а что читать, то,

вот то что я и говорю:







  SerialEvent occurs whenever a new data comes in the
  hardware serial RX.  This routine is run between each
  time loop() runs, so using delay inside loop can delay
  response.  Multiple bytes of data may be available.

 

А вот код из исходников Arduino:







int main(void)
{
	init();

	initVariant();

#if defined(USBCON)
	USBDevice.attach();
#endif
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}

 

 

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

DIYMan, спасибо, вероятно, в моем коде лучше if(gsmSerial.available()), заменить на while, чтобы считывалось за раз если есть и знать, что бесконечно данных не будет. Но даже с serialEvent с while внутри, как Вы и написали, данные будут считываться пока не кончатся, даже если в другие сериалы придет что-то, и также данные потеряются. Так как функции все вызываются по порядку, пока не отработает предыдущая.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Jatixo пишет:

Но даже с serialEvent с while внутри, как Вы и написали, данные будут считываться пока не кончатся, даже если в другие сериалы придет что-то, и также данные потеряются. Так как функции все вызываются по порядку, пока не отработает предыдущая.

Всё правильно вы пишете, но не принимаете во внимание один нюанс, при работе с несколькими портами. Допустим, в первый порт мы послали команду и нам надо дождаться ответа. Псевдокод:

SendCommand("ATE0");
while(1)
{
  if(Serial.available() && Serial.find("OK"))
    break;
}
// тут что-то делаем дальше

То такой вот подход с while наглухо блокирует работу с остальными портами, пока на нужном порту вы не вычитаете ответ. Кстати - довольно распространённый подход среди негодных интернет-примеров ;)

В результате - потеря данных на других портах. Если же юзать банальный serialEvent, то всё простейшим образом расщепляется на таймслоты, т.е. - послали команду, а в нужном serialEvent вычитываем информацию, и когда получаем ответ - делаем что-то. При этом имеем возможность автоматически работать с другими портами, конечно, при условии грамотной организации кода, как вы правильно заметили - если висеть почём зря и ждать, когда порт разродится данными - не поможет и serialEvent.

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

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

DIYMan пишет:

 

В результате - потеря данных на других портах. Если же юзать банальный serialEvent, то всё простейшим образом расщепляется на таймслоты, т.е. - послали команду, а в нужном serialEvent вычитываем информацию, и когда получаем ответ - делаем что-то. При этом имеем возможность автоматически работать с другими портами, конечно, при условии грамотной организации кода, как вы правильно заметили - если висеть почём зря и ждать, когда порт разродится данными - не поможет и serialEvent.

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

пример проблемного кода увидел, суть понял.

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

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Short Circuit пишет:

пример проблемного кода увидел, суть понял.

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

Пример с serialEvent:

String receiveBuff; // строка, куда будем складывать данные, пока не вычитаем до \r\n

void serialEvent()
{
 while(Serial.available())
 {
   char ch = Serial.read();
   if(ch == '\r') continue;
   if(ch == '\n') { Process(receiveBuff); receiveBuff = "";}
   else receiveBuff += ch;

   yield(); // даём поработать коду, который требует срочного внимания.
 }
}
void Process(const String& line)
{
// тут обрабатываем строку, которая пришла от модуля 
}

Впрочем, serialEvent - это не прерывания, конечно. Для действительно неблокирующей другие порты вычитки надо либо дописывать HardwareSerial, либо - вовсе отказываться от него и обрабатывать прерывания ручками.

Обратите внимание на использование функции yield - это хороший тон для обеспечения кооперативной работы. Если вы пишете библиотеку или класс, который внутри себя может выполнять длительную работу и при этом хотите, чтобы другие части кода тоже могли что-то делать, то в нужных местах ставите вызов функции yield. По сути - это пустой вызов, но в скетче вы можете определить эту функцию, написав

void yield()
{
}

и прописав туда нужный код. Естественно, что в приведённом примере внутри функции yield не стоит вызывать serialEvent, иначе - стек попортите рекурсией.

Короче - подходов есть много разных, и в документации они прописаны. Жаль, что никто её не читает :)

sanekru
Offline
Зарегистрирован: 05.12.2017

Доброго дня.

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

void balance ()
{
 String balanceval; String inputstr;
 Serial.println("AT+CUSD=1,#100#,15");
 delay (4000);
while(Serial.available())
  {
   char ch=Serial.read();
   if( ch == '\r' )
    {
     if(inputstr.indexOf(F("+CUSD:"))>-1)
      {
       int p1 = inputstr.indexOf(F("\""));
       int p2 = inputstr.lastIndexOf(F("\""));
       inputstr = inputstr.substring(p1+1,p2);
       String decodestr;
       Decode7bit(inputstr, decodestr);
       balanceval = decodestr;
       sendTextMessage(balanceval);
      }
    }
    else inputstr+=ch; 
  } 
}

в ней после запроса стоит delay на ожидание прихода данных от оператора, и в этот момент МК зависает, вместо того что-бы делать полезную работу.

Так вот вопрос Как оптимизировать функцию что б избавиться от delay?

PS. Есть предположение что её можно разбить путём выноса 


     if(inputstr.indexOf(F("+CUSD:"))>-1)
      {
       int p1 = inputstr.indexOf(F("\""));
       int p2 = inputstr.lastIndexOf(F("\""));
       inputstr = inputstr.substring(p1+1,p2);
       String decodestr;
       Decode7bit(inputstr, decodestr);
       balanceval = decodestr;
       sendTextMessage(balanceval);
      }

в основной блок опроса Serial крутящийся в loop.

Подтвердите моё предположение или есть более грамотное решение?

PS2 Ещё есть ф-ция отправки сообщения

void sendTextMessage(String txt) 
  {
   Serial.print("AT+CMGS="); Serial.print((char)34);
   Serial.print("+7**********"); Serial.print((char)34); //phone number
   Serial.print((char)13);
   delay(100);
   Serial.println(txt);
   delay(100);
   Serial.print((char)26); Serial.print((char)13);
  }

в ней тоже имеется 2 delay (пусть крохотные, но есть) и если их убрать то иногда некорректно отрабатывает (правда было проверенно на SoftwareSerial).

 

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

sanekru пишет:

все ответы в данной теме были плавно переключены не на избавление от delay, а на парсинг из serial

да, именно так.

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

sanekru пишет:

Так вот вопрос Как оптимизировать функцию что б избавиться от delay?

Никак, полностью переписать (см.выше).

вот вам пример перекодировки SMS , обратите внимание идет последовательная обработка входящих данных


void getDecode7bitChar(byte byte1, byte byte0) {
  byte posIdx = smsIdxCurrentChar % 8;
  byte byteOut;
  if (posIdx > 0) {
    byteOut =  byte0 << posIdx;
    byte b2 = byte1 >> (8 - posIdx);
    byteOut = byteOut | b2;
    byteOut = byteOut & 0x7F;
    if (byte1) {
#if (SHOW_SMS == 1)
      Serial.write(byteOut);
#endif
      // find external command
      byte bc = findCmdInBuf(byteOut);
      if (bc) execExtCmd(bc - 1);
    }
    if (posIdx == 6) {
      byteOut =  byte0 >> 1;
#if (SHOW_SMS == 1)
      Serial.write(byteOut);
#endif
      // find external command
      byte bc = findCmdInBuf(byteOut);
      if (bc) execExtCmd(bc - 1);
      ++smsIdxCurrentChar;
    }
  } else {
    byteOut = byte0;
    byteOut = byteOut & 0x7F;
#if (SHOW_SMS == 1)
    Serial.write(byteOut);
#endif
    // find external command
    byte bc = findCmdInBuf(byteOut);
    if (bc) execExtCmd(bc - 1);
  }
  ++smsIdxCurrentChar;
}

 

а вот например отправка СМС, аналогично побайтно берется отправляемая строка и в каждом loop цикле (образно) отправляется уже байтв кодированном виде в модем:


byte smsOutPackedByte() {
  switch (sms_out_pos_mode) {
    case 0: { // SCA
        if (sms_out_pos_other) {
          ++sms_out_pos_mode;
          sms_out_pos_other = 0;
          return '0';
        } else {
          ++sms_out_pos_other;
          return '0';
        }
        break;
      }
    case 1: { // PDU Type
        if (sms_out_pos_other) {
          ++sms_out_pos_mode;
          sms_out_pos_other = 0;
          return '1';
        } else {
          ++sms_out_pos_other;
          return '0';
        }
        break;
      }
    case 2: { // MR
        if (sms_out_pos_other) {
          ++sms_out_pos_mode;
          sms_out_pos_other = 0;
          return '0';
        } else {
          ++sms_out_pos_other;
          return '0';
        }
        break;
      }
    case 3: { // DA
        if (sms_out_pos_other < 2) { // lenght sender
          // lenth to HEX
          if (sms_out_pos_other) {
            ++sms_out_pos_other;
            byte tb = (smsLenSender % 16);
            if (tb > 9) return (tb + 0x37); else return (tb + '0');
          } else {
            ++sms_out_pos_other;
            return ((smsLenSender / 16) + '0');
          }
        } else if (sms_out_pos_other < 4) { // format sender = 91
          if (sms_out_pos_other == 2) {
            ++sms_out_pos_other;
            return '9';
          } else {
            ++sms_out_pos_other;
            return '1';
          }
        } else { // sender
          if (sms_out_pos_sender < smsLenSender) {
            ++sms_out_pos_other; ++sms_out_pos_sender;
            if (bitRead(sms_out_pos_sender, 0)) {
              if ((sms_out_pos_sender >= smsLenSender) && (bitRead(smsLenSender, 0)))
                return ('F'); else
                return (smsSender[sms_out_pos_sender]);
            }  else  {
              return (smsSender[sms_out_pos_sender - 2]);
            }
          } else {
            sms_out_pos_other = 0; ++sms_out_pos_mode;
            if (bitRead(smsLenSender, 0))
              return (smsSender[smsLenSender - 1]);
            else return 0xFF;
          }
        }
        break;
      }
    case 4: { // PID
        if (sms_out_pos_other) {
          ++sms_out_pos_mode;
          sms_out_pos_other = 0;
          return '0';
        } else {
          ++sms_out_pos_other;
          return '0';
        }
        break;
      }
    case 5: { // DCS
        if (sms_out_pos_other) {
          ++sms_out_pos_mode;
          sms_out_pos_other = 0;
          return '8';
        } else {
          ++sms_out_pos_other;
          return '0';
        }
        break;
      }
    case 6: { // UDL
        if (sms_out_pos_other) {
          ++sms_out_pos_mode;
          sms_out_pos_other = 0;
          byte uh = smsUDL % 16;
          if (uh > 9) return (uh + 0x37); else return (uh + '0');
        } else {
          ++sms_out_pos_other;
          byte uh = smsUDL / 16;
          if (uh > 9) return (uh + 0x37); else return (uh + '0');
        }
        break;
      }
    case 7: { // UD
        if (sms_out_pos_string < smsLenString) {
          byte outChar = sms_buf[sms_out_pos_string];
          byte bpos = (sms_out_pos_other % 4);
          switch (bpos) {
            case 0: {
                ++sms_out_pos_other;
                return '0';
                break;
              }
            case 1: {
                ++sms_out_pos_other;
                if (outChar < 0x7F) return '0'; else return '4';
                break;
              }
            case 2: {
                ++sms_out_pos_other;
                if (outChar < 0x7F) {
                  // ascii
                  outChar = (outChar / 16);
                  if (outChar > 9) return (outChar + 0x37); else return (outChar + '0');
                } else {
                  // win1251
                  switch (outChar) {
                    case 168: {
                        return '0'; break;
                      }
                    case 184: {
                        return '5'; break;
                      }
                    default: {
                        outChar -= 0xB0;
                        outChar = (outChar / 16);
                        if (outChar > 9) return (outChar + 0x37); else return (outChar + '0');
                      }
                  }
                }
                break;
              }
            case 3: {
                ++sms_out_pos_other;
                ++sms_out_pos_string;
                if (outChar < 0x7F) {
                  // ascii
                  outChar = (outChar % 16);
                  if (outChar > 9) return (outChar + 0x37); else return (outChar + '0');
                } else {
                  // win1251
                  switch (outChar) {
                    case 168: {
                        return '1'; break;
                      }
                    case 184: {
                        return '1'; break;
                      }
                    default: {
                        outChar -= 0xB0;
                        outChar = (outChar % 16);
                        if (outChar > 9) return (outChar + 0x37); else return (outChar + '0');
                      }
                  }
                }
                break;
              }
            default: {}
          }
        } else {
          ++sms_out_pos_mode;
          sms_out_pos_other = 0;
          return 0; // 0xFF for next step
        }
        break;
      }
    default: {
        return 0;
      };
  }
}

 

sanekru
Offline
Зарегистрирован: 05.12.2017

Спасибо, смысл понятен, буду устраивать себе мозговой штурм.

PS У меня в буфер Serial всё и не помещалось, ну я и пошёл неправильным путём, увеличил размер буфера!

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

sanekru пишет:

 ну я и пошёл неправильным путём, увеличил размер буфера!

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

в идеале размер буфера не должен быть больше одной принимаемой команды.

и да, старайтесь не использовать тип String, привыкайте к массиву char.

вот немного кривой но рабочий пример, буфер 24 байта

http://arduino.ru/forum/apparatnye-voprosy/vse-o-sim800l-i-vse-chto-s-ni...

 

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

sanekru пишет:

Ветка конечно заброшенная, но ответ на неё мне кажется актуальным и для меня не найденным, все ответы в данной теме были плавно переключены не на избавление от delay, а на парсинг из serial, а судя по названию темы и вопросу создателя в первом сообщении, как правильно опрашивать gsm модем без использования delay, 

КАк известно, правильно сформулированный вопрос - это более половины ответа. 

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

И именно поэтому более опытные участники форума "плано переключают" обсуждение темы с неправильно заданного вопроса на тот, который следовало бы задать как правильный.

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