Помогите новичку дописать код.

leshak
Offline
Зарегистрирован: 29.09.2011

OK. Строку мы научились "собирать в буффер". Причем что-бы она была "настоящей сишной строкой". А значит - теперь нам доступны стандартный функции для работы со строками. Гуглим "C/C++ строковые функции" и пытаемся найти "подходящую нам". Будем учится работать с уже "принятыми строками".

Ну и ознакамливаемся "какие еще есть"... что-бы когда понадобится - примерно знать что искать (нет конечно гарантии что все они реализованы в arduino, для экономии места, но... всегда можно "взять и попробовать", по крайней мере "базовые" - доступны)

Ой... я же забыл сказать что мы хотим сделать :) Как же без этого искать "подходящую функцию" :)

А хотим мы вот что. Что-бы нам оно не на каждую строку кричало "Echo from arduino", а скажем "Command detected", но только на строку вида "cmd".

То есть, если я послал в сериал "cmd" - оно сказало что-то, любые другие строки - игнорировало молча.

Поможет нам в этом функция strcmp() . На сайте arduino.ru - в документации ее описание не найдете. Так что - прийдется нагуглить в любом справочнике по C/C++  . Там правда может быть сказано, что для того что-бы она была доступна нужно подключить библиотеку "#include <string.h>" - не обращайте внимание. ArduinoIDE уже подключает эту библиотеку ко всем скетчам. Самому ничего делать не нужно. Можно сразу пользоваться strcmp()

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

На всякий случай, функцию isLineReady() мы уже не меняем. Она уже и так "достаточно хороша". Читает строки в буффер - пусть только этим и знанимается. Меняем код в loop(). Который работает с "уже собранной строкой".

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

Предлагаю, как только новичек закончит свой первый проект, будем выдавать ему диплом об окончании :) Установленого образца! Ну и это нужно начинать принимать пожертвования на поддержание онлайн университета :)

Life23
Offline
Зарегистрирован: 10.08.2013

хм.. Пожертвование - это святое! куда ж без этого!? ;))

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

Life23 пишет:

хм.. Пожертвование - это святое! куда ж без этого!? ;))

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

Life23
Offline
Зарегистрирован: 10.08.2013

Ну о том, что я отделаюсь просто словами благодарности - я даже не думал ))

по поводу последнего задания:

void loop() {

  if(isLineReady()){ // есть готовая строка?
      if(strcmp(input_buff, "cmd")==0){
      GPRS.print("Command OK");
      }
  }
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

>Ну о том, что я отделаюсь просто словами благодарности - я даже не думал ))

Давайте мы пока будет думать про функции strcmp() и strstr()

Puhlyaviy известный специалист по "уводу темы в сторону" :)  (я вообщем-то и сам не без этого греха ;), но....)

Вообщем давате пока подумаем как сделать что-бы "Command OK" (кстати удобней ее было-бы с помощью println выводить) выводилось, не только когда строка в точности равна "cmd", а когда комбинация "cmd" встречалась где угодно внутри строки. Вообщем что-бы детекст срабатывал на строки типа:

"aaacmdbbb"
"aaaa cmd bbb cmd"
"cmdaaabbbcmd"

Но при этом молча на строки вида "aaa cm d bbb", "c m d" и т.п не вызвали сработки. Вообщем игнорировал все строки где нет подряд букв c,m,d 

leshak
Offline
Зарегистрирован: 29.09.2011

Подсказка: тут вам нужно вспомнить как я рассказывал, что если мы в if(...) пиханем что-то не булевское, если вместо многоточия там будет что-то что возвращет что-то отличное от true/false (1/0), то это "что-то" будет интерпретироваться как "равно оно нулю или нет".

Вообщем код

if(ЧТО-ТО)

равносилен

if(ЧТО-ТО!=0)

поэтому не нужно пугатся слов "функция возвращает указатель". Вам пока достаточно проверять "вернула она что-то отличное от нуля или нет". А то что "что-то" это указатель - пока нам не важно.

Life23
Offline
Зарегистрирован: 10.08.2013

Вы меня опередили )

