Serial.read в ардуино

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

ntdfish пишет:

Конкретно про serial мне многое не понятно: размер буфера serial, как в буфере накапливаются символы, как буфер очищается и тп.

Это Вам надо про FIFO и прочие основы работы UART-чипов почитать. Писатели документации на ардуину не могу все начинать "с нуля". :)

ntdfish пишет:

А вы кстати не пользуетесь обычным компилятором Си для отладки? Просто с ардуины тяжело получить обратную связь.

Как "тяжело"? Serial.print в программном коде и пишите отладку через сериал-консоль...

capt_smile
Offline
Зарегистрирован: 18.12.2012
void setup()
{
  Serial.begin(9600);
  Serial.println("Start");
}

void loop()
{
  ReadSerial();
}

void ReadSerial()
{
  int i=0;
  char mas[32];
  boolean data = false;
  memset(&mas,0,sizeof(mas));
  while(Serial.available()>0)
  {
   mas[i]=Serial.read();
   i++;
   data = true;
  }
  if (data)
  {
   Serial.println(mas);
   data = false;
  }
  delay(500);
}

короче удалось сделать только таким способом. без delay не работает :(

step962
Offline
Зарегистрирован: 23.05.2011

capt_smile пишет:

а какая-же функция на самом деле будет очищать буфер?

ну или какой хитростью мне достигнуть результата? нужно считать символы из сериалМонитора в массив и вывести его

Так считайте их в массив! И выведите!

Пока - судя по скетчу - вы лишь проверяете их поступление...

Где вызов функции Serial.read()?

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

 

Мы сделать, фактически, делаем вывод массива символов. То есть работает как с C-шной текстовой сторокой. У которой ОБЯЗАТЕЛЬНО должен быть признак конца строки - нулевой байт. IMHO полагатся тут на memset - плохая идея, лучше делать  mas[i]=0 после заполнения массива.

А еще нужно обязательно проверять что i<31  (помнить что еще один байт нужен под ноль).

 

 

 

 

serenya
Offline
Зарегистрирован: 17.02.2012

Подскажите пожалуйста как сделать такое, по UART приходит строка вида "-12,3", это данные о температуре, как мне эти данные разложить по символьно, каждый в свою переменную, что-бы на выходе получить такое?

sayi_d1 = -
sayi_d2 = 1
sayi_d3 = 2
sayi_d4 = .
sayi_d5 = 3
maksim
Offline
Зарегистрирован: 12.02.2012
if(Serial.available() > 5)
{
  sayi_d1 = Serial.read();
  sayi_d2 = Serial.read();
  sayi_d3 = Serial.read();
  sayi_d4 = Serial.read();
  sayi_d5 = Serial.read();
  
  while(Serial.available()) // очищаем буфер
  {
    delay(2);
    Serial.read();
  }
}

Но я бы на вашем месте воспользовался массивом и заполнял бы в цикле.

serenya
Offline
Зарегистрирован: 17.02.2012

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

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

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

"Избегать дублирования" - первейшая заповедь. Ну ведь банально должно быть лениво копи-пастать строчки. Ну или, предположим, после Serial.read() вы еще и что-то сделать с этим байтом захотите? 5-ть строчек править? Или вывести это все в Serial, опять 5-ть одинаковых строк делать? А если завтра потребуется не 5-ть, а 15-ть?

Тем более, что перейти на массив - принципиальной разницы - нет. Нужно просто не боятся, пойти в раздел проргамирование и почитать про массивы. Никуда вы от них не денетесь, если собрались програмирование ардуины осваивать.

А перейти к работе с массивом можно было-бы так (тот же самый код, пока с дублированием)

byte sayi_d[5];  // объявляем массив на 5-ть элеметов
if(Serial.available() > 5)
{
  sayi_d[0] = Serial.read();
  sayi_d[1] = Serial.read();
  sayi_d[2] = Serial.read();
  sayi_d[3] = Serial.read();
  sayi_d[4] = Serial.read();
  
  while(Serial.available()) // очищаем буфер
  {
    delay(2);
    Serial.read();
  }
}

Сильно сложней?

Зато теперь, можно избавить от дублирования, как и говорил Максим "читать циклом".

 

byte sayi_d[5];  // объявляем массив на 5-ть элеметов
if(Serial.available() > 5)
{
  for(byte i=0;i<5;i++)sayi_d[i]=Serial.read();
  
  while(Serial.available()) // очищаем буфер
  {
    delay(2);
    Serial.read();
  }
}

Осталось только избавится от магической цифры 5-ть. Написать где-то вверху скетча

#define SAY_TOTAL 5 

И поправить код в такой вид:

byte sayi_d[SAY_TOTAL];  // объявляем массив 
if(Serial.available() >= SAY_TOTAL)
{
  for(byte i=0;i<SAY_TOTAL;i++)sayi_d[i]=Serial.read();
  
  while(Serial.available()) // очищаем буфер
  {
    delay(2);
    Serial.read();
  }
}

И все.

Теперь, если нам нужно будет не 5-ть, а 25-ть читать, мы исправим всего одну строчку. Вместо "#define SAY_TOTAL 5"  напишем "#define SAY_TOTAL 25

А сам код - даже менять не нужно будет. Будет работать с большим количеством.

serenya
Offline
Зарегистрирован: 17.02.2012

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

bms
Offline
Зарегистрирован: 23.04.2013

Такая проблемка. Хочу ардуиной подключиться к телефору по СОМ-порту (0 и 1 пины). Пока пробую отработку команд при помощи  монитора порта. Есть такой кусок кода, где нужно подключиться к порту, отправить ему команду "АТ" и получить "ОК" (который на текущий момент я сам руками ввожу в мониторе), и потом, если всё прошло успешно продолжить посылать в порт комманды и получать "ответы". Дошёл до момента, когда полученный ответ (в данном случае "ОК") нужно обработать (т.е. сравнить) и уже дальше что-то делать (например, зажечь светодиод). Операция сравнения не получается. Видимо полученный "ОК" не такой.

int ledPin = 13; // мигаем если всё ОК
int xBytes = 0; // количество получаемых байт информации

void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
Serial.println("AT");
while(!Serial.available()) {}
delay(250); // без задержки Serial.available() получает только 1 байт
xBytes = Serial.available();
 int i;
 char message[xBytes]; // будущая информация "из порта" (в байтах символов)
 for (i=0; i<xBytes; i++) {
  message[i] = Serial.read();
 }
if (String(message) == "OK") {
 digitalWrite(ledPin, HIGH);
 }
else {
 digitalWrite(ledPin, LOW);
 Serial.println("Error!");
 }
Serial.flush();
}

