Что-то со считыванием из Serial...

Andrey-S
Offline
Зарегистрирован: 02.01.2015

И снова всем привет! Мужики, помогите мне разобраться с таким моментом: есть заинтересовавшая меня статья https://habrahabr.ru/post/167209/ в которой снизу есть такой код:

#include <Servo.h> 
 
Servo myservo;

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

String getParam(){
	String re;
	while (Serial.available()) {
		re.concat(Serial.read()-48);
	}
	return re;
}

int getPin(String p){
  	return p.substring(0,2).toInt();
}

int getVal(String p){
  	return p.substring(2,6).toInt();
}

// Главный цикл
void loop() {
	while (Serial.available()) {
		char command = (char)Serial.read();
		String param = getParam();
		int pin = getPin(param);
		int p;
		switch (command) {
			case '0': //Digital write
				pinMode(pin,OUTPUT);
				digitalWrite(pin, LOW);
		      	break;
		    case '1':  //Digital write
				pinMode(pin,OUTPUT);
				digitalWrite(pin, HIGH);
		      	break;
		    case '2': //Servo
				myservo.attach(pin);
				p = getVal(param);
				myservo.write(p);
		      	break;
		    case '3': //Digital read
				pinMode(pin,INPUT);
				Serial.print(digitalRead(pin));
		      	break;
		    case '4': { //Analog read
		    	int aPin = A0;
		    	switch (pin) {
		    		case 1: aPin = A1;	break;
		    		case 2: aPin = A2;	break;
		    		case 3: aPin = A3;	break;
		    		case 4: aPin = A4;	break;
		    		case 5: aPin = A5;	break;
		    		}
				Serial.print(analogRead(aPin));
				}
		      	break;
		    case '5': //Analog write
				pinMode(pin,OUTPUT);
				p = getVal(param);
				analogWrite(pin, p);
		      	break;
		}
	}
}

Исходя из статьи, если залить этот код в дуинку и отправить по Serial "113" должен включиться 13 пин...Если отправит 013 - выключиться (это написано в статье под самим кодом)... В итоге все работает на моих 2х китайских МЕГАх как то не так... Не буду вспоминать сколько я игрался с этим в часах, но заработал этот код только тогда, когда я скорость поменял на 115200 и после строки "char command = (char)Serial.read();" добавил еще 2:

   Serial.print("command=");
   Serial.println(command);

Как я понимаю, эти 2 строки всего лишь создали небольшую временную задержку, без них байтики начинают пропадать... Если вернуть скорость на 9600 и отправить команду "113" то все цифры попадают в эти две строчки и ответ приходит:

command=1
command=1
command=3

Получается, что функция getParam() не работает... Буду благодарен любой помощи.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так поставьте вместо 12-ой строки три такие:

const char c = (char)(Serial.read()-48);
Serial.print("c="); Serial.println(c);
re.concat(с);

"сразу прояснится на доске"

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

В приведённом коде отсутствует обработка ошибок, отсутствует проверка на то, что команда принята полностью. Это - как минимум, глубже не смотрел.

Я не раз повторял уже, повторю ещё раз: чтение "в лоб" по Serial.available - самая хадшая идея из тех, что могут возникнуть. Вот смотрите: вы попали в очередной вызов loop, проверили Serial.available - есть что-то там в порту, начали читать всё, что упало в буфер. И потом - начали выполнять свои действия в зависимости от того, что в буфере UART покладено/положено/зарыто.

НО! В буфер могли ещё не упасть все символы, переданные с той стороны, с чем вы, собственно, и столкнулись.

Как это решается при вдумчивом подходе? Обычно - вводят нечто вроде стоп-последовательности, при получении которой мы понимаем, что в буфере накопилась вся команда, переданная нам извне. Можно юзать хоть три 0xFF подряд (как делает дисплей Nextion), хоть символы новой строки (\r\n) - тут каждый пляшет, как умеет.

