Перевод принятого символа с com-порта в число
- Войдите на сайт для отправки комментариев
Сб, 28/04/2012 - 00:37
Как перевести символ(заведомо число) с com-порта в число на ардуино? желательно пример кодом привести. Заранее благодарю
Arduino.ru -> Программирование -> Преобразование типов данных. Непосредственно символ можно преобразовать не в число, а в цифру. Символы можно собрать в строку т.е. массив символов, а строку в свою очередь перевести в число. Попробуйте так, сейчас проверить не на чем, поэтому не знаю как будет работать.
Но может есть способ и проще.
сомневаюсь, что приведенная функция будет работать в исходном виде.
Если я правильно понимаю проблему топикстартера, число вводится на компьютере ручками в каком-нибудь терминале и отправляется микроконтроллеру (программным способом всегда можно отправить число в бинарном виде и не насиловать мозг). Терминал, отправляя строку, завершает ее символом конца строки, а то и двумя - конца строки и возврата каретки. Эти символы принимающая программа должна уметь отсеивать. Это раз.
Строки в си представляют собой последовательность ячеек памяти, в которых хранятся отдельные символы. Эта последовательность должна завершаться ячейкой с признаком конца строки - 0x00. Без такого завершения строки любая строчная функция будет обрабатывать "строку" вплоть до падшей восьмерки, то бишь до бесконечности. Ну, или случайно встретившейся на задворках памяти пустой ячейки. Естественно, с непредсказуемым результатом. Это два.
Но подход в целом правильный. Хотя и не единственно возможный.
Я всегда делал так:
И не использовал преобразование типов данных, но если есть уже такие функции, то на них в первую очередь и ссылаюсь.
Предположим, у нас на входе число 1 (строка, состоящая из единственного символа "1").
Условие if(a >= 48 && a <= 57) истинно (a=49) и мы попадаем в блок расчета числа b:
b += a - 48; => b=0+49-48=1;
b *= 10; => b=1*10=10;
Упс... получили число в 10 раз большее переданного.
Я бы исправил это, поменяв операторы местами:
b*=10;
b += a - 48;
Вы не внимательно смотрите код - по завершению чтения из буфера окончательное число делится на 10 , 10/10 = 1.
Но действительно, что бы убрать деление на 10 в конце, можно поменять местами строки, для оптимизации кода:
Теперь буду делать так.
Спасибо. На днях попробую, по результатам отпишусь
есть функция atoi в Cи для перевода из символв в цифры.не знаю как онамно другие Си'шные функции рабоали в Аrduino IDE хорошо
обьясняю :
если брать данные споследовательного порта,то как то так :
Хотя нет компилируется, только если аргумент массив.
то есть по вашему функция Serial.read(); не возвращает константу?)))а что же ,по вашему, она возвращает?
обьясняю;
в очень короткий промежуток времени происходит следующее :
функция Serial.read(); считывает то что было на последовательном порте.эти данные становяться константой,котоую функция atoi() переводит в integer.
так не компилируется т.к. Serial.read() возвращает не указатель.
Вот так компилируется:
а если усложнить задачу и, например, будет с ком порта приходить символ и цифра: f500 - f будет рассматриваться как флаг на выполнение какой то операции, а 500 - как длительность выполнения данной операции. Как тогда разложить на символ и число в две переменные?
А так же как и с минусом
>то есть по вашему функция Serial.read(); не возвращает константу?
Конешно. Фунции вообще не умеют возвращать константы. Они умеют возвращать только значения определенного типа.
Конкретно эта фунция возвращает значение типа int.
>Только проблема в том, что аргумент должен быть const
не путайте констаны и ключевое слово const. Что оно означает - зависит от контекста его применения. В данном случае он просто запрещает функцие atoi менять переданный параметр внутри себя. Но вы спокойно можете передавать, в качестве параметра - любые переменные. То есть если вы вызвали atoi(myvar);, то вы уверены что myvar - не изменило своего значения.
Правда если переменная "указатель", то функция не сможет поменять "куда он указывает", но будет способна изменить значение по этому адресу. const от этого защитить не сможет.
>данный код переведет все цифры из массива символов в массив целых.
Данный код даже не скомпилируется. Так как atoi ждет УКАЗАТЕЛЬ на массив типа char, причем заканчивающийся символом конца строки \0.
Не говоря уже про то, что непонятны зачем вы пытаетесь ей скормить каждый символ отдельно, если ее назначение - парсить строку целиком. Почитайте ее описание.
Что бы перевести один символ в целое, достаточно из его кода вычесть 48. Что с успехом уже делалось в скетчах выше.
Вообщем вот так выглядит перевод массива кодов, в целое
Ну а с serial. Нужно будет, завести какой-то массив-буффер. Вычитывать из serial байты по одному и заполнять этот массив. Следить, при этом что-бы массив не переполнился. Не забыть добавить в конце нулевой символ (если он не приходит из порта).
А потом уже atoi кормить этот массив.
Ну и, напоследок, уже включая откровенного зануду
>есть функция atoi в Cи
В языке Cи - нет такой функции. Это библиотечная функция. Библиотека cstdlib . Стандартная библиотека, согласен, но это не часть языка.
И еще, в последнем случае если в строке будет хоть один "левый" символ, то функция возвращает 0 так что b123 - не прокатит. А в предпоследнем (когда char a[1]) 123b - получим 1230.
А почему вы не применяете метод ToInt класса String String::toInt?
Вот есть даже пример стандартный:
А так же как и с минусом
В этом варианте у меня не выходит правильно запустить (((
получаем с ком порта например f500 -> если f то, например, зажигаем светодиод с delay(500!!) (500 которое пришло с ком порта) и затем гасим его. И в добавок заставляем мигать его с данным делеем, повторяя процедуру несколько раз.
Так вот получилось добиться только того, что зажигается только 1 раз с заданным интервалом (((( более не воспринимает(
Так конечно, вам нужно delay(500!!) вынести за цикл чтения из ком порта, так же как и с минусом - minus это свего лишь флаг, а действие, которое при изменении этого флага будет выполняется полсле чтения из ком порта:
А почему вы не применяете метод ToInt класса String String::toInt?
Вот есть даже пример стандартный:
А вы сами этот пример проверяли?
Так конечно, вам нужно delay(500!!) вынести за цикл чтения из ком порта, так же как и с минусом - minus это свего лишь флаг, а действие, которое при изменении этого флага будет выполняется полсле чтения из ком порта:
Делал примерно также, но почему то не завелось))) Ваш код работает! Спасибо огромное)) С меня пиво! )))
Буду дальше педалить, если что отпишусь!
Либо вот так:
а без while как то можно организовать? а то у меня случается подвисание в ожидании символов с ком порта....
Можно, только это не решит проблему т.к. пока не закончится чтение из коп порта никаких действий происходить не будет, так же как и с циклом. Наверное все-таки будет правильнее решеть проблему с зависанием...
А почему вы не применяете метод ToInt класса String String::toInt?
...
А вы сами этот пример проверяли?
Проверял, у меня работает.
Это если еще в конце числа послать '\n'
Это если еще в конце числа послать '\n'
Функция прекращает работу на любом символе, отличном от цифры
Нет, только если '\n' к конце, а так даже 12d456 возвращает 12456. Хотя смотря что вы имели ввиду под словом "прекращает"... В общем можно сделать вывод числа после цикла чтения, а в целом этот вариант тоже приемлем.
Мне даже интересно стало (по поводу нюансов) - сейчас в железе проверю :)
Я как раз эту функцию предполагаю использовать дл яразбора строки терминала...
Да, кстати, и отрицательные числа возвращаются положительными.
Отрицательные возвращаются отрицательными, прекращает работу на любом нечисловом символе, как я и говорил, просто в тестовом скетче нужно заремарить фильтрацию цифр - строчку if (if (isDigit(inChar)) {, иначе до функции доходят, мягко говоря, не все символы :). Вот распечатка терминала:
http://arduino.cc/en/Reference/StreamParseInt
Так как Serial, наследуется от Stream, То можно использовать Serial.parseInt()
Может быть. Но я имел в виду класс String arduino.cc/en/Reference/StringObject, кстати, там нет описания toInt, но всегда бывает полезно заглянуть в заголовочный файл, чтобы узнать, что вообще этот класс может hardware\arduino\cores\arduino\WString.h
Либо вот так:
Решил воспользоваться этим примерном для управления положения сервы и тут возникла проблема....
При отсылке в ком-порт как программно так и через обычные терминалы когда шлю значение допустим r50 он мне выдает r52 и при посылке r51 число становиться уже 62 тоесть плюсует 10.... такое заметил только от 50 до 60 когда посылаешь
Как это можно побороть что бы все было как послал ? И с чем это связано ?
Да, очень странный глюк, попробуйте так:
Да теперь значения от 0 до 180 все посылаються нормально
Огромное спасибо
Спасибо большое, последний пример помог.
Можно ли в строке как-то отыскивать символы?
Допустим чтобы в строке f1r200q500 символы разделяли бы цифры, присваиваемые переменным (символ f, например, отвечал бы за вращение двигателя в ту или иную сторону (f1, f2), r отвечал бы за шаги (200шагов), q определял бы частоту вращения (задержка 500)).
Вариант с последовательными командами скорее всего отпадает, т.к. он будет отправлять в три раза больше команд (если отправлять поочереди f1, r200, q500).
С уважением, Иосиф.
В строках 18 и 19 попробуйте f=1 и r=1 заменить на f=Serial.parseInt(); и r=Serial.parseInt();
Только тогда, после последней отправляемой цифры нужно еще слать какой-нибудь не цифровой символ (что-бы оно знало что "цифры закончились"). Если вы шлете это "руками из Serail монитора", то можно просто включить режим Line Ending CR (внизу справа, чуть левее скорости порта).
хм.. а имеет ли смысл строить сложные алгоритмы по декодированию строки на приёмной стороне (которая довольно ограниченна по ресурсам)? не проще ли разделить инфу на строгие кадры, где будет заведомо известно: где имена переменных, где их значения, где дополнительные команды и т.п.? наверняка возникнет некоторая избыточность передаваемых данных, но это позволит проверять подлинность инфы (особенно актуально при ручном наборе в терминал).
хм.. а имеет ли смысл строить сложные алгоритмы по декодированию строки на приёмной стороне (которая довольно ограниченна по ресурсам)?
А в чем их сложность?
не проще ли разделить инфу на строгие кадры, где будет заведомо известно: где имена переменных, где их значения, где дополнительные команды и т.п.? наверняка возникнет некоторая избыточность передаваемых данных, но это позволит проверять подлинность инфы (особенно актуально при ручном наборе в терминал).
Нет. Не проще. Проще будет если перейти на бинарный протокол, тогда все что вы сказали - имеет смысл. В случае же если "руками шлем команды", то все равно нужно будет искать какой-то маркер "начала-конца команды", все равно нужно будет переводить строки в числа. Только очень сильно повысим риск "ошибки оператора". Он будет должен
И главное - не понятно ради чего все. Попробуйте все это реализовать в виде кода и сильно подозреваю что он не выйдет короче (а возможно и длиней) чем изначальный вариант.
Так что выбор обычно стоит между "бинарный формат - простой код" и "человеческий формат - танцы с конвертациями и парсингом".
тогда ещё вопрос по приведённому коду, есть ли какие либо преимущества в сборе буфера Serial в массив char перед сбором в одну переменную типа String? Строку вроде как нагляднее и проще анализировать, если команды длиннее одного байта.
а про терминал. врятли это итоговое решение, в результате будет какое либо приложение, которое и будет слать данные, а приложению уже всё равно что и как слать. Кадровую передачу данных я попробую воплотить, хотя бы ради того чтобы попробовать.
А почему вы не применяете метод ToInt класса String String::toInt?
Вот есть даже пример стандартный:
Отлично работает! Спасибо большое!
Только пример не очень удачный.
Вообще-то должно работать просто
А вообще, имена переменным не принято такие давать - отучайтесь!
Вообще-то должно работать просто
В моем примере надо было из определенных символов массива получить int. Тем не менее спасибо.
А вообще, имена переменным не принято такие давать - отучайтесь!
Это всего лишь пример! Никто их так не задает.
А можно пару слов (и пару строк) об этом моменте? Предполагается передавать между двумя ардуинами некоторый односторонний протокол: цифры разеделенные каким-то символом. Что-то вроде этого:
Как на ардуине-приемнике разобрать такую строку?
Спасибо.
Как на ардуине-приемнике разобрать такую строку?
Вестимо, по разделителям. Например, юзая strchr, или просто вычитывая до разделителя в буфер, и как только вычитали очередное значение - чего-то предпринимать.
Один добрый человек подсказал вот такую функцию, очень удобную и простую. За что ему и спасибо!
Один добрый человек подсказал вот такую функцию
Жесть. Можно сильно проще. Но раз это работает - пущай.
Собственно источник - http://arduino.stackexchange.com/questions/1013/how-do-i-split-an-incoming-string
Там же обсуждение и различные методы, выбираем, изучаем и творим )