void loop() {

  if(isLineReady()){ // есть готовая строка?
      if(strstr(input_buff, "cmd")){
      GPRS.println("Command OK");
  }
  }
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

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

То есть

"cmdaaa" - должно сработать
"aaacmd" - не должно.

"Готовой фунции" для такой проверки - у нас нет. Но, написать свою - тоже отностиельно не сложно.

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

Но... давайте пока с указателями попробуем. Напишем функцию startsWith(char* inputString,char* prefix), которая будет возвращать является true если input начинается на prefix

bool startsWith (char* inputString, char* prefix) {
    return (strstr(inputString, prefix) - inputString) == 0;
}

inputString - где искать
prefix - что искать (искомая подстрока)

Чуть-чуть пояснение. strstr - вернуло нам указатель "где оно нашло искомую подстроку". Теперь нам нужно сравнить этот указатель, с указателем на строку "где искать". Если они совпадают - значит оно нашло "в самом начале строки где-искать". Ну и выясняем мы "совпалили указатели" самым тупым способом. Отняли один указатель от другого. Если в итоге разница ноль - значит указатели совпадали.

И еще одну поправочку нужно сделать. Помните когда мы писали "защиту от переполнения буфера", мы "теряли часть строки" (если буфер переполнился). Причем тогда - нам было не важно что теряется "начало строки" или "ее хвост".

Но теперь, раз мы будем проверять "начинается ли строка с букв cmd" - нам предпочтительеней, при переполнении "терять хвост".
Что бы строки типа
"cmdaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - тоже срабатывали.
Тогда мы можем "делать буффер поменьше" при этом детект будет работать даже на длинный строках корректно.

Найдите, где-то выше по ветке я давал пример как "терять хвост строки". Там было что-то типа "это выглядело бы так". Подправте isLineReady() под вновь открывшиеся обстоятельства.

leshak
Offline
Зарегистрирован: 29.09.2011

Кстати, в качестве "внекласного задания", что-бы освоится лучше с массивами, указателями и т.п. можете попробовать самостоятельно реализовать startWith еще несколькими способами. Скажем проходит с помощью for() или while() по каждому байту строки и сравнивать (пока не упремся в символ нуля).

Или с помощью strlen() сравнить длины строк. Если inputString короче - ну значит она явно не начинается на prefix :)

А вот если длиней - тогда их можно сравнить с помощью функции strncmp(). Которая подобна strcmp() только сравнивает не целиком две строки, а только нужное нам количество символов.

или можно написать еще одну функцию indexOf() которая будет возвращать "индекс" найденной подстроки. И -1 если не нашло

(ее реализация - будет очень похожа на startsWith из прошлого поста. А сам startWith() примет вид


bool startsWith (char* inputString, char* prefix) {
    return indexOf(inputString,prefix)==0;
}

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

haplen
Offline
Зарегистрирован: 23.08.2013

Подскажите пожалуйста, что неправильно в объявлении массива функций:

 void (massive[]) (void)={st, raz, stab, rez}; // массив указателей функций

Компилятор ругается: declaration of 'massive' as array of functions

haplen
Offline
Зарегистрирован: 23.08.2013

Вопрос снят. Забыл *.

Life23
Offline
Зарегистрирован: 10.08.2013
#define GPRS Serial

#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из GPRS данных


void setup(){
  GPRS.begin(9600);
}

void loop() {

  if(isLineReady()){ // есть готовая строка?
      if(startsWith(input_buff,"cmd")){
      GPRS.println("Command OK");
      }
  }
}

bool isLineReady(){
  static byte inputBuffIndex=0;
  if (GPRS.available()){
    byte t = GPRS.read();
      switch(t){
	case '\n':
	input_buff[inputBuffIndex] = 0; 
        inputBuffIndex=0; 
        return true; 
	break;
	case '\r':
        break;
         default:
          if (inputBuffIndex < INPUT_BUF_SIZE){
          input_buff[inputBuffIndex] = t; 
          inputBuffIndex++;
         }  
      }
  }
  return false;
}

bool startsWith (char* inputString, char* prefix) {
    return (strstr(inputString, prefix) - inputString) == 0;
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Замечательно. Еще и выделение строк в коде освоили :)

Усложняем задачу. Теперь нам нужно, что-бы увидив cmd оно не сразу "кричало об этом". А интерпретировало это примерно так:

1. Если строка начинается на "cmd" - нужно ЗАПОМНИТЬ это. Так как СЛЕДУЮЩАЯ строка - будет самой командой.
2. Выводило в Serial эту СЛЕДУЮЩУЮ строку типа "вот мы получили такую комманду".

Вообщем:

1. Игнорируем все, пока не прийдется строка начинающая на cmd
2. Выводим в Serial монитор строку прешедшую следующей после cmd.
3. И опять пункт 1

p.s. nextLineWillCommand - следующая строка будет коммандой.

Life23
Offline
Зарегистрирован: 10.08.2013

не совсем понял задания.. (

leshak
Offline
Зарегистрирован: 29.09.2011

Смотрите, сейчас у нас работает по логике

"строка начинающаяся на cmd" - это команда.

А нужна логика

"строка которая идет ПОСЛЕ строки начинающейся на cmd" - это команда.

Вообщем что-бы в терминале, я мог набрать что-то такое (кажду строку набираю отдельно и нажимаю кнопку <send> в конце). 

some command<send>
(ардуина молчит)

cmdblabla<send>
(ардуино молчит, но уже приготовилась к приему команды)

some command<send>
Ардуина:  Yo--ho!!!... Command 'some command'  received

some command<send>
(ардуино молчит, так как никто не предупрелидл что это команда)

some command<send>
(ардуино молчит, так как никто...)

cmd<send>
(ардуино молчит, но уже приготовилась к приему команды)

other command<send>
Ардуина:  Yo--ho!!!... Command 'other command'  received

Зачем мы это делаем? А вот что-бы SMS-ски принимать. Как шилд нам отдает SMS-ски? Вначале присылает строку начинающуюся на "+CMT". Типа "ой.. мы тут SMS получили, ща буду диктовать ее". А потом, следующей строкой, отдает нам сам текст SMS-ски.

Так и тут. Мы вначале ждем строку начинающуюся на "cmd", а следующую строку интерпретируем как "команду" (кричим, выводим ее, еще что-то).

 

leshak
Offline
Зарегистрирован: 29.09.2011

ну что, задача понятна?

Life23
Offline
Зарегистрирован: 10.08.2013

да, понятна. попробую теперь в коде это сделать.

Life23
Offline
Зарегистрирован: 10.08.2013
#define GPRS Serial

#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из GPRS данных
boolean nextLineWillCommand = false;

void setup(){
  GPRS.begin(9600);
}

void loop() {
  if(isLineReady()){ // есть готовая строка?
    if(nextLineWillCommand){
    GPRS.println("Yo--ho!!!");
    nextLineWillCommand = false;
    }
      if(startsWith(input_buff,"cmd")){
      nextLineWillCommand = true;
      }
  }
}

bool isLineReady(){
  static byte inputBuffIndex=0;
  if (GPRS.available()){
    byte t = GPRS.read();
      switch(t){
	case '\n':
	input_buff[inputBuffIndex] = 0; 
        inputBuffIndex=0; 
        return true; 
	break;
	case '\r':
        break;
         default:
          if (inputBuffIndex < INPUT_BUF_SIZE){
          input_buff[inputBuffIndex] = t; 
          inputBuffIndex++;
         }  
      }
  }
  return false;
}

bool startsWith (char* inputString, char* prefix) {
    return (strstr(inputString, prefix) - inputString) == 0;
}

правильно я мыслю?

Клапауций
Offline
Зарегистрирован: 10.02.2013

Puhlyaviy пишет:

Предлагаю, как только новичек закончит свой первый проект, будем выдавать ему диплом об окончании :) Установленого образца! Ну и это нужно начинать принимать пожертвования на поддержание онлайн университета :)