Кусок всего кода выше, а вот момент который меня смущает:

if (String(message) == "OK")
 

Вставлял в разных местах и Serial.println(String(message)) и Serial.println(message) для проверки того, что там получилось. Вроде бы "ОК" как "ОК". Но где-то косяк, видимо с переводом строки или 1 битом в конце. Как это "победить"?

Вопрос "в догонку".... а помёт ли в будущем телефон посылаемую "АТ" команду, а то сомнения какие-то вдруг появились.

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

Serial.println - вам будет не достаточно. С ним вы не видите "невидимые символы" (символы с кодами меньше 32 Ascii Table - ASCII character codes and html, octal, hex and decimal chart conversion). Так что, что-бы видеть действительно свою строку - вам нужно по этому message пробегатся циклом и выводить именно коды символов, а не сами символы.

После 14-той строки вставте 

Seria.println(message[i],DEC); Serial.print(",")

Что-бы увидеть все что там есть на самом деле.

И скорее всего вы увидите (как вы и заподозрили), в конце два лишних кода 10 (новая строка) и/или 13 (возврат каретки).

Естественно - их нужно выбрасывать перед тем как начинать работать с полученной строкой. Но не просто выбрасывать - а заменять на символ окончания строки - символ с кодом 0. Что-бы ваш message[] стал корректной Си-ной строкой. Что-бы было понятно где ее конец. А без этого все строковые функции - будут работать только в случае везения.

Вообщем не помню сколько постов назад я уже упоминал, что "в топку String". Ну не родной он для Си. Вроде как "проще в использовании", а жрет память и постоянно туда-сюда конвертировать приходится. Так что его просто-та - обманчива. Как видите разбиратся с string все равно нужно. Даже если потом вы планируете конвертировать его String. Все равно "понимать нужно" и в итоге простота оборачивается "маскировкой грабель". А если вы сделали нормальную сишную строку, то непонятно зачем вообще String нужен. Две сишные строки уже прекрасно стандартная функция strcmp сравнить может.