В итоге, псевдокод выглядит как-то так:

String buff; // буфер приёма очередной команды

void GotCommand(const String& command)
{
// тут обрабатываем всю строку, которая пришла из порта
}

void loop()
{
// собираем команду до тех пор, пока не встретится символ новой строки \n
   while(Serial.available())
   {
      char ch = Serial.read();
       if(ch == '\r') continue;
       if(ch == '\n') { GotCommand(buff); buff = ""; }
      else buff += ch;
   }
}

В приведённом коде, кстати, тоже нет контроля максимальной длины пакета - можно засрать оперативу, посылая какие угодно символы, кроме \n ;) Но всё дописывается парой строчек.

Ещё раз убеждаюсь, что over9000 провентов примеров из статей в интернете - стоит рассматривать только как примеры. Как плохие примеры.

Andrey-S
Offline
Зарегистрирован: 02.01.2015

ЕвгенийП пишет:

Так поставьте вместо 12-ой строки три такие:

const char c = (char)(Serial.read()-48);
Serial.print("c="); Serial.println(c);
re.concat(с);

"сразу прояснится на доске"

Сделал так:

String getParam(){
  String re;
  while (Serial.available()) {
    const char c = (char)(Serial.read()-48);
   Serial.print("c="); 
   Serial.println(c);
    re.concat(с);


  }
  return re;
}

Ругается на re.concat(с); Говорит

 Arduino: 1.6.5 (Windows 7), Плата"Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)"

 
controll:15: error: stray '\' in program
controll.ino: In function 'String getParam()':
controll:15: error: 'u0441' was not declared in this scope
stray '\' in program
 
 
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Andrey-S пишет:

Ругается на re.concat(с); Говорит

 
Андрей, ну это ж не повод писать сюда! Ну, в скобках вместо латинской "с" там случайно при наборе оказалась русская буква, благо на вид неотличимы. Поправьте - впишите латинскую. Делов-то.
Andrey-S
Offline
Зарегистрирован: 02.01.2015

Во ж блин, никогда не попадал на такие ошибки еще... Спасибо - буду знать... Итого результат отправки 113 на скорости 9600 ="1"... отправка 013 дает тот же результат...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Andrey-S пишет:

Во ж блин, никогда не попадал на такие ошибки еще... Спасибо - буду знать... Итого результат отправки 113 на скорости 9600 ="1"... отправка 013 дает тот же результат...

Вы что-то невнятное написали. Лог скопипастите из сериал-монитора сюда. 

Andrey-S
Offline
Зарегистрирован: 02.01.2015

DIYMan, спасибо за пояснение... Возможно, как раз это именно мой случай... Попробую провернуть нечто подобное сейчас. Спасибо!

Andrey-S
Offline
Зарегистрирован: 02.01.2015

ЕвгенийП, отправляю (кавычки не в счет)  "113" а в монитор просто единица выскакивает одна-одинёшенька... Похоже очень на то, про что говорил  DIYMan... Как я понял код, первая единица из "113" вылавливается и отправляется в блок свич-кейс, а все что за ней следует - в функцию getParam(),из которая потом сабстрингами вынимаются нужные циферки (поправьте если я не прав)... И в итоге данные еще не успели все сложиться в буфер, а дуинка их уже поперла обрабатывать... Это всего лишь мое предположение на основе многочасовой камасутры с этим кодом...

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

здесь #76 DigiUSB замени на Serial и юзай.

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

Andrey-S
Offline
Зарегистрирован: 02.01.2015

Клапауций 322, благодарю! Поразбираюсь что там и зачем... А может кто-нибудь посоветует статейку, в которой использую ту же связку ардуино-питон-веб страничка, в которой будет попонятнее объясняться вся эта схема? А то что-то в ссылке с первого поста все никак не запускается нифига то одно, то другое... Косяков там мелких дофига и догонять их времени вагон уходит. Интересует, чтобы дуинка общалась с веб-страничкой именно через УАРТ.