Предлагаю диплом выдавать посмертно, имущество экспроприировать в пользу детей Лешака.

Life23
Offline
Зарегистрирован: 10.08.2013

я бы на вашем месте не шутил таким "черным юмором".. как то не уместно.. 

тем более, что Вы наверно не прочитали мой ответ на даный пост..

Клапауций
Offline
Зарегистрирован: 10.02.2013

Life23 пишет:

я бы на вашем месте не шутил таким "черным юмором".. как то не уместно.. 

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

пост прочитал - университет сжечь.

Life23
Offline
Зарегистрирован: 10.08.2013

идите Вы в Отвлеченные темы со своим остроумием и философией...

 

 

Клапауций
Offline
Зарегистрирован: 10.02.2013

Life23 пишет:

идите Вы в Отвлеченные темы со своим остроумием и философией...

не хочу я ни вкакие отвлечённые темы идти со своим всем...

 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

я бы на вашем месте не шутил таким "черным юмором".. как то не уместно.. 

тем более, что Вы наверно не прочитали мой ответ на даный пост..

Да ладно вам :)  Ведь небыло оговорено "чье посмертно" и "чье имущество". Вспоминайте Ходжу Насредина.

"Неизвестно кто к тому времени сдохнет. То ли Шах, то ли ишак, то ли Калапуций. Пойди потом разберись кто из них лучше знал богословие" :)

А вообще, я удивлен столь позним появлением Калапуция в этой ветке (первый ожидаемый - уже отметился).

Но вынужден отметить две вещи:

1. Несмотря на весь сарказм и желание "увести в сторону моря" - оба иногда говорят весьма дельные/полезные вещи, пусть и не всегда в дружелюбной форме (кстати я тоже часто так поступаю, не всегда я такой белый и пушистый).
2. Конечно я не могу говорить за других, это не более чем мое "впечатление". Но по сравнению с тем как обычно оба высказываются - в вашем случае это было, относительно конечно, "очень мягко и дружелюбно". Мне кажется, в какой-то мере это проявление уважения к вашим усилиям.

Вообщем IMHO это скорее было "дружеское подтрунивание" чем "наезд и троллинг". Нужно отдать должное - оба "сдерживались из последних сил". И явно не потому что "Не заметили нас". Уже за 200 сообщений ветка :)

 

