OK. Строку мы научились "собирать в буффер". Причем что-бы она была "настоящей сишной строкой". А значит - теперь нам доступны стандартный функции для работы со строками. Гуглим "C/C++ строковые функции" и пытаемся найти "подходящую нам". Будем учится работать с уже "принятыми строками".
Ну и ознакамливаемся "какие еще есть"... что-бы когда понадобится - примерно знать что искать (нет конечно гарантии что все они реализованы в arduino, для экономии места, но... всегда можно "взять и попробовать", по крайней мере "базовые" - доступны)
Ой... я же забыл сказать что мы хотим сделать :) Как же без этого искать "подходящую функцию" :)
А хотим мы вот что. Что-бы нам оно не на каждую строку кричало "Echo from arduino", а скажем "Command detected", но только на строку вида "cmd".
То есть, если я послал в сериал "cmd" - оно сказало что-то, любые другие строки - игнорировало молча.
Поможет нам в этом функция strcmp() . На сайте arduino.ru - в документации ее описание не найдете. Так что - прийдется нагуглить в любом справочнике по C/C++ . Там правда может быть сказано, что для того что-бы она была доступна нужно подключить библиотеку "#include <string.h>" - не обращайте внимание. ArduinoIDE уже подключает эту библиотеку ко всем скетчам. Самому ничего делать не нужно. Можно сразу пользоваться strcmp()
На всякий случай, функцию isLineReady() мы уже не меняем. Она уже и так "достаточно хороша". Читает строки в буффер - пусть только этим и знанимается. Меняем код в loop(). Который работает с "уже собранной строкой".
Предлагаю, как только новичек закончит свой первый проект, будем выдавать ему диплом об окончании :) Установленого образца! Ну и это нужно начинать принимать пожертвования на поддержание онлайн университета :)
>Ну о том, что я отделаюсь просто словами благодарности - я даже не думал ))
Давайте мы пока будет думать про функции strcmp() и strstr()
Puhlyaviy известный специалист по "уводу темы в сторону" :) (я вообщем-то и сам не без этого греха ;), но....)
Вообщем давате пока подумаем как сделать что-бы "Command OK" (кстати удобней ее было-бы с помощью println выводить) выводилось, не только когда строка в точности равна "cmd", а когда комбинация "cmd" встречалась где угодно внутри строки. Вообщем что-бы детекст срабатывал на строки типа:
"aaacmdbbb"
"aaaa cmd bbb cmd"
"cmdaaabbbcmd"
Но при этом молча на строки вида "aaa cm d bbb", "c m d" и т.п не вызвали сработки. Вообщем игнорировал все строки где нет подряд букв c,m,d
Подсказка: тут вам нужно вспомнить как я рассказывал, что если мы в if(...) пиханем что-то не булевское, если вместо многоточия там будет что-то что возвращет что-то отличное от true/false (1/0), то это "что-то" будет интерпретироваться как "равно оно нулю или нет".
Вообщем код
if(ЧТО-ТО)
равносилен
if(ЧТО-ТО!=0)
поэтому не нужно пугатся слов "функция возвращает указатель". Вам пока достаточно проверять "вернула она что-то отличное от нуля или нет". А то что "что-то" это указатель - пока нам не важно.
хорошо. Теперь нам нужно, что-бы оно срабатывало не "где угодно в строке", а только "если строка начинается на cmd".
То есть
"cmdaaa" - должно сработать
"aaacmd" - не должно.
"Готовой фунции" для такой проверки - у нас нет. Но, написать свою - тоже отностиельно не сложно.
Причем можно разными путями пойти. Можно, к примеру сделать обертку на strstr что-бы она возвращала не "указатель" на строку, которую нашли, а "индекс где нашли". Тогда "если индекс равен 0" - значит строка начинается на "то что нам нужно".
Но... давайте пока с указателями попробуем. Напишем функцию startsWith(char* inputString,char* prefix), которая будет возвращать является true если input начинается на prefix
inputString - где искать
prefix - что искать (искомая подстрока)
Чуть-чуть пояснение. strstr - вернуло нам указатель "где оно нашло искомую подстроку". Теперь нам нужно сравнить этот указатель, с указателем на строку "где искать". Если они совпадают - значит оно нашло "в самом начале строки где-искать". Ну и выясняем мы "совпалили указатели" самым тупым способом. Отняли один указатель от другого. Если в итоге разница ноль - значит указатели совпадали.
И еще одну поправочку нужно сделать. Помните когда мы писали "защиту от переполнения буфера", мы "теряли часть строки" (если буфер переполнился). Причем тогда - нам было не важно что теряется "начало строки" или "ее хвост".
Но теперь, раз мы будем проверять "начинается ли строка с букв cmd" - нам предпочтительеней, при переполнении "терять хвост".
Что бы строки типа
"cmdaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - тоже срабатывали.
Тогда мы можем "делать буффер поменьше" при этом детект будет работать даже на длинный строках корректно.
Найдите, где-то выше по ветке я давал пример как "терять хвост строки". Там было что-то типа "это выглядело бы так". Подправте isLineReady() под вновь открывшиеся обстоятельства.
Кстати, в качестве "внекласного задания", что-бы освоится лучше с массивами, указателями и т.п. можете попробовать самостоятельно реализовать startWith еще несколькими способами. Скажем проходит с помощью for() или while() по каждому байту строки и сравнивать (пока не упремся в символ нуля).
Или с помощью strlen() сравнить длины строк. Если inputString короче - ну значит она явно не начинается на prefix :)
А вот если длиней - тогда их можно сравнить с помощью функции strncmp(). Которая подобна strcmp() только сравнивает не целиком две строки, а только нужное нам количество символов.
или можно написать еще одну функцию indexOf() которая будет возвращать "индекс" найденной подстроки. И -1 если не нашло
(ее реализация - будет очень похожа на startsWith из прошлого поста. А сам startWith() примет вид
Замечательно. Еще и выделение строк в коде освоили :)
Усложняем задачу. Теперь нам нужно, что-бы увидив cmd оно не сразу "кричало об этом". А интерпретировало это примерно так:
1. Если строка начинается на "cmd" - нужно ЗАПОМНИТЬ это. Так как СЛЕДУЮЩАЯ строка - будет самой командой.
2. Выводило в Serial эту СЛЕДУЮЩУЮ строку типа "вот мы получили такую комманду".
Вообщем:
1. Игнорируем все, пока не прийдется строка начинающая на cmd
2. Выводим в Serial монитор строку прешедшую следующей после cmd.
3. И опять пункт 1
p.s. nextLineWillCommand - следующая строка будет коммандой.
"строка которая идет ПОСЛЕ строки начинающейся на 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", а следующую строку интерпретируем как "команду" (кричим, выводим ее, еще что-то).
Предлагаю, как только новичек закончит свой первый проект, будем выдавать ему диплом об окончании :) Установленого образца! Ну и это нужно начинать принимать пожертвования на поддержание онлайн университета :)
Предлагаю диплом выдавать посмертно, имущество экспроприировать в пользу детей Лешака.
я бы на вашем месте не шутил таким "черным юмором".. как то не уместно..
тем более, что Вы наверно не прочитали мой ответ на даный пост..
Да ладно вам :) Ведь небыло оговорено "чье посмертно" и "чье имущество". Вспоминайте Ходжу Насредина.
"Неизвестно кто к тому времени сдохнет. То ли Шах, то ли ишак, то ли Калапуций. Пойди потом разберись кто из них лучше знал богословие" :)
А вообще, я удивлен столь позним появлением Калапуция в этой ветке (первый ожидаемый - уже отметился).
Но вынужден отметить две вещи:
1. Несмотря на весь сарказм и желание "увести в сторону моря" - оба иногда говорят весьма дельные/полезные вещи, пусть и не всегда в дружелюбной форме (кстати я тоже часто так поступаю, не всегда я такой белый и пушистый).
2. Конечно я не могу говорить за других, это не более чем мое "впечатление". Но по сравнению с тем как обычно оба высказываются - в вашем случае это было, относительно конечно, "очень мягко и дружелюбно". Мне кажется, в какой-то мере это проявление уважения к вашим усилиям.
Вообщем IMHO это скорее было "дружеское подтрунивание" чем "наезд и троллинг". Нужно отдать должное - оба "сдерживались из последних сил". И явно не потому что "Не заметили нас". Уже за 200 сообщений ветка :)
Ну во первых... нужно и самому привыкать искать проблемы. Не всегда же я буду под рукой :)
Прогоните "текстовые данные" из #217 поста. Ведет себя ардуина как в "диалоге-примере" или есть отличие? Глядя в Serial вы сможете понять какую команду приняла ардуина?
Во вторых. Пытайтесь стать мысленно на место "хакера/тестера". Представте что вам нужно "сломать" скетч. Заставить его повести как-то "не в соотвествии с изначальным заданием". Попробуйте найти какие-то "граничные комбинации входящих данных" которые "снесут ему мозг". Слишком длинные строки, слишком короткие (пустые). Или, к примеру "а если в сам текст команды запихнуть "признак команды"?
Вот к примеру есл послать такие три строки
cmd
cmd
some command
1 - признак команды, 2 - сама команда, 3 - строка которая должа быть проигнорирована (так как прошлая строка "тело команды", а не "признак команды"). Ваш скетч именно так "поймет" такую последовательностб строк?
И давайте применим еще одну "хитрость". Сделаем для 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 и т.п. - так мы точно "не оставим за собой строительного мусора" :)
Э... схитрили :) Чуть упростили себе жизнь. В задании было
Yo--ho!!!... Command 'some command' received
А у вас вышло
Yo-ho!!! received command:some command
А если бы не человек был с другой стороны, а то же какая-то парсилка которая ждет именно такого формата как было в задании?
К тому же. Обрамление кавычками принятой комманды имеет то преимущество, что сразу видны пробелы. Вот возьму я и пошлю
"some command ". В вашем выводе - эти пробелы разглядеть не получится. У меня сразу будет видно "..... Command 'some command '" - я себе съекономлю час жизни, если потом эта команда будет с чем-нибудь сравниваться. Сразу глазами увижу.
"Хитрость" из #228 - тоже пропустили. Когда мы переключимся на настоящий gprs, он обладеет от этих "Yo-hoo".
Но, в принципе и как вы сделали "не хуже". Просто можно и так и этак. Хотя.... лично мне мой вариант больше нравится :) Как-то куски более независимы. Не нужно просматривать предыдущий 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;
}
}
}
Далее, мы опять видим что у нас функция 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");
}
Кстати, обратите внимание. Что внутри executeCommand я использую не глобальную переменную input_buff, а передал ее внутрь как параметр функции. Функция стала "более независима, более обособлена". Если мне нужно будет отладить именно ее поведение, то я могу не набирать в Serial команды, а вынести ее в отдельный скетч и написать что-то типа
В executeCommand, кроме вывода в DBG, с помощью if else if else и т.п. проверять чему же равна пришедшая комманда.
Скажем на "led on" - включать светик, на "led off" - выключать.
Строки вы уже умеете сравнивать. Только проверяйте не input_buff, а именно параметр функции command
И да... сделайте nextLineWillСommand - локальной переменной (естественно с ключевым слово static) - мы же уже умеем не засорять глобальное простраство имен. Только в крайнем случае там что-нибудь заводим.
Прежде чем начинать светик мучать - слепите #231 целиком. Дайте сюда. А то я "из головы писал", мог где-то очепятнутся. А так как "Это кусок", то даже на компилируемость не проверил.
копирую с Arduino IDE - уезжает у меня форматирование после вставки в форум :(
Есть подозрение что это из-за того что вы для "двигания" используете пробелы. Попробуйте клавишу "Tab" для этого.
А еще, в ArduinoIDE есть волшебная кнопка Ctrl-T (но я обычно ей пользуюсь только когда беру "чужой код плохо форматированны". Свой - предпочитаю сам с помощью TAB и Shift-TAB двигать. Кстати можно выделить и "двигать" сразу несколько строк.
Life23 пишет:
static bool nextLineWillCommand = false;
Вставил в Loop. Не могу сообразить правильно, то что вписал его сюда...(
Не понял в чем проблема. В коде вроде нормально все вставлено. Да и "делали уже такое не раз". Вон inputBuffIndex вставлен так же. Или вас смущало что это именно loop()? Так с точки зрения синтаксиса - она ничем не отличается. Это такая же функция как isLineReady(), executeCommand() и т.п.
Ее "особость" только в одном состоит - не вы ее вызываете. Все остальные функции вы должны "написать и не забыть вызвать", а loop() - только "написать". Причем "написать ее нужно обязательно" (хотя-бы пустую).
И еще. Ну неужели не хочется между строками 56 и 57 пустую строку (или две) вставить? Что-бы визуально отделить "вывод в лог" от "бизнес логики"? Вы же не на пергаменте пишите. Не нужно быть "как Ленин" (он там тоже на осьмушках указы писал, вроде,- бумагу экономил :)
Ну что, работает? Если да то "пора готовится к слиянию" :)
А для этого нам нужно, как мы помним. Сделать "минимально возможными" 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();
}
повыносить в отдельный функции типа action_LoadON(); и action_LoadOFF(); а в них, еще, пихануть дополнительные DBG.print (типа "включили/выключили") Да еще и millis() выводить что-бы в логе было видно "когда включили". Но это уже "со шлюхами и блек-джеком".
Но это уже - как хотите. Я обычно - делаю именно так. Ну хотя-бы потому, что если скетч пишу удаленному клиенту, то хочу видеть "полную картину" что происходит. Физического же диода я не вижу ;) А "полагатся на слова" - это не всегда удобно :)
В вашем случае - это может быть удобно что-бы "выложить лог на форум". Сразу видно "что там происходит" :)
Вообщем - решать вам. Делать ли скетч более "многословным", зато "удобней в отладке" или оставить "как есть".
После того как вы примете решение по #244, вам нужно еще одно решение принять.
Что мы дальше будем делать :)
В принципе можно занятся двумя вещами:
1. Начать уже объединять скетчи в один
2. Еще чуть-чуть помучать этот. Поднять уровень его "дуракоупорности". Сделать его еще более "непробиваемым" (и этим более удобным конечному потребителю).
1. Что-бы все логика была регистро-независимая. Что есть что-бы срабатыловало как "led on", так и "LED ON", так и "lEd oN" и т.п.
2. Взял-бы даташит на gprs и почитал, а не сохраняются ли у него sms-ски в памяти? Если сохраняются - что происходит когда переполняется? Если после этого перестает принимать новые - значит после обработки каждой sms-ски, нужно слать команду на ее удаление.
3. Посмотрел-бы как выглядит целиком строки которые присылает модем при получении SMS-ски. Подозреваю что там будет и номер с которого пришла SMS-ска. Сделал бы проверку, что-бы выполнялись команды только с определенного номера.
Но... с точки зрения "феншуя разработки" решение "Еще чуть-чуть помучать этот" - это не правильный выбор.
Важно как можно раньше получится "минимально работающий прототип", пройти "весь цикл" целиком, а уж потом - навешивать дополнительные фишки можно. Главное что в каждый момент - у вас будет "рабочая версия".
(вот как сами части мы писали по отдельности. вначале сделали "что-то минимальное", а потом постепенно усложняли).
А то вдруг у нас при совмещении выяснится что "нужно чуток переделать, мешают они друг другу". Если каждый содержит "по одной фиче" - нужно будет переделать одну фичу. А если их 20-ть, то все 20-ть править :) Или вдруг мы столько насобачим "плюшек", что потом для совмещения скетчей уже памяти не хватит :( Обидно об этом будет узнать уже после написания 100 страниц кода :)
Вообщем я бы предложил, вам, все-таки "запустить все вместе", выполнить "програму минимум", а потом жать ее до "максимума" (реализовыать то что описано в #246).
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 //тело смс
OK. Строку мы научились "собирать в буффер". Причем что-бы она была "настоящей сишной строкой". А значит - теперь нам доступны стандартный функции для работы со строками. Гуглим "C/C++ строковые функции" и пытаемся найти "подходящую нам". Будем учится работать с уже "принятыми строками".
Ну и ознакамливаемся "какие еще есть"... что-бы когда понадобится - примерно знать что искать (нет конечно гарантии что все они реализованы в arduino, для экономии места, но... всегда можно "взять и попробовать", по крайней мере "базовые" - доступны)
Ой... я же забыл сказать что мы хотим сделать :) Как же без этого искать "подходящую функцию" :)
А хотим мы вот что. Что-бы нам оно не на каждую строку кричало "Echo from arduino", а скажем "Command detected", но только на строку вида "cmd".
То есть, если я послал в сериал "cmd" - оно сказало что-то, любые другие строки - игнорировало молча.
Поможет нам в этом функция strcmp() . На сайте arduino.ru - в документации ее описание не найдете. Так что - прийдется нагуглить в любом справочнике по C/C++ . Там правда может быть сказано, что для того что-бы она была доступна нужно подключить библиотеку "#include <string.h>" - не обращайте внимание. ArduinoIDE уже подключает эту библиотеку ко всем скетчам. Самому ничего делать не нужно. Можно сразу пользоваться strcmp()
На всякий случай, функцию isLineReady() мы уже не меняем. Она уже и так "достаточно хороша". Читает строки в буффер - пусть только этим и знанимается. Меняем код в loop(). Который работает с "уже собранной строкой".
Предлагаю, как только новичек закончит свой первый проект, будем выдавать ему диплом об окончании :) Установленого образца! Ну и это нужно начинать принимать пожертвования на поддержание онлайн университета :)
хм.. Пожертвование - это святое! куда ж без этого!? ;))
хм.. Пожертвование - это святое! куда ж без этого!? ;))
ну а как вы еще хотели проявить вашу благодарность тому же Лешаку, который извел на вас кучу личного времени... :)
Ну о том, что я отделаюсь просто словами благодарности - я даже не думал ))
по поводу последнего задания:
>Ну о том, что я отделаюсь просто словами благодарности - я даже не думал ))
Давайте мы пока будет думать про функции strcmp() и strstr()
Puhlyaviy известный специалист по "уводу темы в сторону" :) (я вообщем-то и сам не без этого греха ;), но....)
Вообщем давате пока подумаем как сделать что-бы "Command OK" (кстати удобней ее было-бы с помощью println выводить) выводилось, не только когда строка в точности равна "cmd", а когда комбинация "cmd" встречалась где угодно внутри строки. Вообщем что-бы детекст срабатывал на строки типа:
"aaacmdbbb"
"aaaa cmd bbb cmd"
"cmdaaabbbcmd"
Но при этом молча на строки вида "aaa cm d bbb", "c m d" и т.п не вызвали сработки. Вообщем игнорировал все строки где нет подряд букв c,m,d
Подсказка: тут вам нужно вспомнить как я рассказывал, что если мы в if(...) пиханем что-то не булевское, если вместо многоточия там будет что-то что возвращет что-то отличное от true/false (1/0), то это "что-то" будет интерпретироваться как "равно оно нулю или нет".
Вообщем код
равносилен
поэтому не нужно пугатся слов "функция возвращает указатель". Вам пока достаточно проверять "вернула она что-то отличное от нуля или нет". А то что "что-то" это указатель - пока нам не важно.
Вы меня опередили )
хорошо. Теперь нам нужно, что-бы оно срабатывало не "где угодно в строке", а только "если строка начинается на cmd".
То есть
"cmdaaa" - должно сработать
"aaacmd" - не должно.
"Готовой фунции" для такой проверки - у нас нет. Но, написать свою - тоже отностиельно не сложно.
Причем можно разными путями пойти. Можно, к примеру сделать обертку на strstr что-бы она возвращала не "указатель" на строку, которую нашли, а "индекс где нашли". Тогда "если индекс равен 0" - значит строка начинается на "то что нам нужно".
Но... давайте пока с указателями попробуем. Напишем функцию startsWith(char* inputString,char* prefix), которая будет возвращать является true если input начинается на prefix
inputString - где искать
prefix - что искать (искомая подстрока)
Чуть-чуть пояснение. strstr - вернуло нам указатель "где оно нашло искомую подстроку". Теперь нам нужно сравнить этот указатель, с указателем на строку "где искать". Если они совпадают - значит оно нашло "в самом начале строки где-искать". Ну и выясняем мы "совпалили указатели" самым тупым способом. Отняли один указатель от другого. Если в итоге разница ноль - значит указатели совпадали.
И еще одну поправочку нужно сделать. Помните когда мы писали "защиту от переполнения буфера", мы "теряли часть строки" (если буфер переполнился). Причем тогда - нам было не важно что теряется "начало строки" или "ее хвост".
Но теперь, раз мы будем проверять "начинается ли строка с букв cmd" - нам предпочтительеней, при переполнении "терять хвост".
Что бы строки типа
"cmdaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - тоже срабатывали.
Тогда мы можем "делать буффер поменьше" при этом детект будет работать даже на длинный строках корректно.
Найдите, где-то выше по ветке я давал пример как "терять хвост строки". Там было что-то типа "это выглядело бы так". Подправте isLineReady() под вновь открывшиеся обстоятельства.
Кстати, в качестве "внекласного задания", что-бы освоится лучше с массивами, указателями и т.п. можете попробовать самостоятельно реализовать startWith еще несколькими способами. Скажем проходит с помощью for() или while() по каждому байту строки и сравнивать (пока не упремся в символ нуля).
Или с помощью strlen() сравнить длины строк. Если inputString короче - ну значит она явно не начинается на prefix :)
А вот если длиней - тогда их можно сравнить с помощью функции strncmp(). Которая подобна strcmp() только сравнивает не целиком две строки, а только нужное нам количество символов.
или можно написать еще одну функцию indexOf() которая будет возвращать "индекс" найденной подстроки. И -1 если не нашло
(ее реализация - будет очень похожа на startsWith из прошлого поста. А сам startWith() примет вид
И.. думаю еще можно парочку "способов реализации" нафантазировать. Вообщем если есть желание, можете "поигратся" с ней :)
Подскажите пожалуйста, что неправильно в объявлении массива функций:
void (massive[]) (void)={st, raz, stab, rez}; // массив указателей функций
Компилятор ругается: declaration of 'massive' as array of functions
Вопрос снят. Забыл *.
Замечательно. Еще и выделение строк в коде освоили :)
Усложняем задачу. Теперь нам нужно, что-бы увидив cmd оно не сразу "кричало об этом". А интерпретировало это примерно так:
1. Если строка начинается на "cmd" - нужно ЗАПОМНИТЬ это. Так как СЛЕДУЮЩАЯ строка - будет самой командой.
2. Выводило в Serial эту СЛЕДУЮЩУЮ строку типа "вот мы получили такую комманду".
Вообщем:
1. Игнорируем все, пока не прийдется строка начинающая на cmd
2. Выводим в Serial монитор строку прешедшую следующей после cmd.
3. И опять пункт 1
p.s. nextLineWillCommand - следующая строка будет коммандой.
не совсем понял задания.. (
Смотрите, сейчас у нас работает по логике
"строка начинающаяся на cmd" - это команда.
А нужна логика
"строка которая идет ПОСЛЕ строки начинающейся на cmd" - это команда.
Вообщем что-бы в терминале, я мог набрать что-то такое (кажду строку набираю отдельно и нажимаю кнопку <send> в конце).
Зачем мы это делаем? А вот что-бы SMS-ски принимать. Как шилд нам отдает SMS-ски? Вначале присылает строку начинающуюся на "+CMT". Типа "ой.. мы тут SMS получили, ща буду диктовать ее". А потом, следующей строкой, отдает нам сам текст SMS-ски.
Так и тут. Мы вначале ждем строку начинающуюся на "cmd", а следующую строку интерпретируем как "команду" (кричим, выводим ее, еще что-то).
ну что, задача понятна?
да, понятна. попробую теперь в коде это сделать.
правильно я мыслю?
Предлагаю, как только новичек закончит свой первый проект, будем выдавать ему диплом об окончании :) Установленого образца! Ну и это нужно начинать принимать пожертвования на поддержание онлайн университета :)
Предлагаю диплом выдавать посмертно, имущество экспроприировать в пользу детей Лешака.
я бы на вашем месте не шутил таким "черным юмором".. как то не уместно..
тем более, что Вы наверно не прочитали мой ответ на даный пост..
я бы на вашем месте не шутил таким "черным юмором".. как то не уместно..
не уверен - философы не пришли к единому мнению... бытие определяет сознание или наоборот.
пост прочитал - университет сжечь.
идите Вы в Отвлеченные темы со своим остроумием и философией...
идите Вы в Отвлеченные темы со своим остроумием и философией...
не хочу я ни вкакие отвлечённые темы идти со своим всем...
я бы на вашем месте не шутил таким "черным юмором".. как то не уместно..
тем более, что Вы наверно не прочитали мой ответ на даный пост..
Да ладно вам :) Ведь небыло оговорено "чье посмертно" и "чье имущество". Вспоминайте Ходжу Насредина.
"Неизвестно кто к тому времени сдохнет. То ли Шах, то ли ишак, то ли Калапуций. Пойди потом разберись кто из них лучше знал богословие" :)
А вообще, я удивлен столь позним появлением Калапуция в этой ветке (первый ожидаемый - уже отметился).
Но вынужден отметить две вещи:
1. Несмотря на весь сарказм и желание "увести в сторону моря" - оба иногда говорят весьма дельные/полезные вещи, пусть и не всегда в дружелюбной форме (кстати я тоже часто так поступаю, не всегда я такой белый и пушистый).
2. Конечно я не могу говорить за других, это не более чем мое "впечатление". Но по сравнению с тем как обычно оба высказываются - в вашем случае это было, относительно конечно, "очень мягко и дружелюбно". Мне кажется, в какой-то мере это проявление уважения к вашим усилиям.
Вообщем IMHO это скорее было "дружеское подтрунивание" чем "наезд и троллинг". Нужно отдать должное - оба "сдерживались из последних сил". И явно не потому что "Не заметили нас". Уже за 200 сообщений ветка :)
>правильно я мыслю?
Ну во первых... нужно и самому привыкать искать проблемы. Не всегда же я буду под рукой :)
Прогоните "текстовые данные" из #217 поста. Ведет себя ардуина как в "диалоге-примере" или есть отличие? Глядя в Serial вы сможете понять какую команду приняла ардуина?
Во вторых. Пытайтесь стать мысленно на место "хакера/тестера". Представте что вам нужно "сломать" скетч. Заставить его повести как-то "не в соотвествии с изначальным заданием". Попробуйте найти какие-то "граничные комбинации входящих данных" которые "снесут ему мозг". Слишком длинные строки, слишком короткие (пустые). Или, к примеру "а если в сам текст команды запихнуть "признак команды"?
Вот к примеру есл послать такие три строки
1 - признак команды, 2 - сама команда, 3 - строка которая должа быть проигнорирована (так как прошлая строка "тело команды", а не "признак команды"). Ваш скетч именно так "поймет" такую последовательностб строк?
И давайте применим еще одну "хитрость". Сделаем для Serial не один, а два псевдонима
DBG - это сокращение от DEBUG - отладка.
Перым - будет пользоваться в коде который должен, в итоге, работать с настоящим GPRS приемником, а вторым (DBG.println) для вывода всякой отладчной информации. Все эти "Yo-hoo", сообщений какая команда получена и проч - нам же не нужно, что-бы потом они случайно в gprs-шилд полезли. Понятно что в "финальном скетче" их можно выкинуть, но... всегда можно где-то случайно забыть/оставить, во вторых, даже с реальным железом иногда хочется видеть "что происходит". Тоже нужна отладка.
Тогда в финальном скетче мы просто заменим "#define GPRS SoftwareSerial(...)" а "#define DBG.... " оставим как есть. И весь вывод у нас "автоматически" разделитися. Отладочная инфа - пойдет в сериал монитор, а "AT-команды" - шилду :)
А когда скетч будет "ну совсем-совсем финальным". И мы, все-таки, захотим убрать всю эту отладку с концами, нам будет достаточно выкинуть #define DBG и дальше компилятор нам сам подскажет, где нужно убрать DBG.println и т.п. - так мы точно "не оставим за собой строительного мусора" :)
вот так?.. проверил - вроде бы то что нужно..
Э... схитрили :) Чуть упростили себе жизнь. В задании было
А у вас вышло
А если бы не человек был с другой стороны, а то же какая-то парсилка которая ждет именно такого формата как было в задании?
К тому же. Обрамление кавычками принятой комманды имеет то преимущество, что сразу видны пробелы. Вот возьму я и пошлю
"some command ". В вашем выводе - эти пробелы разглядеть не получится. У меня сразу будет видно "..... Command 'some command '" - я себе съекономлю час жизни, если потом эта команда будет с чем-нибудь сравниваться. Сразу глазами увижу.
"Хитрость" из #228 - тоже пропустили. Когда мы переключимся на настоящий gprs, он обладеет от этих "Yo-hoo".
Еще. Вместо
можно было сделать
Но, в принципе и как вы сделали "не хуже". Просто можно и так и этак. Хотя.... лично мне мой вариант больше нравится :) Как-то куски более независимы. Не нужно просматривать предыдущий if что-бы понять когда в эту ветку попадает. Да и... переставить местами их можно будет. Как-то легче думается когда порядок проверок в коде совпадает с тем порядком в котором строки приходят. Наглядней, что-ли
Далее, мы опять видим что у нас функция loop() начала "разрастся", делаем в ней "вообще разные вещи". И сами команды "детектим" и обработку команды выполняем (пока только выводим принятую команду в Serial, но ведь дальше больше будет).
Вынесем "работу с принятой командой" в отдельную функцию. Ну скажем ... executeCommand ее назовем
Кстати, обратите внимание. Что внутри executeCommand я использую не глобальную переменную input_buff, а передал ее внутрь как параметр функции. Функция стала "более независима, более обособлена". Если мне нужно будет отладить именно ее поведение, то я могу не набирать в Serial команды, а вынести ее в отдельный скетч и написать что-то типа
То есть - ей уже "пофиг" откуда взялась комманда.
OK.. Вообщем осталось "почти чуть-чуть" :)
В executeCommand, кроме вывода в DBG, с помощью if else if else и т.п. проверять чему же равна пришедшая комманда.
Скажем на "led on" - включать светик, на "led off" - выключать.
Строки вы уже умеете сравнивать. Только проверяйте не input_buff, а именно параметр функции command
И да... сделайте nextLineWillСommand - локальной переменной (естественно с ключевым слово static) - мы же уже умеем не засорять глобальное простраство имен. Только в крайнем случае там что-нибудь заводим.
Еще. Вместо
можно было сделать
Для Вас то это проще :) но мне что бы понимать, лечше расписать белее "извращенно" ))
Хотя изначально пытался сделать так как "можно было сделать" - но запутался..
Для Вас то это проще :) но мне что бы понимать, лечше расписать белее "извращенно" ))
Скорее всего "сразу выйти" - забыли (сам два раза забывал).
Ну я - вроде показал "как проще".
А... проверять несколько условий в if, пользоватся операторами && и || -тоже нужно учится. И писать и "читать" такой код.
Прежде чем начинать светик мучать - слепите #231 целиком. Дайте сюда. А то я "из головы писал", мог где-то очепятнутся. А так как "Это кусок", то даже на компилируемость не проверил.
Вот код. Все работает. Даже не смотря на то что Вы писали "с головы"! :)
boolean nextLineWillCommand = false; - так и оставили глобальной :(
Традиционно "форматирование":
Строка 34-мь - break уехал влево.
Строка 37-мь - default уехал вправо (должен быть под case).
А так... здорово :)
Осталось только в executeCommand реазовать управление светиком в зависимости то того какая command пришла.
АААаа.. )) ну не все сразу.. )) исправлю )
копирую с Arduino IDE - уезжает у меня форматирование после вставки в форум :(
может это из за того что еще работаю с "Sublime Text2", так что не ругайте по поводу "форматирования". (
Вставил в Loop. Не могу сообразить правильно, то что вписал его сюда...(
копирую с Arduino IDE - уезжает у меня форматирование после вставки в форум :(
Есть подозрение что это из-за того что вы для "двигания" используете пробелы. Попробуйте клавишу "Tab" для этого.
А еще, в ArduinoIDE есть волшебная кнопка Ctrl-T (но я обычно ей пользуюсь только когда беру "чужой код плохо форматированны". Свой - предпочитаю сам с помощью TAB и Shift-TAB двигать. Кстати можно выделить и "двигать" сразу несколько строк.
Вставил в Loop. Не могу сообразить правильно, то что вписал его сюда...(
Не понял в чем проблема. В коде вроде нормально все вставлено. Да и "делали уже такое не раз". Вон inputBuffIndex вставлен так же. Или вас смущало что это именно loop()? Так с точки зрения синтаксиса - она ничем не отличается. Это такая же функция как isLineReady(), executeCommand() и т.п.
Ее "особость" только в одном состоит - не вы ее вызываете. Все остальные функции вы должны "написать и не забыть вызвать", а loop() - только "написать". Причем "написать ее нужно обязательно" (хотя-бы пустую).
И еще. Ну неужели не хочется между строками 56 и 57 пустую строку (или две) вставить? Что-бы визуально отделить "вывод в лог" от "бизнес логики"? Вы же не на пергаменте пишите. Не нужно быть "как Ленин" (он там тоже на осьмушках указы писал, вроде,- бумагу экономил :)
Ну что, работает? Если да то "пора готовится к слиянию" :)
А для этого нам нужно, как мы помним. Сделать "минимально возможными" setup() и loop(). С setup() у нас и так все "терпимо". А вот loop() - немного "великоват".
Решаем это все, как и в прошлый раз - выносим из него все в отдельную функцию. Скажем waitAndExecuteCommand(). И вызываем из loop() уже ее.
Еще можно, для удобства, вытащить в глобальные переменные (в наш конфиг) строки означающие "щас будет команда" и сам текст комманд. Что-бы потом не лазить по коду, не искать "где же оно", если мы захотим, скажем, сменить текст команды "load on", на предположим "rele on".
Или, даже если не нужно менять. Удобно же глянуть в заголовок скетча и сразу понять "какими командами он управляет". Чем искать их разбросанными по скетчу.
все красиво работает :)
Можно еще вещи типа digitalWrite(LOAD_PIN,HIGH);
повыносить в отдельный функции типа action_LoadON(); и action_LoadOFF(); а в них, еще, пихануть дополнительные DBG.print (типа "включили/выключили") Да еще и millis() выводить что-бы в логе было видно "когда включили". Но это уже "со шлюхами и блек-джеком".
Но это уже - как хотите. Я обычно - делаю именно так. Ну хотя-бы потому, что если скетч пишу удаленному клиенту, то хочу видеть "полную картину" что происходит. Физического же диода я не вижу ;) А "полагатся на слова" - это не всегда удобно :)
В вашем случае - это может быть удобно что-бы "выложить лог на форум". Сразу видно "что там происходит" :)
Вообщем - решать вам. Делать ли скетч более "многословным", зато "удобней в отладке" или оставить "как есть".
После того как вы примете решение по #244, вам нужно еще одно решение принять.
Что мы дальше будем делать :)
В принципе можно занятся двумя вещами:
1. Начать уже объединять скетчи в один
2. Еще чуть-чуть помучать этот. Поднять уровень его "дуракоупорности". Сделать его еще более "непробиваемым" (и этим более удобным конечному потребителю).
добавил еще:
с millis() - я думаю в моем случае не стоит.. у меня есть светик и я могу проверить.
Понятно, что когда пишешь без "железа" это наверно единственный способ отладить прогу.
Склоняюсь к тому, что бы "Еще чуть-чуть помучать этот." для надежности и практики для себя.
OK. Для себя я бы сделал три вещи.
1. Что-бы все логика была регистро-независимая. Что есть что-бы срабатыловало как "led on", так и "LED ON", так и "lEd oN" и т.п.
2. Взял-бы даташит на gprs и почитал, а не сохраняются ли у него sms-ски в памяти? Если сохраняются - что происходит когда переполняется? Если после этого перестает принимать новые - значит после обработки каждой sms-ски, нужно слать команду на ее удаление.
3. Посмотрел-бы как выглядит целиком строки которые присылает модем при получении SMS-ски. Подозреваю что там будет и номер с которого пришла SMS-ска. Сделал бы проверку, что-бы выполнялись команды только с определенного номера.
Но... с точки зрения "феншуя разработки" решение "Еще чуть-чуть помучать этот" - это не правильный выбор.
Важно как можно раньше получится "минимально работающий прототип", пройти "весь цикл" целиком, а уж потом - навешивать дополнительные фишки можно. Главное что в каждый момент - у вас будет "рабочая версия".
(вот как сами части мы писали по отдельности. вначале сделали "что-то минимальное", а потом постепенно усложняли).
А то вдруг у нас при совмещении выяснится что "нужно чуток переделать, мешают они друг другу". Если каждый содержит "по одной фиче" - нужно будет переделать одну фичу. А если их 20-ть, то все 20-ть править :) Или вдруг мы столько насобачим "плюшек", что потом для совмещения скетчей уже памяти не хватит :( Обидно об этом будет узнать уже после написания 100 страниц кода :)
Вообщем я бы предложил, вам, все-таки "запустить все вместе", выполнить "програму минимум", а потом жать ее до "максимума" (реализовыать то что описано в #246).
1. Что-бы все логика была регистро-независимая. Что есть что-бы срабатыловало как "led on", так и "LED ON", так и "lEd oN" и т.п.
Использовать strlwr?
2. Взял-бы даташит на gprs и почитал, а не сохраняются ли у него sms-ски в памяти? Если сохраняются - что происходит когда переполняется? Если после этого перестает принимать новые - значит после обработки каждой sms-ски, нужно слать команду на ее удаление.
Да, сохраняет. проверенно на практике. Надо удалять. команда AT+CMGD=1,4.
3. Посмотрел-бы как выглядит целиком строки которые присылает модем при получении SMS-ски.
вот что у меня получилось: