Вообщем сможете сделать этот for так, что-бы не содержал в себе никаких символов в явном виде.
for (byte simv=65, index=0; simv <= 74; simv++,index++)
Кстати. Вы тут показали хорошие понимание цикла for()
Но, тут не уверен что это "плохой стиль" возможно это "мое субъективное", но IMHO большинству програмеров - все таки привычней когда в цикле - один счетчик. Вообщем "так делать можно", иногда даже позволяет "сделать все красиво", но злоупотреблять и запихивать в for половину программы - не стоит. Теоретически и присовоение масива можно было запихнуть в заголовох фора. Вообще сделать тело пустым :)
for (byte simv=65, index=0; simv <= 74; str[index++]=simv++);
Но сие уже "интелектуальный анонизм". Если хотим создать трудности тому кто будет читать :)
Вообщем отказатся от двух счетчиков - тут явно нужно. Они же у нас не являются независимыми. Между ними - есть четкая взаимосвязь. Обра проходит цифры "по порядку". Только стартовая цифра отличиается. Второй - мы всегда можем высчитать из первого :)
for(byte index=0;index<10;index++){
str[index]=index+'A'; // да, в данном случае 'A' действительно "наглядней"
}
#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из Serial данных
void Setup(){}
void Loop(){}
Теперь наша задача - заполнить этот input_buff не с помощью цикла,а кодами символов приходящими из Serial (если они приходят).
Признаком окончания строки в Serial у нас будет символ \n . Для этого мы в Serial мониторе, в нижнем правом углу, чуть левее скорости выберем "Newline". Это означает что когда мы нажимаем кнопку Send, к тому что мы напечатали добивится \n . И наш скетч сможет узнать что "строка принята целиком".
При этом, нам нужно не забыть, что в input_buff - нам уже не нужно сохранять этот \n, а нужно .... ну вы уже догадались :)
Вообщем когда строка примется "целиком". Пусть ардуина ее вернет. Как-то так:
Serial.print("Echo from Arduino:");
delay(500); // что же это за эхо без задержки? ;)
Serial.println(input_buff);
И опять должна приготовится принимать следующую строку.
не могу понять, какое должно быть условие, что бы считать все байты с serial.. (
Или читать все, пока не "упремся" в размер буфера?
Я вам ссылку давал available - им вы можете узнать "а есть ли что-нибудь в serial". Вообщем-то можете узнать и "сколько штук есть", но нам достаточно знать что "что-то есть", потому что...
Ну нужно пытатся сразу "считать все байты". Все равно нам Serial.read() возвращает их по одному. Так что у нас задача дробится на
"считать байт", "решить что с ним делать" (положить в буфер, игнорировать, заменить на нулевой символ).
Размер буффера - да нужно будет учитывать. Но... дайте это чуть позже. Пока - просто не будет слать в Serial монитор строки больше размера буфера. Ограничемся посылкой слов "hello", "bla-bla" и т.п.
Вообщем - читайте один байт за один проход loop(), если даже там в Serial еще что-то осталось - не страшно. Следующий байт считается при следующем проходе:
Только, вот сразу из Serial.read() сразу в массив пихать - не очень хорошо. Для большинства символов - действительно "нужно сохранить в буффер", но у нас ведь есть и исключения. Конец строки там как-то обработать... так что, лучше прочитать символ во временную переменную. Посмотреть "что это за символ", и уж на основании этого решать "сохранять его в буфер или нет".
Такой подход "байт за один проход", так же позволит вам что-бы остальная логика работала "как-бы паралельно". Может вы еще диодом, в тоже самое время мигаете. Или кнопки опрашиваете. Если мы сразу будет ждать "всю строку целиком", то в случае когда нам будет отправлять эту строку "меддд-лееее-но.....", у нас скетч встанет "колом". Диод - перестанет мигать, кнопки - опрашиватся. Пока строка не прийдется целиком.
А так - есть символ - обработали - сделали еще что-то полезное. Опять проверили. Есть? Обработали, нет... ну будем пока заниматся другими делами.... Символ конца строки? OK. Теперь посмотрим какая строка у нас "собралась из кусочков".
да. символ конца строки, по условия задачи означает "строка пришла целиком, можно начинать что-то с ней делать именно как со строкой". начинаеть ее сравнивать с чем-нибудь, отправлять в качестве "эхо" и т.п.
Не забудте только настроить Serial монитор, что-бы он посылал эту \n. Что-бы она действительно приходила.
#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из Serial данных
int i=0;
void setup(){
Serial.begin(9600);
}
void loop(){
if(Serial.available()){
input_buff[i] = Serial.read();
i++;
}
Serial.print("Echo from Arduino:");
delay(500); // что же это за эхо без задержки? ;)
Serial.println(input_buff);
}
в начале конечно чуть не сломал голову: какого меня посылает компилятор.. С большой буквы setup был и loop.
выводит он мне "эхо" вот только по строчно "валит" и плюсует новые символы..
А дальше у меня ступор.. пробывал с For. но не могу понять какое ему условие поставить. что бы эхо было полной строкой что я ему отправил.. (
и не "строчил каждые" deley(500) "Echo from Arduino:"(
и понял еще одну штуку: если я отправил что-то в serial, условие seial.available уже будет больше 0.
Наверно не совсем я понимал работу железа.. получаеться - если я отправил в сериал, то там эти данные и храняться, т.е. в буфере чипа?
>Наверно не совсем я понимал работу железа.. получаеться - если я отправил в сериал, то там эти данные и храняться, т.е. в буфере чипа?
Да. Пока не считаем его Serial.read() (или буфер переполнится)
>А дальше у меня ступор.. пробывал с For
не нужно.
>вот только по строчно "валит" и плюсует новые символы..
А вы где-то записали \0 в input_buff? Как же Serial.print(input_buff) поймет что "пора остановится"?
Вы где-то проверяете что пришло \n?
Вам же "эхо" нужно выводить только когда "пришел символ конца строки", а не "при каждом проходе loop()" (как сейчас).
leshak пишет:
так что, лучше прочитать символ во временную переменную. Посмотреть "что это за символ", и уж на основании этого решать
leshak пишет:
"считать байт", "решить что с ним делать" (положить в буфер, игнорировать, заменить на нулевой символ)
Заведите переменную, скажем "char ch", делайте чтение Serial.read() в нее. Если она \n, значит в буффер пора пихануть \0 и вывести эхо, если нет - сохраняем ch в буффер.
в начале конечно чуть не сломал голову: какого меня посылает компилятор.. С большой буквы setup был и loop.
leshak пишет:
P.S. Кстати в C/C++ принято называть функции с маленькой буквы. Как-бы "джентельменское соглашение". Что-бы все было однотипно и не гадать.
Теперь понимаете почему "придерживатся стиля" - так важно и почему это может часы жизни и комок нервов спасти? ;) Почему я все время пристаю с такими мелочами-гупостями "назовите по другому", "дефайн - большими буквами" и т.п.?
Заведите переменную, скажем "char ch", делайте чтение Serial.read() в нее. Если она \n, значит в буффер пора пихануть \0 и вывести эхо, если нет - сохраняем ch в буффер.
Ладно. Если не выходит. Плюньте пока на буффер. Давайте упростим задачу до:
1. Если пришел байт из Serial
2. Прочитать его в переменную типа byte или char
3. Сравнить эту переменную с \n, если совпало - Сделать Serial.println("End of line detected"); . Если не совпало - ничего не делать.
4. Ждать следующего байта.
void loop() {
if (Serial.available() > 0) { //если есть доступные данные
t = Serial.read();
if (t == '10');{
Serial.print("I received end line");
delay(1000);
}
}
}
не пойму, почему у меня Serial.print("I received end line"); выполняеться два раза.. (
А '10' - это что? Кавычки - говорят о том что это "это символ". Но... я вижу там два символа. так что... вы сравниваете t - не очень пойми с чем. t - содержит код принятого символа. код - "это просто цифра".
Посмотрите на сообщение #151 . Вы писали что "да это понятно".
Мы же раньше уже "устанавливали какой-то элемент массива" зная его код. Тут тоже самое. Только вместо присваивания - нужно сравнить.
P.S. Если "все равно не понятно" и "а точно!!! (и хлопок по лбу)" - не возникло. Говорите. Не убредайте в дебри :) Тут все "ну очень близко".
P.S. И по идее, delay(1000); - тут совсем не нужен. println- будет срабатывать только когда он компа пришел символ "конца строки". А вы же не будете его слать "очень часто". Только когда нажали кнопку Send - следовательно "частить" - не будет. И задержки - не нужны.
И не забывайте. Что вам не нужно "руками" из сериал монитора посылать символ с кодом 10-ть. Нужно настроить сам сериал монитор, что-бы он посылал его в конце каждой строки автоматом:
leshak пишет:
в Serial у нас будет символ \n . Для этого мы в Serial мониторе, в нижнем правом углу, чуть левее скорости выберем "Newline". Это означает что когда мы нажимаем кнопку Send, к тому что мы напечатали добивится \n . И наш скетч сможет узнать что "строка принята целиком".
"не пойму, почему у меня Serial.print("I received end line"); выполняеться два раза.. ("
Если выполняеться "Serial.print", значит условие в if возвращает "True"?
Но один я не заметил, что натупил и постлавил ";" после условия? ))
void loop() {
if (Serial.available() > 0) { //если есть доступные данные
byte t = Serial.read();
if (t == 10){
Serial.print("I received end line");
}
}
}
я не мог понять, почему у меня выполнялось условие даже без кавычек..
Ну так ведь и компилятор "спотыкается" на первой ошибке, выбрасывает ее и ниже по коду уже не смотрит :) Я тоже привык смотреть "глазами компилятора" :) Нашли одну - исправили, если не заработало - ищем дальше :)
>Если выполняеться "Serial.print", значит условие в if возвращает "True"?
да.
Только чуть-чутьп о стилистике (форматирование кода, отступы).
Плохо:
void loop() {
if (Serial.available() > 0) { //если есть доступные данные
byte t = Serial.read();
if (t == 10){
Serial.print("I received end line");
}
}
}
Хорошо:
void loop() {
if (Serial.available() > 0) { //если есть доступные данные
byte t = Serial.read();
if (t == 10){
Serial.print("I received end line");
}
}
}
Все вложенные блоки - отодвигаем вправо. Иначе вы очень быстро запутаетесь какая фигурная скобка что закрывает. Что выполняется внутри первого if-а, а что второго и т.д.
OK. Next.
Сейчас мы сделали "если пришел символ конца строки, то делаем print", пусть это так и останется, но добавится "а если пришедший символ какой-то другой, не конец строки, то ложим его в буфер и увеличивает индекс буфера". Тут вам понадобится if...else
Половина его у вас уже написана в #170, а вторая половина, на 80% написана в №162 (строки 10,11). Осталось только "слепить вместе".
int i=0;
void setup(){
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) { //если есть доступные данные
byte t = Serial.read();
if (t == 10){
Serial.print("I received end line");
}
else {
input_buff[i] = Serial.read();
i++;
}
}
}
Пока да. Только Serial.print почему-то в лево уехало :) (вообщем с форматирование уже намного лучше, но еще "не идеально").
По именам. i - неудачное название. Традиционно i - используется в качестве счетчика в цикле for. Поэтому любой читающий воспринимает что где-то снаружи есть цикл for. В третьих такие короткие имена для глобальных переменных - потенциальный источник больших мозголомок. Вот будете вы потом склеивать этот код с каким-то другим скетчем. И там будет тоже использовать переменная i (для совершенного других целей). И все сломается. И заметить почему "все идет не так" - будет очень трудно.
Вообщем гораздо лучше было назвать inputBufIndex - сразу понятно что это за хрень (может и буферов у нас несколько будет?)
Ну и втретьих. Индекс же у нас не может быть отрицательным. Поэтому для него int - неудачный тип. Нужен либо byte (если мы заранее знаем что буфер не будет больше 255), либо unsigned int (вдруг мы потом увеличим буфер)
Вообщем-то как-то так:
int inputBuffIndex=0;
void setup(){
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) { //если есть доступные данные
byte t = Serial.read();
if (t == '\n'){
Serial.print("I received end line");
}
else {
input_buff[inputBuffIndex] = Serial.read();
inputBuffIndex++;
}
}
}
Заменил t == 10 на t =='\n' - что-бы легче читалось. вдруг кто-то не помнит какой код у переноса строки? ;) Но это - вкусовое "t == 10" - тоже вполне нормально.
"а если пришедший символ какой-то другой, не конец строки, то ложим его в буфер и увеличивает индекс буфера"
А у вас вышло
"если пришедший символ как-йто другой, не конец строки, то читаем и ложим в буффер следующий символ" (причем в слепой вере что следующий символ успел прийти).
Представте себе что Serial это такая труба с шариками. разноцветными. С той стороны компьютер "засовывает" в эту трубу шарики разного цвета. Каждый вызов Serial.read - достает очередной шарик из трубы в вашу руку (переменная t). И на основании его "цвета" вы решаете что делать.
Выполнить какое-то действие если шарик "правильного цвета" ( \n ) или положить ЕГО в коробку (в буффер).
#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из Serial данных
unsigned int inputBuffIndex=0;
void setup(){
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) { //если с трубы полезло:
byte t = Serial.read(); //берем с трубы в руку(то что в руках - переменная t)
if (t != '\n'){ //если в руках не то что нам нужно
input_buff[inputBuffIndex] = t; //пихаем в ящик с ячейками
inputBuffIndex++; //закрываем ячейку и оставляем табличку "сунуть в след. ячейку")
}
else {
Serial.print("I received end line"); //если шарик тото что нам нужен - кричим: "Вот он!"
}
}
}
О... другое дело. Непонятно зачем только вы условие с t=='\n', переделали на t!='\n' - но можно и так :)
Хотя я бы, все-таки рекомендовал вернутся к t=='\n'. А ну как нам нужно будет обрабатывать еще какой-нибудь "спец символ"?
Тогда мы сможем легко добавить еще один if.
Вообщем, в данном случае лучше сохранять в буфер по признаку "все остальные символы...", чем "если не \n"
#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из Serial данных
unsigned int inputBuffIndex=0;
void setup(){
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) { //если с трубы полезло:
byte t = Serial.read(); //берем с трубы в руку(то что в руках - переменная t)
if(t=='\n'){ //если шарик тото что нам нужен
Serial.print("I received end line"); // кричим: "Вот он!"
}
else { // любые другие шарики
input_buff[inputBuffIndex] = t; //пихаем в ящик с ячейками
inputBuffIndex++; //закрываем ячейку и оставляем табличку "сунуть в след. ячейку")
}
}
}
OK. Поехали дальше. Символы в буффер мы научились складывать. Но что-бы "буффер/набор символов" превратился в "настоящую C-шну строку" нужно еще что-то в этот буфер пихануть. Что-то что-бы функции типа Serial.print могли догадатся где строка закончилась.
P.S. Если я слишком "туманно" изъяснился, если СРАЗУ не поняли что еще нужно сделать кроме "кричим вот он" - говорите.
когда мы увидили "шарик тот что нам надо" пишим в последнюю не заполненную ячейку "0"(окончание строки в масиве" ). Проверяем перед "кричим вот он" есть ли в последней ячейке "0". да - кричим. нет - идем дальше по коду.
когда мы увидили "шарик тот что нам надо" пишим в последнюю не заполненную ячейку "0"
Верно, а теперь давайте в коде это увидим.
Life23 пишет:
(окончание строки в масиве" ). Проверяем перед "кричим вот он" есть ли в последней ячейке "0". да - кричим. нет - идем дальше по коду.
А это уже вы пошли наводить "тень на плетень". Зачем нам проверять есть ли в последней ячейки ноль, если мы только что туда сами его записали?
Смотрите, мы увидели что "шарик правильный" (пришел символ конца строки в терминах Serial).
На это мы делаем два действия:
1. Сохраняем в буффер символ конца строки (только уже "сишный", не \n который пришел, а "просто ноль". правильно вы сказали).
2. "Кричим".
Нужно будет - можем еще светодиод зажеч. Вообщем мы поймали событие "пришел правильный шарик", и по этому событию делаем любое нужно нам количество действий.
Вообщем это вы уже пошли придумывать сложности которых нет. Кроме записи нуля в буффер - пока ничего не нужно (в рамках текущего задания).
#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из Serial данных
unsigned int inputBuffIndex=0;
void setup(){
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0){ //если с трубы полезло:
byte t = Serial.read(); //берем с трубы в руку(то что в руках - переменная t)
if(t=='\n'){ //если шарик то что нам нужен
input_buff[inputBuffIndex] = '\0'; //Пихаем в ячейку с которой "работаем" 0 (окончание С-шной строки)
Serial.print("I received end line"); // кричим: "Вот он!"
}
else { // любые другие шарики
input_buff[inputBuffIndex] = t; //пихаем в ящик с ячейками
inputBuffIndex++; //закрываем ячейку и оставляем табличку "сунуть в след. ячейку")
}
}
}
Ну что-ж. Теперь у нас в input_buff лежит "правильная сишная строка". И ничто не мешает нам ее вывести. Реализовать то что мы хотели сделать изначально в #154. Вместо "I received end line" вывести нашу "полученную строку".
Попробуйте сделать это.
После этого сразу начинать думать над вопросом "а если начнет передаватся вторая строка, куда она начнет записыватся?"
#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из Serial данных
unsigned int inputBuffIndex=0;
void setup(){
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0){ //если с трубы полезло:
byte t = Serial.read(); //берем с трубы в руку(то что в руках - переменная t)
if(t=='\n'){ //если шарик тото что нам нужен
input_buff[inputBuffIndex] = 0; //Пихаем в ячейку с которой "работаем" 0 (окончание С-шной строки)
Serial.print("Echo from Arduino:"); // кричим: "Вот он!"
delay(500); // что же это за эхо без задержки? ;) (с)
Serial.println(input_buff); //показываем что у нас в ящике с ячейками
inputBuffIndex=0; //после того как мы показали наш "ящик с ячейками" - убираем все с "ячеек"
}
else { // любые другие шарики
input_buff[inputBuffIndex] = t; //пихаем в ящик с ячейками
inputBuffIndex++; //закрываем ячейку и оставляем табличку "сунуть в след. ячейку")
}
}
}
Ну как-бы. Правда не все коментарии мне понятны :) Но то уж фиг с ним. Главное что вам понятно.
К коду - притензий нет (сами-то проверили, хе-хе, работает?) :)
OK. Смотрим дальше. А что будет если к нам прелезет строка символов на 50-т? Памяти-то мы выделили только на 20-ть. Значит дальше оно выйдет за границы буффера и пойдет записыватся "куда-попало" (и может скрешить нам весь скетч).
Выделить больше - не вариант (сколько бы не выделили, всегда может прийти еще длиней).
Конечно если "не влезает", то принять длинную строку мы не сможем. Но нужно что-бы хотя-бы к катастрофе это не приводило. Проверять еще и эту ситуацию.
Тут у нас есть два варианта. Если "буфер уже полон", то....
1. Следующие пришедшие символы читаем, но никуда не записываем. Тупо теряем их.
2. Начинаем опять писать "в начало буфера" (делаем inputBuffIndex=0). Новые пришедшие символы буду записаны поверх ранних.
В одном случае мы потеряем "хвост" длинной строки, в другом "ее голову". Какой вариант вы выберите - не суть важно. В любом случае эта строка "обкоцалась". Будет ждать следующию нормальную :)
Хотя есть еще одни вариант. Интерпретировать "буфер полон" точно так как и "пришел символ \n". Тогда мы ничего не потеряем. Просто длинная строка "разобъется на две строки".
Но, учитывая что мы будем делать дальше, я бы предложил все-таки выбрать из двух вариантов "лишнее просто теряется".
Да. Это вариант "теряем голову длинной строки". На всякий случай можно проверку сделать менее строгой
if (inputBuffIndex >= INPUT_BUF_SIZE)
Если мы где-то в другом месте ошибемся и еще где-то случайно сделаем inputBuffIndex++ (хотя не должны)
Вариант "теряем хвост" выглядел-бы так
else { // любые другие шарики
if (inputBuffIndex < INPUT_BUF_SIZE){ // сохраняем только если еще есть место свободное
input_buff[inputBuffIndex] = t; //пихаем в ящик с ячейками
inputBuffIndex++; //закрываем ячейку и оставляем табличку "сунуть в след. ячейку")
}
}
#define INPUT_BUF_SIZE 20 // размер входного буфера
char input_buff[INPUT_BUF_SIZE+1]; // место под хранение входящих из Serial данных
byte inputBuffIndex=0;
void setup(){
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0){ //если с трубы полезло:
byte t = Serial.read(); //берем с трубы в руку(то что в руках - переменная t)
if(t=='\n'){ //если шарик тот что нам нужен
input_buff[inputBuffIndex] = 0; //Пихаем в ячейку с которой "работаем" 0 (окончание С-шной строки)
Serial.print("Echo from Arduino:"); // кричим: "Вот он!"
delay(500); // что же это за эхо без задержки? ;) (с)
Serial.println(input_buff); //показываем что у нас в ящике с ячейками
inputBuffIndex=0; //после того как мы показали наш "ящик с ячейками" - убираем все с "ячеек"
}
else { // любые другие шарики
if (inputBuffIndex >= INPUT_BUF_SIZE){
inputBuffIndex=0;
}
input_buff[inputBuffIndex] = t; //пихаем в ящик с ячейками
inputBuffIndex++; //закрываем ячейку и оставляем табличку "сунуть в след. ячейку")
}
}
}
Но код потихоньку начинает становится "тяжелым". Уже в одни взгля не поймешь что тут происходит (еще можно, но...).
Как минимум у нас получился винигрет логики которая отвечает за "чтение и собирание строки" с логикой "что делать когда строка собрана целиком" (кричать, выводить и т.п.)
Способ борьбы со сложностью мы уже знаем - разбивание на функции.
я щас быстренько сам вынесу в функцию, просто словами и образами я буду неделю объяснять "что я хочу", "кодом" - оно зачастую легче. что вам "самому сделать" - еще будет :)
Итак, что я хочу:
1. Что-бы функция занималась приемом строки - и ничего более
2. Что-бы функция возвращала значение true или false в зависимости от того "Готова строка" или еще нет.
3. inputBuffIndex - убрать из глобальных переменных. Так как использоватся она должна только внутри функции. "Внутреняя кухня". Всем остальным нужно только знать "готова строка или нет".
Функцию назовем isLineRead() и будем вызывать ее из loop()
Заодно... как и в прошлом скетче сделаем для Serial дефайт GPRS . Нам же их потом "лепить вместе", поэтому желательно "в одном стиле".
#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()){ // есть готовая строка?
GPRS.print("Echo from Arduino:"); // Кричим о том что "строка готова"
delay(500); // что же это за эхо без задержки? ;) (с)
GPRS.println(input_buff); //показываем что у нас в ящике с ячейками
}
}
// читаем из GPRS приходящие символы и складывает их в input_buff[]
// возвращает true, если в input_buff[] собралась "полная сишная строка"
// во всех остальных случаех - возвращает false
bool isLineReady(){
static byte inputBuffIndex=0; // текущий индекс в буффере, куда мы будем записывать "очередьно" пришедший символ
if (GPRS.available() > 0){ //если с трубы полезло:
byte t = GPRS.read(); //берем с трубы в руку(то что в руках - переменная t)
if(t=='\n'){ //если шарик тот что нам нужен
input_buff[inputBuffIndex] = 0; //Пихаем в ячейку с которой "работаем" 0 (окончание С-шной строки)
inputBuffIndex=0; //после того как мы показали наш "ящик с ячейками" - убираем все с "ячеек", начинаем заполнять буффер с начала
return true; // возвращаем что "строка готова" и выходим из функции, больше нам пока тут делать нечего
}
else { // любые другие шарики
if (inputBuffIndex >= INPUT_BUF_SIZE)inputBuffIndex=0; // при переполнении - начинаем заполнять буфер с начала
input_buff[inputBuffIndex] = t; //пихаем в ящик с ячейками
inputBuffIndex++; //закрываем ячейку и оставляем табличку "сунуть в след. ячейку")
}
}
return false; // раз мы дошли сюда, значит "строка еще не готова" (ничего не пришло, или пришел не конец строки)
}
Посмотрите. Есть вопросы по этой трансформации или "все очевидно"?
Кстати тут уже немного просматривается "слишком много коментариев". Если какие-то строки уже вам уже "ну совсем очевидны сами по себе" - можете чуток потереть коменты (или сделать их менее многословными). А то уже за коментами код теряется :) Каждую строку коментировать - это тоже не очень хорошо. Я просто стараюсь "объяснять подробней", но в итоговом коде - лучше так не оставлять.
OK. Тогда давайте еще одну "ситуацию" предусмотрим. Пока мы предполагали что из управляющих символов нам приходит только \n
Но это бывает далеко не всегда (на разных операционках, в разных кодировках, разные модемы и т.п.) иногда в конце строки посылают сразу два символ \r\n . Еще и "возврат карретки" приходит. (кстати Serail.println() - в конце именно "\r\n" добавляет, а не "\n")
Сверх-страшного - нечего не слушится. \n в конце все-таки есть и мы нормально отработаем "конец строки". Но, вот этот \r - попадем нам внутрь нашего буффера. А он нам - нафиг не нужен.
Можете проверить что "он туда попадает" таким способом. В сериал мониторе. Внизу. Чуть левее скорости, выставте вместо "Newline" (добавлять в конце \n), ставите Both NL & CR (добавлять в конце \r\n). И посылаете скетчку строку "12345678901234567890" (тут ровно 20-ть символов). Вроде "должно влезть" в наш буффер. А не "влезет". Обрежетеся (увидите пустую строку). Именно потому что \r лишний затесался и на самом деле строка вышла 21 символ.
Вообщем нужно, кроме "детекта" \n, еще обрабатываться ситуацию когда приходит \r . В этом случае нужно ..... просто ничего не делать (не сохранять, счетчик не увеличивать... вообще ничего). Тогда наш счетч будет иметь одинаковое поведение в не зависимости от того в каком виде нам шлют концы строк.
Попробуйте сделать это. Тут можно конечно впихнуть еще один if внутрь ветки else, но постарайтесь сделать это с помощью конструкции описанной тут Оператор If..else | Аппаратная платформа Arduino (во втором примере видно эту "конструкцию").
И еще, пока не забыл. О том как С/C++ делает привидение к boolean типу. Если где-то ожидается значени true/false (HIGH/LOW). В if условии, булевской операторе и т.п. А мы туда "подали" что-то другое (какое-то число, строку, еще что-то), то компилятор действует по логике: если это "что-то" является нулем - считаем его false, все остальное - принимаем за true.
Это позволяет условия вида
if (GPRS.available() > 0)
Писать более краток
if (GPRS.available() )
Если available() вернет что-то отличное от нуля - if сработает. В таком виде не тольое более кратко, но оно даже как-то ближе к естественному языку. Легче читается. почти дословно "если в GPRS есть доступные символы...", вместо "если в GPRS количество доступных символов больше 0".
Это ОЧЕНЬ распространненая форма записи. Привыкайте так писать. И что-бы легче понимать когда в чужом коде встерите, и что-бы ваш код не выглял как "чайниковский" :)
Вот еще еще пример где часто это можно встретить. Пишут
if(digitalRead(PIN)){
Вместо
if(digitaRead(PIN)==HIGH){
или
if(!digitalRead(PIN)){
вместо
if(digitalRead(PIN)==LOW)
(тут используется еще и тот факт, что HIGH/LOW это синонимы/псеводонимы true/false) Вернее синонимы нуля и единицы.
Если загляните в файлик Arduino.h (он подключается ко всем вашим скетчам, незаметно/автоматом) то поймете откуда растут ноги этой
Не, вы тут сделали условие !='\r', а нужно было "ловить ситуацию когда t=='\r'"
Что-бы вышло нечто типа "если t равно "значение1" - делаем одно, если t==другое делаем второе, если t==третье... и т.п. если же ни одно из условий не сработало ("во всех остальных случаях"), то..... сохраняем в буффер
if(t=='\n\){
...
}
else if(t=='\r){
.....
} else { // если ни одно из условий не выполнилось
}
Тогда, если нам понадобится обрабатывать еще какие-то сутиации, мы можем однотипно выписывать
if(){}
else if{}
else if{}
else if{}
....
else {}
До бесконечности. Ловить разные "ситуации" сколько нам нужною.
В вашем же варианте, последний if будет очень замудренным. Скажем поднадобится вам делать на \r не "ничего", а "вывести Serial.println("Carriage return detected");" можно будет просто вписать println не меняяя логики.
В принципе "правильно поняли". Можно в более явно виде написать "пустой блок кода":
if(t=='\r'){}
else
Что-бы не привыкать лепить точку запятой после if. Вы, кажись один раз, уже имели развлечение с такой закравшеся точкой запятой.
OK. Конструкцию "if else if else if else" ... рассмотрели. Это типичный паттерн когда нужно "проверить по очереди условия и выполнить что-то только по первому совпавшему". Ее имеет смысл применять когда сами условия if - какие-то "сложно-разнообразные". Для ситуации когда все условия имеют вид if(переменная==ЗНАЧЕНИ1) else if(переменная==ЗНАЧЕНИ2) else if(переменная==ЗНАЧЕНИ3) else, то есть когда мы ищем "с чем совпадает переменная (одна и таже)" есть специальная, более специализированно-компактная конструкция языка switch case (если внимательно читали if...else там в конце было примечание про нее).
Попробуйте теперь функцию isLineReady() что-бы использовался switch, а не if-else
P.S. Я собственно потому все время и "упирался" вашим попыткам сделать if(t!='\n')и if(t!='\r'). Потому что логику использующу "не равно" - трудно потом "переделать на swith", а вот когда все условия имеют вид if(t=='...') - не должно вызвать проблемм. Логика остается такой же, только чуть-чуть синтаксис меняется.
Супер. Только форматирование "чуть-чуть" поехало. case влево ушли. А так - "то что доктор прописал".
Кстати, всю эту функцию можно рассматиривать как "типичная заготовка для приема-обработки чего-то из сериал".
Вот предположим у нас-бы стояла задача принимать буквы из Serial. На буквы a/b включать/выключать LED1, а на c/d - LED2
Мы бы написали что-то типа:
void listenSerialCommands(){
if(Serial.available()){
byte c=Serial.read();
switch(c){
case 'a': digitalWrite(LED1,HIGH);
break;
case 'b': digitalWrite(LED1,LOW);
break;
case 'c': digitalWrite(LED2,HIGH);
break;
case 'd': digitalWrite(LED2,LOW);
break;
case '\n': // переводы строк
case '\r: // и возврат каретки
// игонорируем
break;
default:
Serial.print("Unknown command '");
Serail.print(c); // выводим символ который не поняли
Serial.print("' - ");//
Serial.println(c,DEC); // и код этого символа
}
}
}
Как видите "структурно" - очень похоже на то к чему мы пришли собирая строку в буффер. Так что этот "паттерн" - он довольно универсален.
Вообщем сможете сделать этот for так, что-бы не содержал в себе никаких символов в явном виде.
))) я именно с этого и начинал
так? ;)
Не сомневайтесь. Если бы вы сначала показали бы этот код. Я бы сказал "а теперь без цифр только символами" :)
Именно что-бы была увереность "перехода туда/сюда" и четкое понимание связи символа и его кода :)
Кстати и перевод строки и нулевой символ тоже можно было так же:
Вообщем теперь, думаю код вида:
или
Уже не вызвать изумлений. И будет понятно "что тут делают" (или не понятно? ;)
это понятно. просто не стал писать в посте.
и с этим.
Вообщем сможете сделать этот for так, что-бы не содержал в себе никаких символов в явном виде.
for (byte simv=65, index=0; simv <= 74; simv++,index++)
Кстати. Вы тут показали хорошие понимание цикла for()
Но, тут не уверен что это "плохой стиль" возможно это "мое субъективное", но IMHO большинству програмеров - все таки привычней когда в цикле - один счетчик. Вообщем "так делать можно", иногда даже позволяет "сделать все красиво", но злоупотреблять и запихивать в for половину программы - не стоит. Теоретически и присовоение масива можно было запихнуть в заголовох фора. Вообще сделать тело пустым :)
Но сие уже "интелектуальный анонизм". Если хотим создать трудности тому кто будет читать :)
Вообщем отказатся от двух счетчиков - тут явно нужно. Они же у нас не являются независимыми. Между ними - есть четкая взаимосвязь. Обра проходит цифры "по порядку". Только стартовая цифра отличиается. Второй - мы всегда можем высчитать из первого :)
Ну что же. Пора потихоньку начинать читать Serial
Нам понадобятся available и read
Начнем новый скетч.
Теперь наша задача - заполнить этот input_buff не с помощью цикла,а кодами символов приходящими из Serial (если они приходят).
Признаком окончания строки в Serial у нас будет символ \n . Для этого мы в Serial мониторе, в нижнем правом углу, чуть левее скорости выберем "Newline". Это означает что когда мы нажимаем кнопку Send, к тому что мы напечатали добивится \n . И наш скетч сможет узнать что "строка принята целиком".
При этом, нам нужно не забыть, что в input_buff - нам уже не нужно сохранять этот \n, а нужно .... ну вы уже догадались :)
Вообщем когда строка примется "целиком". Пусть ардуина ее вернет. Как-то так:
И опять должна приготовится принимать следующую строку.
На всяк случай - это была постановка задачи :)
Уже "реально полезной", а не только "что-бы разобратся".
Пока не могу сообразить.. "бяки" какие-то у меня получаються..
таким образом мы читаем байты с порта?
не могу понять, какое должно быть условие, что бы считать все байты с serial.. (
Или читать все, пока не "упремся" в размер буфера?
Вообщем когда строка примется "целиком".
т.е. пока не встретим в строке \n "конец строки"?
не могу понять, какое должно быть условие, что бы считать все байты с serial.. (
Или читать все, пока не "упремся" в размер буфера?
Я вам ссылку давал available - им вы можете узнать "а есть ли что-нибудь в serial". Вообщем-то можете узнать и "сколько штук есть", но нам достаточно знать что "что-то есть", потому что...
Ну нужно пытатся сразу "считать все байты". Все равно нам Serial.read() возвращает их по одному. Так что у нас задача дробится на
"считать байт", "решить что с ним делать" (положить в буфер, игнорировать, заменить на нулевой символ).
Размер буффера - да нужно будет учитывать. Но... дайте это чуть позже. Пока - просто не будет слать в Serial монитор строки больше размера буфера. Ограничемся посылкой слов "hello", "bla-bla" и т.п.
Вообщем - читайте один байт за один проход loop(), если даже там в Serial еще что-то осталось - не страшно. Следующий байт считается при следующем проходе:
Только, вот сразу из Serial.read() сразу в массив пихать - не очень хорошо. Для большинства символов - действительно "нужно сохранить в буффер", но у нас ведь есть и исключения. Конец строки там как-то обработать... так что, лучше прочитать символ во временную переменную. Посмотреть "что это за символ", и уж на основании этого решать "сохранять его в буфер или нет".
Такой подход "байт за один проход", так же позволит вам что-бы остальная логика работала "как-бы паралельно". Может вы еще диодом, в тоже самое время мигаете. Или кнопки опрашиваете. Если мы сразу будет ждать "всю строку целиком", то в случае когда нам будет отправлять эту строку "меддд-лееее-но.....", у нас скетч встанет "колом". Диод - перестанет мигать, кнопки - опрашиватся. Пока строка не прийдется целиком.
А так - есть символ - обработали - сделали еще что-то полезное. Опять проверили. Есть? Обработали, нет... ну будем пока заниматся другими делами.... Символ конца строки? OK. Теперь посмотрим какая строка у нас "собралась из кусочков".
т.е. пока не встретим в строке \n "конец строки"?
да. символ конца строки, по условия задачи означает "строка пришла целиком, можно начинать что-то с ней делать именно как со строкой". начинаеть ее сравнивать с чем-нибудь, отправлять в качестве "эхо" и т.п.
Не забудте только настроить Serial монитор, что-бы он посылал эту \n. Что-бы она действительно приходила.
Вы если "уперлись" - показываейте свои размышления. Лучше выслушать лишний раз "ну что это за фигня", чем бится лбом о стену.
что первое пришло в голову, так это вот это:
в начале конечно чуть не сломал голову: какого меня посылает компилятор.. С большой буквы setup был и loop.
выводит он мне "эхо" вот только по строчно "валит" и плюсует новые символы..
А дальше у меня ступор.. пробывал с For. но не могу понять какое ему условие поставить. что бы эхо было полной строкой что я ему отправил.. (
и не "строчил каждые" deley(500) "Echo from Arduino:"(
и понял еще одну штуку: если я отправил что-то в serial, условие seial.available уже будет больше 0.
Наверно не совсем я понимал работу железа.. получаеться - если я отправил в сериал, то там эти данные и храняться, т.е. в буфере чипа?
>Наверно не совсем я понимал работу железа.. получаеться - если я отправил в сериал, то там эти данные и храняться, т.е. в буфере чипа?
Да. Пока не считаем его Serial.read() (или буфер переполнится)
>А дальше у меня ступор.. пробывал с For
не нужно.
>вот только по строчно "валит" и плюсует новые символы..
А вы где-то записали \0 в input_buff? Как же Serial.print(input_buff) поймет что "пора остановится"?
Вы где-то проверяете что пришло \n?
Вам же "эхо" нужно выводить только когда "пришел символ конца строки", а не "при каждом проходе loop()" (как сейчас).
Заведите переменную, скажем "char ch", делайте чтение Serial.read() в нее. Если она \n, значит в буффер пора пихануть \0 и вывести эхо, если нет - сохраняем ch в буффер.
в начале конечно чуть не сломал голову: какого меня посылает компилятор.. С большой буквы setup был и loop.
P.S. Кстати в C/C++ принято называть функции с маленькой буквы. Как-бы "джентельменское соглашение". Что-бы все было однотипно и не гадать.
Теперь понимаете почему "придерживатся стиля" - так важно и почему это может часы жизни и комок нервов спасти? ;) Почему я все время пристаю с такими мелочами-гупостями "назовите по другому", "дефайн - большими буквами" и т.п.?
Заведите переменную, скажем "char ch", делайте чтение Serial.read() в нее. Если она \n, значит в буффер пора пихануть \0 и вывести эхо, если нет - сохраняем ch в буффер.
Ладно. Если не выходит. Плюньте пока на буффер. Давайте упростим задачу до:
1. Если пришел байт из Serial
2. Прочитать его в переменную типа byte или char
3. Сравнить эту переменную с \n, если совпало - Сделать Serial.println("End of line detected"); . Если не совпало - ничего не делать.
4. Ждать следующего байта.
ок. занимаюсь..
не пойму, почему у меня Serial.print("I received end line"); выполняеться два раза.. (
подскажите, в каком месте я натупил.. ((
А '10' - это что? Кавычки - говорят о том что это "это символ". Но... я вижу там два символа. так что... вы сравниваете t - не очень пойми с чем. t - содержит код принятого символа. код - "это просто цифра".
Посмотрите на сообщение #151 . Вы писали что "да это понятно".
Мы же раньше уже "устанавливали какой-то элемент массива" зная его код. Тут тоже самое. Только вместо присваивания - нужно сравнить.
P.S. Если "все равно не понятно" и "а точно!!! (и хлопок по лбу)" - не возникло. Говорите. Не убредайте в дебри :) Тут все "ну очень близко".
P.S. И по идее, delay(1000); - тут совсем не нужен. println- будет срабатывать только когда он компа пришел символ "конца строки". А вы же не будете его слать "очень часто". Только когда нажали кнопку Send - следовательно "частить" - не будет. И задержки - не нужны.
И не забывайте. Что вам не нужно "руками" из сериал монитора посылать символ с кодом 10-ть. Нужно настроить сам сериал монитор, что-бы он посылал его в конце каждой строки автоматом:
в Serial у нас будет символ \n . Для этого мы в Serial мониторе, в нижнем правом углу, чуть левее скорости выберем "Newline". Это означает что когда мы нажимаем кнопку Send, к тому что мы напечатали добивится \n . И наш скетч сможет узнать что "строка принята целиком".
я не мог понять, почему у меня выполнялось условие даже без кавычек..
А вы наехали на меня за эти кавычки.. ))
>А вы наехали на меня за эти кавычки.. ))
Ну так ведь и компилятор "спотыкается" на первой ошибке, выбрасывает ее и ниже по коду уже не смотрит :) Я тоже привык смотреть "глазами компилятора" :) Нашли одну - исправили, если не заработало - ищем дальше :)
>Если выполняеться "Serial.print", значит условие в if возвращает "True"?
да.
Только чуть-чутьп о стилистике (форматирование кода, отступы).
Плохо:
Хорошо:
Все вложенные блоки - отодвигаем вправо. Иначе вы очень быстро запутаетесь какая фигурная скобка что закрывает. Что выполняется внутри первого if-а, а что второго и т.д.
OK. Next.
Сейчас мы сделали "если пришел символ конца строки, то делаем print", пусть это так и останется, но добавится "а если пришедший символ какой-то другой, не конец строки, то ложим его в буфер и увеличивает индекс буфера". Тут вам понадобится if...else
Половина его у вас уже написана в #170, а вторая половина, на 80% написана в №162 (строки 10,11). Осталось только "слепить вместе".
Правильно?
>Правильно?
Пока да. Только Serial.print почему-то в лево уехало :) (вообщем с форматирование уже намного лучше, но еще "не идеально").
По именам. i - неудачное название. Традиционно i - используется в качестве счетчика в цикле for. Поэтому любой читающий воспринимает что где-то снаружи есть цикл for. В третьих такие короткие имена для глобальных переменных - потенциальный источник больших мозголомок. Вот будете вы потом склеивать этот код с каким-то другим скетчем. И там будет тоже использовать переменная i (для совершенного других целей). И все сломается. И заметить почему "все идет не так" - будет очень трудно.
Вообщем гораздо лучше было назвать inputBufIndex - сразу понятно что это за хрень (может и буферов у нас несколько будет?)
Ну и втретьих. Индекс же у нас не может быть отрицательным. Поэтому для него int - неудачный тип. Нужен либо byte (если мы заранее знаем что буфер не будет больше 255), либо unsigned int (вдруг мы потом увеличим буфер)
Вообщем-то как-то так:
Заменил t == 10 на t =='\n' - что-бы легче читалось. вдруг кто-то не помнит какой код у переноса строки? ;) Но это - вкусовое "t == 10" - тоже вполне нормально.
>Правильно?
Ох... проглядел. Я же сказал что "80% написана в №162"
Но нельзя же просто "брать отуда и вставлять не глядя".
Вот скажите, что у вас делается в строках 09 и 14? Какие действия происходят в этих строках?
Попробуйте перевести их "на русский язык" (сюда).
Нужно было:
"а если пришедший символ какой-то другой, не конец строки, то ложим его в буфер и увеличивает индекс буфера"
А у вас вышло
"если пришедший символ как-йто другой, не конец строки, то читаем и ложим в буффер следующий символ" (причем в слепой вере что следующий символ успел прийти).
Представте себе что Serial это такая труба с шариками. разноцветными. С той стороны компьютер "засовывает" в эту трубу шарики разного цвета. Каждый вызов Serial.read - достает очередной шарик из трубы в вашу руку (переменная t). И на основании его "цвета" вы решаете что делать.
Выполнить какое-то действие если шарик "правильного цвета" ( \n ) или положить ЕГО в коробку (в буффер).
все написал в комментариях :)
О... другое дело. Непонятно зачем только вы условие с t=='\n', переделали на t!='\n' - но можно и так :)
Хотя я бы, все-таки рекомендовал вернутся к t=='\n'. А ну как нам нужно будет обрабатывать еще какой-нибудь "спец символ"?
Тогда мы сможем легко добавить еще один if.
Вообщем, в данном случае лучше сохранять в буфер по признаку "все остальные символы...", чем "если не \n"
OK. Поехали дальше. Символы в буффер мы научились складывать. Но что-бы "буффер/набор символов" превратился в "настоящую C-шну строку" нужно еще что-то в этот буфер пихануть. Что-то что-бы функции типа Serial.print могли догадатся где строка закончилась.
P.S. Если я слишком "туманно" изъяснился, если СРАЗУ не поняли что еще нужно сделать кроме "кричим вот он" - говорите.
когда мы увидили "шарик тот что нам надо" пишим в последнюю не заполненную ячейку "0"(окончание строки в масиве" ). Проверяем перед "кричим вот он" есть ли в последней ячейке "0". да - кричим. нет - идем дальше по коду.
правильно я понял?
когда мы увидили "шарик тот что нам надо" пишим в последнюю не заполненную ячейку "0"
Верно, а теперь давайте в коде это увидим.
(окончание строки в масиве" ). Проверяем перед "кричим вот он" есть ли в последней ячейке "0". да - кричим. нет - идем дальше по коду.
А это уже вы пошли наводить "тень на плетень". Зачем нам проверять есть ли в последней ячейки ноль, если мы только что туда сами его записали?
Смотрите, мы увидели что "шарик правильный" (пришел символ конца строки в терминах Serial).
На это мы делаем два действия:
1. Сохраняем в буффер символ конца строки (только уже "сишный", не \n который пришел, а "просто ноль". правильно вы сказали).
2. "Кричим".
Нужно будет - можем еще светодиод зажеч. Вообщем мы поймали событие "пришел правильный шарик", и по этому событию делаем любое нужно нам количество действий.
Вообщем это вы уже пошли придумывать сложности которых нет. Кроме записи нуля в буффер - пока ничего не нужно (в рамках текущего задания).
Оно. Можно было
Как-то оно короче выглядит :)
Ну что-ж. Теперь у нас в input_buff лежит "правильная сишная строка". И ничто не мешает нам ее вывести. Реализовать то что мы хотели сделать изначально в #154. Вместо "I received end line" вывести нашу "полученную строку".
Попробуйте сделать это.
После этого сразу начинать думать над вопросом "а если начнет передаватся вторая строка, куда она начнет записыватся?"
Ну как-бы. Правда не все коментарии мне понятны :) Но то уж фиг с ним. Главное что вам понятно.
К коду - притензий нет (сами-то проверили, хе-хе, работает?) :)
OK. Смотрим дальше. А что будет если к нам прелезет строка символов на 50-т? Памяти-то мы выделили только на 20-ть. Значит дальше оно выйдет за границы буффера и пойдет записыватся "куда-попало" (и может скрешить нам весь скетч).
Выделить больше - не вариант (сколько бы не выделили, всегда может прийти еще длиней).
Конечно если "не влезает", то принять длинную строку мы не сможем. Но нужно что-бы хотя-бы к катастрофе это не приводило. Проверять еще и эту ситуацию.
Тут у нас есть два варианта. Если "буфер уже полон", то....
1. Следующие пришедшие символы читаем, но никуда не записываем. Тупо теряем их.
2. Начинаем опять писать "в начало буфера" (делаем inputBuffIndex=0). Новые пришедшие символы буду записаны поверх ранних.
В одном случае мы потеряем "хвост" длинной строки, в другом "ее голову". Какой вариант вы выберите - не суть важно. В любом случае эта строка "обкоцалась". Будет ждать следующию нормальную :)
Хотя есть еще одни вариант. Интерпретировать "буфер полон" точно так как и "пришел символ \n". Тогда мы ничего не потеряем. Просто длинная строка "разобъется на две строки".
Но, учитывая что мы будем делать дальше, я бы предложил все-таки выбрать из двух вариантов "лишнее просто теряется".
Тогда в Else добавить проверку текущего индекса inputBuffIndex с INPUT_BUF_SIZE. если равен тогда сбросить на 0?
Да. Это вариант "теряем голову длинной строки". На всякий случай можно проверку сделать менее строгой
Если мы где-то в другом месте ошибемся и еще где-то случайно сделаем inputBuffIndex++ (хотя не должны)
Вариант "теряем хвост" выглядел-бы так
Вставте эту "защиту от переполнения" в скетч (любой ее вариант). Дайте его сюда целиком.
Хорошо. Вроде работает. Вроде все безопастно :)
Но код потихоньку начинает становится "тяжелым". Уже в одни взгля не поймешь что тут происходит (еще можно, но...).
Как минимум у нас получился винигрет логики которая отвечает за "чтение и собирание строки" с логикой "что делать когда строка собрана целиком" (кричать, выводить и т.п.)
Способ борьбы со сложностью мы уже знаем - разбивание на функции.
я щас быстренько сам вынесу в функцию, просто словами и образами я буду неделю объяснять "что я хочу", "кодом" - оно зачастую легче. что вам "самому сделать" - еще будет :)
Итак, что я хочу:
1. Что-бы функция занималась приемом строки - и ничего более
2. Что-бы функция возвращала значение true или false в зависимости от того "Готова строка" или еще нет.
3. inputBuffIndex - убрать из глобальных переменных. Так как использоватся она должна только внутри функции. "Внутреняя кухня". Всем остальным нужно только знать "готова строка или нет".
Функцию назовем isLineRead() и будем вызывать ее из loop()
Заодно... как и в прошлом скетче сделаем для Serial дефайт GPRS . Нам же их потом "лепить вместе", поэтому желательно "в одном стиле".
Посмотрите. Есть вопросы по этой трансформации или "все очевидно"?
Кстати тут уже немного просматривается "слишком много коментариев". Если какие-то строки уже вам уже "ну совсем очевидны сами по себе" - можете чуток потереть коменты (или сделать их менее многословными). А то уже за коментами код теряется :) Каждую строку коментировать - это тоже не очень хорошо. Я просто стараюсь "объяснять подробней", но в итоговом коде - лучше так не оставлять.
Вопросов по трансформации нет. Все действительно очевидно. Особых изменений нет.
OK. Тогда давайте еще одну "ситуацию" предусмотрим. Пока мы предполагали что из управляющих символов нам приходит только \n
Но это бывает далеко не всегда (на разных операционках, в разных кодировках, разные модемы и т.п.) иногда в конце строки посылают сразу два символ \r\n . Еще и "возврат карретки" приходит. (кстати Serail.println() - в конце именно "\r\n" добавляет, а не "\n")
Сверх-страшного - нечего не слушится. \n в конце все-таки есть и мы нормально отработаем "конец строки". Но, вот этот \r - попадем нам внутрь нашего буффера. А он нам - нафиг не нужен.
Можете проверить что "он туда попадает" таким способом. В сериал мониторе. Внизу. Чуть левее скорости, выставте вместо "Newline" (добавлять в конце \n), ставите Both NL & CR (добавлять в конце \r\n). И посылаете скетчку строку "12345678901234567890" (тут ровно 20-ть символов). Вроде "должно влезть" в наш буффер. А не "влезет". Обрежетеся (увидите пустую строку). Именно потому что \r лишний затесался и на самом деле строка вышла 21 символ.
Вообщем нужно, кроме "детекта" \n, еще обрабатываться ситуацию когда приходит \r . В этом случае нужно ..... просто ничего не делать (не сохранять, счетчик не увеличивать... вообще ничего). Тогда наш счетч будет иметь одинаковое поведение в не зависимости от того в каком виде нам шлют концы строк.
Попробуйте сделать это. Тут можно конечно впихнуть еще один if внутрь ветки else, но постарайтесь сделать это с помощью конструкции описанной тут Оператор If..else | Аппаратная платформа Arduino (во втором примере видно эту "конструкцию").
И еще, пока не забыл. О том как С/C++ делает привидение к boolean типу. Если где-то ожидается значени true/false (HIGH/LOW). В if условии, булевской операторе и т.п. А мы туда "подали" что-то другое (какое-то число, строку, еще что-то), то компилятор действует по логике: если это "что-то" является нулем - считаем его false, все остальное - принимаем за true.
Это позволяет условия вида
Писать более краток
Если available() вернет что-то отличное от нуля - if сработает. В таком виде не тольое более кратко, но оно даже как-то ближе к естественному языку. Легче читается. почти дословно "если в GPRS есть доступные символы...", вместо "если в GPRS количество доступных символов больше 0".
Это ОЧЕНЬ распространненая форма записи. Привыкайте так писать. И что-бы легче понимать когда в чужом коде встерите, и что-бы ваш код не выглял как "чайниковский" :)
Вот еще еще пример где часто это можно встретить. Пишут
Вместо
или
вместо
(тут используется еще и тот факт, что HIGH/LOW это синонимы/псеводонимы true/false) Вернее синонимы нуля и единицы.
Если загляните в файлик Arduino.h (он подключается ко всем вашим скетчам, незаметно/автоматом) то поймете откуда растут ноги этой
синонимичности, там так это объявлено
Поэтому HIGH/OUTPUT/true/1 полностью "взаимозаменяемы".
Но что-то это меня уже понесло в строну моря :)
Вообщем
Смотрится лучше :)
Кстати по поводу #define вспомнился старый прикол
#define true false //счастливой отладки, суки..., ))))))))))))))))
Не, вы тут сделали условие !='\r', а нужно было "ловить ситуацию когда t=='\r'"
Что-бы вышло нечто типа "если t равно "значение1" - делаем одно, если t==другое делаем второе, если t==третье... и т.п. если же ни одно из условий не сработало ("во всех остальных случаях"), то..... сохраняем в буффер
Тогда, если нам понадобится обрабатывать еще какие-то сутиации, мы можем однотипно выписывать
До бесконечности. Ловить разные "ситуации" сколько нам нужною.
В вашем же варианте, последний if будет очень замудренным. Скажем поднадобится вам делать на \r не "ничего", а "вывести Serial.println("Carriage return detected");" можно будет просто вписать println не меняяя логики.
Я просто не мог понять как "ничего не делать" при условии t == '\r'.. :)
В принципе "правильно поняли". Можно в более явно виде написать "пустой блок кода":
Что-бы не привыкать лепить точку запятой после if. Вы, кажись один раз, уже имели развлечение с такой закравшеся точкой запятой.
OK. Конструкцию "if else if else if else" ... рассмотрели. Это типичный паттерн когда нужно "проверить по очереди условия и выполнить что-то только по первому совпавшему". Ее имеет смысл применять когда сами условия if - какие-то "сложно-разнообразные". Для ситуации когда все условия имеют вид if(переменная==ЗНАЧЕНИ1) else if(переменная==ЗНАЧЕНИ2) else if(переменная==ЗНАЧЕНИ3) else, то есть когда мы ищем "с чем совпадает переменная (одна и таже)" есть специальная, более специализированно-компактная конструкция языка switch case (если внимательно читали if...else там в конце было примечание про нее).
Попробуйте теперь функцию isLineReady() что-бы использовался switch, а не if-else
P.S. Я собственно потому все время и "упирался" вашим попыткам сделать if(t!='\n')и if(t!='\r'). Потому что логику использующу "не равно" - трудно потом "переделать на swith", а вот когда все условия имеют вид if(t=='...') - не должно вызвать проблемм. Логика остается такой же, только чуть-чуть синтаксис меняется.
Супер. Только форматирование "чуть-чуть" поехало. case влево ушли. А так - "то что доктор прописал".
Кстати, всю эту функцию можно рассматиривать как "типичная заготовка для приема-обработки чего-то из сериал".
Вот предположим у нас-бы стояла задача принимать буквы из Serial. На буквы a/b включать/выключать LED1, а на c/d - LED2
Мы бы написали что-то типа:
Как видите "структурно" - очень похоже на то к чему мы пришли собирая строку в буффер. Так что этот "паттерн" - он довольно универсален.