leshak
Offline
Зарегистрирован: 29.09.2011

>правильно я мыслю?

Ну во первых... нужно и самому привыкать искать проблемы. Не всегда же я буду под рукой :)

Прогоните "текстовые данные" из #217 поста. Ведет себя ардуина как в "диалоге-примере" или есть отличие?  Глядя в Serial вы сможете понять какую команду приняла ардуина?

Во вторых. Пытайтесь стать мысленно на место "хакера/тестера". Представте что вам нужно "сломать" скетч. Заставить его повести как-то "не в соотвествии с изначальным заданием". Попробуйте найти какие-то "граничные комбинации входящих данных" которые "снесут ему мозг". Слишком длинные строки, слишком короткие (пустые). Или, к примеру "а если в сам текст команды запихнуть "признак команды"?

Вот к примеру есл послать такие три строки

cmd
cmd
some command

1 - признак команды, 2 - сама команда, 3 - строка которая должа быть проигнорирована (так как прошлая строка "тело команды", а не "признак команды"). Ваш скетч именно так "поймет" такую последовательностб строк? 

leshak
Offline
Зарегистрирован: 29.09.2011

И давайте применим еще одну "хитрость". Сделаем для Serial не один, а два псевдонима

#define GRPS Serial
#define DBG Serial

DBG - это сокращение от DEBUG - отладка.

Перым - будет пользоваться в коде который должен, в итоге, работать с настоящим GPRS приемником, а вторым (DBG.println) для вывода всякой отладчной информации. Все эти "Yo-hoo", сообщений какая команда получена и проч - нам же не нужно, что-бы потом они случайно в gprs-шилд полезли. Понятно что в "финальном скетче" их можно выкинуть, но... всегда можно где-то случайно забыть/оставить, во вторых, даже с реальным железом иногда хочется видеть "что происходит". Тоже нужна отладка.

Тогда в финальном скетче мы просто заменим "#define GPRS SoftwareSerial(...)" а "#define DBG.... " оставим как есть. И весь вывод у нас "автоматически" разделитися. Отладочная инфа - пойдет в сериал монитор, а "AT-команды" - шилду :)

А когда скетч будет "ну совсем-совсем финальным". И мы, все-таки, захотим убрать всю эту отладку с концами, нам будет достаточно выкинуть #define DBG и дальше компилятор нам сам подскажет, где нужно убрать DBG.println и т.п. - так мы точно "не оставим за собой строительного мусора" :)

 

Life23
Offline
Зарегистрирован: 10.08.2013
void loop() {
  if(isLineReady()){ // есть готовая строка?
     if(nextLineWillCommand){       
     GPRS.print("Yo--ho!!! received command:");
     GPRS.println(input_buff);
     nextLineWillCommand = false;
     }
      else if(startsWith(input_buff,"cmd")){
      nextLineWillCommand = true;
      }
  }
}

вот так?.. проверил - вроде бы то что нужно..

leshak
Offline
Зарегистрирован: 29.09.2011

Э... схитрили :) Чуть упростили себе жизнь.  В задании было

Yo--ho!!!... Command 'some command'  received

А у вас вышло 

Yo-ho!!! received command:some command

А если бы не человек был с другой стороны, а то же какая-то парсилка которая ждет именно такого формата как было в задании?

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

"some command     ". В вашем выводе - эти пробелы разглядеть не получится. У меня сразу будет видно "..... Command 'some command     '" - я себе съекономлю час жизни, если потом эта команда будет с чем-нибудь сравниваться. Сразу глазами увижу.

"Хитрость" из #228 - тоже пропустили. Когда мы переключимся на настоящий gprs, он обладеет от этих "Yo-hoo".

Еще. Вместо

 else if(startsWith(input_buff,"cmd")){

можно было сделать

if(!nextLineWillCommand && startWith(input_buff,"cmd"))

Но, в принципе и как вы сделали "не хуже". Просто можно и так и этак. Хотя.... лично мне мой вариант больше нравится :) Как-то куски более независимы. Не нужно просматривать предыдущий if что-бы понять когда в эту ветку попадает. Да и... переставить местами их можно будет. Как-то легче думается когда порядок проверок в коде совпадает с тем порядком в котором строки приходят. Наглядней, что-ли

 