И кстати подход "читаем сколько байт в буффере" - потенциально глючный. Строка может и не успеть передатся целиком к моменту проверких Serial.available.  Лучше подходить так:

1. Если в Serial есть хоть что-то - читаем 1 байт.
2. Если он \r - тупо игнорим его
3. Если он \n - заменяем его на символ нуля (и ложим в буффер), считаем что строка считалась целиком и начинаем работу с ней
4. Если он что-то другое - сохраняем его в буффер и ждем следующего символа.

bms
Offline
Зарегистрирован: 23.04.2013

Сделал проще. В принципе мне нужно по сути только ловить "ОК" от "противоположной стороны"... её я получю как long int a = 7975  (которую получаю в цикле как message=message*100+Serial.read(); ), т.е. как перевод символов в разные кодировки: О->79 и К->75.

Походу дело сразу же трабла (так сказать "на будущее" - строка же может быть и длинной, и переменная, определённая как long int не достаточна, какую использовать?).

Далее обычным if (a == 7975) {} else {} определяю "что с этим делать". В мониторе порта в Ардуине всё нормально работает. Затем я подключился к Ардуине Huperterminal-ом (штатным из Windows XP).

Всё что я отправляю как Serial.println() (например,  Serial.println("AT")), он получает, переводит курсор на новую строку.... но "ответить" мне не даёт... я не могу написать ничего в терминале. От чего вопрос.... либо необходим символ/команда, которая бы разрешила мне ответить (перейти в режим отправки/слушания), либо....???

Тогда же как ответит устройство, к которому я собираюсь подключиться???

Может нельзя подключаться к Ардуино через "тоже соединение, что и программирую", хотя он видит стандартный СОМ-порт, который как я понимаю полный аналог Rx-Tx на 0 и 1 "ножках"... или нет?

RANDREY
Offline
Зарегистрирован: 10.06.2012

leshak пишет:

sayi_d[0] = Serial.read();

 sayi_d[1] = Serial.read();     

а между чтениями разве не надо задержку?

Как проверить что пришло в байте?

Данный блок не компилится, для теста шлю 3 байта на Ардуину, принимаю в массив.

if (sayi_d [0])==1
{
lcd.setCursor(0, 0);
lcd.print ("OK");
}

а 2 байта с массива [1] и [2]  надо как-то склеить в один WORD

 

maksim
Offline
Зарегистрирован: 12.02.2012
if (sayi_d[0] == 1)

 

byte LowByte = data[0];
byte  HighByte = data[1];
word Word = (HighByte << 8) + LowByte;

 

RANDREY
Offline
Зарегистрирован: 10.06.2012

Спасибо!

jfd
Offline
Зарегистрирован: 28.11.2014

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

мне не совсем понятно что приходит в сериал монитор

это слегка измененный код из Reference. я в сериал мониторе ввожу "1". ответ приходит в виде

I received: 1
I received:

Откуда берется пустая сторока, я же ввел одно значение? Даже если ввести 50 значений, всеравно они выведутся посторочно плюс одна пустая строка

void setup() {
    Serial.begin(9600); // устанавливаем последовательное соединение
}
 
 
void loop() {
    if (Serial.available() > 0) {  //если есть доступные данные
       // считываем байт
        char incomingByte = Serial.read();
        // отсылаем то, что получили
        Serial.print("I received: ");
        Serial.println(incomingByte);
        
    }
}

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Сообщение #61

maksim
Offline
Зарегистрирован: 12.02.2012

jfd пишет:

Откуда берется пустая сторока, я же ввел одно значение?

Отсюда. 

В правом нижнем углу монитора установите No line ending.

TiPash235
Offline
Зарегистрирован: 12.03.2013


String inData;
void setup()
{
  Serial.begin(9600);
}
void loop() {
  inData = "";
  if (Serial.available() > 0) {
    int h = Serial.available();
    // if you are getting escape -characters try h--; here

    for (int i = 0; i < h; i++) {
      inData += (char)Serial.read();
    }
    // if you are getting escape -characters try Serial.read(); here
  }
  //print it out
  Serial.println(inData);
  delay(1000);
  if (String(inData) == "test")
  {
    Serial.println("TESTIIIING");
  }
}