void loop() {
  if(isLineReady()){ // есть готовая строка?
  
    if(!nextLineWillCommand && startsWith(input_buff,"cmd")){
      nextLineWillCommand = true;
      return;// сразу выходим из loop(), что-бы следующий if не сработал
    }
  
    if(nextLineWillCommand){       
      DBG.print("Yo--ho!!! Command '");
      DBG.print(input_buff);
      DBG.println("' received");
      nextLineWillCommand = false;
    }
    
  }
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Далее, мы опять видим что у нас функция loop() начала "разрастся", делаем в ней "вообще разные вещи". И сами команды "детектим" и обработку команды выполняем (пока только выводим принятую команду в Serial, но ведь дальше больше будет). 

Вынесем "работу с принятой командой" в отдельную функцию. Ну скажем ... executeCommand ее назовем

void loop() {
  if(isLineReady()){ // есть готовая строка?
  
    if(!nextLineWillCommand && startsWith(input_buff,"cmd")){
      nextLineWillCommand = true;
      return; // сразу выходим
    }
  
    if(nextLineWillCommand){       
      executeCommand(input_buff); // осполняем принятую  команду
      nextLineWillCommand = false;
    }
    
  }
}

void executeCommand(char* command){
    // пока все "исполнение комманды" ограничено ее выводом в Log
    DBG.print("Yo--ho!!! Command '");
    DBG.print(command);
    DBG.println("' received");
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Кстати, обратите внимание. Что внутри executeCommand я использую не глобальную переменную input_buff, а передал ее внутрь как параметр функции. Функция стала "более независима, более обособлена". Если мне нужно будет отладить именно ее поведение, то я могу не набирать в Serial команды, а вынести ее в отдельный скетч и написать что-то типа

void setup(){
   DBG.begin(9600);

  DBG.println("Start tests");
   
  executeCommand("some command");
  delay(1000);
  executeCommand("other command");

  DBG.println("All tests done");
}

То есть - ей уже "пофиг" откуда взялась комманда.

leshak
Offline
Зарегистрирован: 29.09.2011

OK.. Вообщем осталось "почти чуть-чуть" :)

В executeCommand, кроме вывода в DBG,  с помощью if else if else и т.п. проверять чему же равна пришедшая комманда.

Скажем на "led on" - включать светик, на "led off" - выключать.

Строки вы уже умеете сравнивать. Только проверяйте не input_buff, а именно параметр функции command

И да... сделайте nextLineWillСommand - локальной переменной (естественно с ключевым слово static) - мы же уже умеем не засорять глобальное простраство имен. Только в крайнем случае там что-нибудь заводим.

Life23
Offline
Зарегистрирован: 10.08.2013

leshak пишет:

Еще. Вместо

 else if(startsWith(input_buff,"cmd")){

можно было сделать

if(!nextLineWillCommand && startWith(input_buff,"cmd"))

Для Вас то это проще :) но мне что бы понимать, лечше расписать белее "извращенно" ))

Хотя изначально пытался сделать так как "можно было сделать" - но запутался.. 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

Для Вас то это проще :) но мне что бы понимать, лечше расписать белее "извращенно" ))

Скорее всего "сразу выйти" - забыли (сам два раза забывал).

Ну я - вроде показал "как проще".

А... проверять несколько условий в if, пользоватся операторами && и || -тоже нужно учится. И писать и "читать" такой код.

leshak
Offline
Зарегистрирован: 29.09.2011

Прежде чем начинать светик мучать - слепите #231 целиком. Дайте сюда.  А то я "из головы писал", мог где-то очепятнутся. А так как "Это кусок", то даже на компилируемость не проверил.

Life23
Offline
Зарегистрирован: 10.08.2013
#define GPRS Serial
#define DBG Serial

#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из GPRS данных
boolean nextLineWillCommand = false;

void setup(){
  GPRS.begin(9600);
}

void loop() {
  if(isLineReady()){ // есть готовая строка?
     if(!nextLineWillCommand && startsWith(input_buff,"cmd")){
      nextLineWillCommand = true;
      return; // сразу выходим
    }
      if(nextLineWillCommand){       
      executeCommand(input_buff); // осполняем принятую  команду
      nextLineWillCommand = false;
    }
  }
}

bool isLineReady(){
  static byte inputBuffIndex=0;
  if (GPRS.available()){
    byte t = GPRS.read();
      switch(t){
	case '\n':
	input_buff[inputBuffIndex] = 0; 
        inputBuffIndex=0; 
        return true; 
	break;
	case '\r':
        break;
         default:
          if (inputBuffIndex < INPUT_BUF_SIZE){
          input_buff[inputBuffIndex] = t; 
          inputBuffIndex++;
         }  
      }
  }
  return false;
}

bool startsWith (char* inputString, char* prefix) {
    return (strstr(inputString, prefix) - inputString) == 0;
}

void executeCommand(char* command){
    // пока все "исполнение комманды" ограничено ее выводом в Log
    DBG.print("Yo--ho!!! Command '");
    DBG.print(command);
    DBG.println("' received");
}

Вот код. Все работает. Даже не смотря на то что Вы писали "с головы"! :)

leshak
Offline
Зарегистрирован: 29.09.2011

boolean nextLineWillCommand = false; - так и оставили глобальной :(

Традиционно "форматирование":

Строка 34-мь - break уехал влево.
Строка 37-мь - default уехал вправо (должен быть под case).

А так... здорово :)  

Осталось только в executeCommand реазовать управление светиком в зависимости то того какая command пришла.

Life23
Offline
Зарегистрирован: 10.08.2013

АААаа.. )) ну не все сразу.. )) исправлю )

Life23
Offline
Зарегистрирован: 10.08.2013
#define GPRS Serial
#define DBG Serial
#define LOAD_PIN 6

#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из GPRS данных

void setup(){
  pinMode(LOAD_PIN,OUTPUT);
  GPRS.begin(9600);
}

void loop() {
  static bool nextLineWillCommand = false;
  if(isLineReady()){ // есть готовая строка?
     if(!nextLineWillCommand && startsWith(input_buff,"cmd")){
      nextLineWillCommand = true;
      return; // сразу выходим
    }
      if(nextLineWillCommand){       
      executeCommand(input_buff); // осполняем принятую  команду
      nextLineWillCommand = false;
    }
  }
}

bool isLineReady(){
  static byte inputBuffIndex=0;
  if (GPRS.available()){
    byte t = GPRS.read();
      switch(t){
	case '\n':
	  input_buff[inputBuffIndex] = 0; 
          inputBuffIndex=0; 
          return true; 
	  break;
	case '\r':
          break;
        default:
          if (inputBuffIndex < INPUT_BUF_SIZE){
          input_buff[inputBuffIndex] = t; 
          inputBuffIndex++;
         }  
      }
  }
  return false;
}

bool startsWith (char* inputString, char* prefix) {
    return (strstr(inputString, prefix) - inputString) == 0;
}

void executeCommand(char* command){
    DBG.print("Yo--ho!!! Command '");
    DBG.print(command);
    DBG.println("' received");
    if(strstr(command, "load on")){
      digitalWrite(LOAD_PIN,HIGH); // включили светодиод
    }
      else if(strstr(command, "load off" )){
        digitalWrite(LOAD_PIN,LOW);
      }
}

копирую с Arduino IDE - уезжает у меня форматирование после вставки в форум :(

может это из за того что еще работаю с "Sublime Text2", так что не ругайте по поводу "форматирования". (

static bool nextLineWillCommand = false;

Вставил в Loop. Не могу сообразить правильно, то что вписал его сюда...(

 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

копирую с Arduino IDE - уезжает у меня форматирование после вставки в форум :(

Есть подозрение что это из-за того что вы для "двигания" используете пробелы. Попробуйте клавишу "Tab" для этого.

А еще, в ArduinoIDE есть волшебная кнопка Ctrl-T  (но я обычно ей пользуюсь только когда беру "чужой код плохо форматированны". Свой - предпочитаю сам с помощью TAB и Shift-TAB двигать. Кстати можно выделить и "двигать" сразу несколько строк.

 

Life23 пишет:

static bool nextLineWillCommand = false;

Вставил в Loop. Не могу сообразить правильно, то что вписал его сюда...(

Не понял в чем проблема. В коде вроде нормально все вставлено. Да и "делали уже такое не раз". Вон inputBuffIndex вставлен так же. Или вас смущало что это именно loop()?  Так с точки зрения синтаксиса - она ничем не отличается. Это такая же функция как isLineReady(), executeCommand() и т.п.

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

И еще. Ну неужели не хочется между строками 56 и 57 пустую строку (или две) вставить? Что-бы визуально отделить "вывод в лог" от "бизнес логики"? Вы же не на пергаменте пишите. Не нужно быть "как Ленин" (он там тоже на осьмушках указы писал, вроде,- бумагу экономил :)

leshak
Offline
Зарегистрирован: 29.09.2011

Ну что, работает? Если да то "пора готовится к слиянию" :)

А для этого нам нужно, как мы помним. Сделать "минимально возможными" setup() и loop(). С setup() у нас и так все "терпимо". А вот loop() - немного "великоват".

Решаем это все, как и в прошлый раз - выносим из него все в отдельную функцию. Скажем waitAndExecuteCommand(). И вызываем из loop() уже ее.

Еще можно, для удобства, вытащить в глобальные переменные (в наш конфиг) строки означающие "щас будет команда" и сам текст комманд. Что-бы потом не лазить по коду, не искать "где же оно", если мы захотим, скажем, сменить текст команды "load on", на предположим "rele on". 

Или, даже если не нужно менять. Удобно же глянуть в заголовок скетча и сразу понять "какими командами он управляет". Чем искать их разбросанными по скетчу.

//*************  SMS - команды  ************************
char* cmd_prefix="cmd"; // начало строки идущей перед "телом sms команды"
char* cmd_on="load on"; // sms команда включения 
char* cmd_off="load off"; // команда выключения
//*************  /SMS - команды  ************************


void setup(){
  .....
}
loop(){
  waitAndExecuteCommand();
}

 

 

Life23
Offline
Зарегистрирован: 10.08.2013
#define GPRS Serial
#define DBG Serial
#define LOAD_PIN 6

#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из GPRS данных

//************* Входящие SMS - команды  ************************
char* cmd_prefix="cmd";
char* cmd_on="load on";
char* cmd_off="load off";
//************* /Входящие SMS - команда  ***********************


void setup(){
  pinMode(LOAD_PIN,OUTPUT);
  GPRS.begin(9600);
}

void loop() {
  waitAndExecuteCommand();
}

void waitAndExecuteCommand(){
  static bool nextLineWillCommand = false;
  if(isLineReady()){ // есть готовая строка?
    if(!nextLineWillCommand && startsWith(input_buff,cmd_prefix)){
      nextLineWillCommand = true;
      return; // сразу выходим
    }
    if(nextLineWillCommand){       
      executeCommand(input_buff); // осполняем принятую  команду
      nextLineWillCommand = false;
    }
  }
}

bool isLineReady(){
  static byte inputBuffIndex=0;
  if (GPRS.available()){
    byte t = GPRS.read();
    switch(t){
    case '\n':
      input_buff[inputBuffIndex] = 0; 
      inputBuffIndex=0; 
      return true; 
      break;
    case '\r':
      break;
    default:
      if (inputBuffIndex < INPUT_BUF_SIZE){
        input_buff[inputBuffIndex] = t; 
        inputBuffIndex++;
      }  
    }
  }
  return false;
}

bool startsWith (char* inputString, char* prefix) {
  return (strstr(inputString, prefix) - inputString) == 0;
}

void executeCommand(char* command){

  // ******** Debag Log ***********************
  DBG.print("Yo--ho!!! Command '");
  DBG.print(command);
  DBG.println("' received");
  // ******** /Debag Log **********************

  if(strstr(command, cmd_on)){
    digitalWrite(LOAD_PIN,HIGH); // включили светодиод
  }
  else if(strstr(command, cmd_off)){
    digitalWrite(LOAD_PIN,LOW);
  }
}

все красиво работает :)

leshak
Offline
Зарегистрирован: 29.09.2011

Можно еще вещи типа digitalWrite(LOAD_PIN,HIGH);

повыносить в отдельный функции типа action_LoadON(); и action_LoadOFF(); а в них, еще, пихануть дополнительные DBG.print (типа "включили/выключили") Да еще и millis() выводить что-бы в логе было видно "когда включили". Но это уже "со шлюхами и блек-джеком".

Но это уже - как хотите. Я обычно - делаю именно  так. Ну хотя-бы потому, что если скетч пишу удаленному клиенту, то хочу видеть "полную картину" что происходит. Физического же диода я не вижу ;) А "полагатся на слова" - это не всегда удобно :)

В вашем случае - это может быть удобно что-бы "выложить лог на форум". Сразу видно "что там происходит" :)

Вообщем - решать вам. Делать ли скетч более "многословным", зато  "удобней в отладке" или оставить "как есть".

leshak
Offline
Зарегистрирован: 29.09.2011

После того как вы примете решение по #244, вам нужно еще одно решение принять.

Что мы дальше будем делать :)

В принципе можно занятся двумя вещами:

1. Начать уже объединять скетчи в один
2. Еще чуть-чуть помучать этот. Поднять уровень его "дуракоупорности". Сделать его еще более "непробиваемым" (и этим более удобным конечному потребителю).

 

Life23
Offline
Зарегистрирован: 10.08.2013

добавил еще:

void actionLoadOn(){
  DBG.println("Load On");
  digitalWrite(LOAD_PIN,HIGH);
}
void actionLoadOff(){
  DBG.println("Load Off");
  digitalWrite(LOAD_PIN,LOW);
}

с millis() - я думаю в моем случае не стоит.. у меня есть светик и я могу проверить. 

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

Склоняюсь к тому, что бы "Еще чуть-чуть помучать этот." для надежности и практики для себя.

leshak
Offline
Зарегистрирован: 29.09.2011

OK. Для себя я бы сделал три вещи.

1. Что-бы все логика была регистро-независимая. Что есть что-бы срабатыловало как "led on", так и "LED ON", так и "lEd oN" и т.п.
2. Взял-бы даташит на gprs и почитал, а не сохраняются ли у него sms-ски в памяти? Если сохраняются - что происходит когда переполняется?  Если после этого перестает принимать новые - значит после обработки каждой sms-ски, нужно слать команду на ее удаление.
3. Посмотрел-бы как выглядит целиком строки которые присылает модем при получении SMS-ски. Подозреваю что там будет и номер с которого пришла SMS-ска. Сделал бы проверку, что-бы выполнялись команды только с определенного номера.

leshak
Offline
Зарегистрирован: 29.09.2011

Но... с точки зрения "феншуя разработки" решение "Еще чуть-чуть помучать этот" - это не правильный выбор.

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

(вот как сами части мы писали по отдельности. вначале сделали "что-то минимальное", а потом постепенно усложняли).

А то вдруг у нас при совмещении выяснится что "нужно чуток переделать, мешают они друг другу". Если каждый содержит "по одной фиче" - нужно будет переделать одну фичу. А если их 20-ть, то все 20-ть править :) Или вдруг мы столько насобачим "плюшек", что потом для совмещения скетчей уже памяти не хватит :(  Обидно об этом будет узнать уже после написания 100 страниц кода :)

Вообщем я бы предложил, вам, все-таки "запустить все вместе", выполнить "програму минимум", а потом жать ее до "максимума" (реализовыать то что описано в #246).

Life23
Offline
Зарегистрирован: 10.08.2013

1. Что-бы все логика была регистро-независимая. Что есть что-бы срабатыловало как "led on", так и "LED ON", так и "lEd oN" и т.п.

Использовать strlwr?

2. Взял-бы даташит на gprs и почитал, а не сохраняются ли у него sms-ски в памяти? Если сохраняются - что происходит когда переполняется?  Если после этого перестает принимать новые - значит после обработки каждой sms-ски, нужно слать команду на ее удаление.

Да, сохраняет. проверенно на практике. Надо удалять. команда AT+CMGD=1,4.

3. Посмотрел-бы как выглядит целиком строки которые присылает модем при получении SMS-ски.

//***** запуск шилда *******
RDY

+CFUN: 1

+CPIN: READY

Call Ready
//***** /Запуск шилда *******


+CMT: "+3809*******7","","13/09/05,17:00:50+12" //данные о том что пришло смс
load on  //тело смс

 

Life23
Offline
Зарегистрирован: 10.08.2013

вот что у меня получилось:

#define GPRS Serial
#define DBG Serial
#define LOAD_PIN 6
#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из GPRS данных

//************* Входящие SMS - команды  ************************
char* cmd_prefix="cmd";
char* cmd_numTel="+380"; //Номер телефона на который реагируем
char* cmd_on="load on";
char* cmd_off="load off";
//************* /Входящие SMS - команда  ***********************

void setup(){
  pinMode(LOAD_PIN,OUTPUT);
  GPRS.begin(9600);
}

void loop() {
  waitAndExecuteCommand();
}

void waitAndExecuteCommand(){
  static bool nextLineWillCommand = false;
  if(isLineReady()){ 
    strlwr(input_buff); //Когда есть готовая строка, переводим символы в нижний регистр. 
    if(!nextLineWillCommand && startsWith(input_buff,cmd_prefix) && strstr(input_buff,cmd_numTel)){ //добавил strstr(input_buff,cmd_numTel) для проверки номера.
      nextLineWillCommand = true;
      return; // сразу выходим
    }
    if(nextLineWillCommand){       
      executeCommand(input_buff); 
      nextLineWillCommand = false;
    }
  }
}

bool isLineReady(){
  static byte inputBuffIndex=0;
  if (GPRS.available()){
    byte t = GPRS.read();
    switch(t){
    case '\n':
      input_buff[inputBuffIndex] = 0; 
      inputBuffIndex=0;
      return true; 
      break;
    case '\r':
      break;
    default:
      if (inputBuffIndex < INPUT_BUF_SIZE){
        input_buff[inputBuffIndex] = t; 
        inputBuffIndex++;
      }  
    }
  }
  return false;
}

bool startsWith (char* inputString, char* prefix) {
  return (strstr(inputString, prefix) - inputString) == 0;
}

void executeCommand(char* command){
  // ******** Debag Logo ***********************
  DBG.print("Yo--ho!!! Command '");
  DBG.print(command);
  DBG.println("' received");
  // ******** /Debag Logo **********************
  if(strstr(command, cmd_on)){
    actionLoadOn();
  }
  else if(strstr(command, cmd_off)){
    actionLoadOff();
  }
}

void actionLoadOn(){
  DBG.println("Load On");
  digitalWrite(LOAD_PIN,HIGH);
  GPRS.println("AT+CMGD=1,4"); //Удаление всех входящих сообщений.
}

void actionLoadOff(){
  DBG.println("Load Off");
  digitalWrite(LOAD_PIN,LOW);
  GPRS.println("AT+CMGD=1,4"); //Удаление всех входящих сообщений.
}