Почему в Arduino перестает работать Serial?

Доброго времени суток.Несколько лет назад я задавал вопрос по зависанию UART Ардуино. И вот снова. Погуглил. И нашел вопрос, в котором содержится половина ответа.
Главное, что я хочу признать, что ошибался в описании внешних признаков. Тогда и сейчас UART не зависал. Да, UART переставал выводить информацию, но отнюдь не зависал - просто условия для вывода информации исчезали. А я этого тогда не увидел.
Итак, есть проблема:в Arduino перестает работать штатная библиотека Serial. Сначала данные на порт Tx выводятся, а через некое стабильное время перестают.
Человек здесь (Почему в Arduino перестает работать Serial? — Хабр Q&A) спрашивает (Вопрос задан более трёх лет назад) и приводит небольшую программу, демонстрирующую сей эффект. Приведу её здесь.

int time = 0;
int count = 0;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("test");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(11);
  if(millis() > time) {
    count++;
    Serial.println(count);
    time = millis() + 1000;
  }
}

На 33 раз всегда вывод останавливается (смотрю через монитор стандартный)

Ему ответили, что надо сделать, что бы программа работала, как задывалось:
Сделайте числа того же размера, что и millis() и всё пойдет норм)
unsigned long time = 0;
unsigned long count = 0;
Но это не объяснение причины, это способ избавиться от проблемы.
Я решил в этом вопросе покопаться и вот, что я сделал.
1)Что бы убедиться, что программа не зависла, а “завис” только UART, я добавил в начало цикла мигание светодиодом.И он мигал всегда.
2)Тогда я поместил код мигания в условие if(millis() > time) и светодиод перестал мигать вместе с работой Serial.
3)Осталось выяснить почему не работает условие.
Здесь я позволю себе привести весь код, что бы каждый желающий мог сам убедиться.

volatile unsigned long TimeMillis=0;
int time = 0;//счетчик count останавливается после 33
//byte time = 0;//счетчик count останавливается после 5179. Выводит только 5
int count = 0;
byte set=0;//начальный сигнал светодиода

void setup() {
  Serial.begin(57600);
  Serial.println("test UART Arduino");
}

void loop()
{
  delay(11);

//компилятор предупреждает: сравнение целочисленных выражений со знаком и без знака
  TimeMillis=millis();
  if(TimeMillis > time) {
    count++;
  set = ! set;// инверсия светодиода, что бы видеть что программа работает
  digitalWrite(13, set);
    
   Serial.print("count=");
    Serial.println(count);
//    Serial.println(TimeMillis, BIN);
    Serial.print("millis=");
    Serial.println(millis(), DEC);
    Serial.println(millis(), HEX);
    Serial.println(millis(), BIN);
    time = millis() + 1000;//test UART Arduino. Only 33 out
    Serial.print("New time=");
    Serial.println(time,BIN);
    Serial.println(time,HEX);
    Serial.println(time,DEC);
  }
}

Последние шаги работы UART
count=32
millis =111100110001011
New time=111110101110100
count=33
millis =111110101110110
New time=11111111111111111000000101011111

Из результатов видно, что до 33 шага чило милисекунд умещалось в 2 байта (15бит).
Но когда произошло очередное добавление 1000, число не поместилось в 2 байта (int time) и ардуина почему-то разместила переменную time в 4-х байтах!
Как известно полный цикл счетчика millis() составляет около 50 суток. Думаю, что через это время число millis() станет больше 11111111111111111000000101011111 и программа продолжит свою работу по выводу информации посредством функции Serial. Но кто же будет столько ждать, да и кому нужна такая работа?

Так же я провел еще один эксперимент, изменив тип на беззнаковый
unsigned int time = 0;
Ниже представлен кусочек результата, из которого видно, что при переполнении time ардуино НЕ выделила еще 2 байта, а урезала до достаточных размеров - до 1 байта. И программа продолжила работать в штатном режиме.

count=65
millis=64347
FB5B
1111101101011011
New time=1111111101000100
FF44
65348
count=66
millis=65352
FF48
1111111101001000
New time=1100110001
331
817

Теперь вопрос к знатокам: увеличение размера int до 4х байтов - это баг или фича?

uint32_t

Недостаточная размерность переменной time - это именно причина.

А вот “открытый” вами эффект перехода от двухбайтовых значений к четырехбайтовым - это фикция, на самом деле этого нет. То что вы наблюдаете - известный глюк , ах. простите… особенность оператора Serial.print

1 лайк

Самое обыкновенное “переполнение millis”, в данном случает того, где хранится его результат – time хочет стать 33000 и “в штанишки не влазит”.

И никаких таинственных глюков («кроме мордобития, никаких чудес»).

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

И, кстати, раз уж Вы занялись исследованием, так хоть анализируйте его результаты. Вы же сами написанли:

Так значит проблема не в Serial! Зачем же у Вас тогда такой глупый заголовок?

Другого ответа от “знатоков” на этом форуме я и не ожидал. Новичкам будет полезно и это главное. Хотел спросить про остальные известные глюки оператора Serial.print, но ответ “знатоков” я уже знаю - идти читать книги.

Что именно не устроило в ответе? Не хотите признавать, что все дело в размерности переменной?

что именно? Найденная вами несуществующая проблема?

1 лайк

Данный, обсуждаемый здесь, “глюк” оказался глюком в голове автора вопроса, вызванным либо применением запрещённых веществ, либо абсолютной пустотой в этой самой голове и к Serial.print никакого отношения не имеет.

Уверен, что остальные глюки – такие же. Впрочем, спрашивайте, посмотрим, что там.

Не признавать очевидное - это признак дебилизма. Вы просто прочтите, что я спрашивал. Меня интересует только то, что я спросил! А мнение по моим дополнительным телодвижениям я ни у кого не спрашивал.

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

Upd. Перенёс пока в ЧЧ.

1 лайк

Это именно обьяснение причины. проблема не в сериал

1 лайк

это?

Ответ - это не то и не другое, размер переменной time остается как был, 2 байта - просто Serial.print преобразовывает его в лонг перед печатью.

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

т.е. Вы считаете такое поведение нормальным? Т.е. это всем очевидный факт, что int time превратился в long Time? Может дадите ссылку где такое свойство Serial.print описано на сайтах, посвященных Ардуино?

это свойство языка С, от того, считаю ли я или Вы это нормальным - ситуация не меняется.
Я просто принимаю это как данность.

это вам к @ЕвгенийП надо

Да.

Нет, не всем, а только тем, кто знает язык С++. Вам, вот, например, не очевидно.

Не дам. Это не имеет никакого отношения к ардуино - это свойство языка С++. Если нужна ссылка на описание языка, где про это написано, могу дать. На “сайтах посвящённых ардуино” может быть такое где-то и написано, но я не знаю, где, а искать не буду, зачем?

Я тоже теперь принимаю как данность. Но это неочевидная данность! Поэтому и прошу ссылку, что бы посыпать свою голову пеплом и удивляться, как же я не увидел такую информацию, которую на каждом сайте выкладывают крупным шрифтом.

Действительно, зачем? Как же Вы потом сможете потом унижать не профи в С++. Набежать и облить грязью человека значительно проще, чем привести аргументы и ссылки.

Вы забыли ещё меня забанить. Как это я посмел спросить то, что всем “профи С++” очевидно, вынести в вопрос.!!! Да еще и неправильно сформулировал! Очевидно же, что если в сериал порт не выводится информация, то это не проблема сериал порта, а глазах или голове смотрящего. Может Вы подскажете, какой вопрос новичок введет в гугл, если у него перестанут выводиться данные в сериал порт? Очень интересно узнать мнение хозяина форума.

Т.е. ссылка на описание языка (которую я Вам предложил дать) Вас не устраивает.

Вы считаете, что я должен всё бросить и искать для Вас ссылку именно на сайтах, посвящённых ардуино? Вот так всё бросить и искать? Для Вас? Вы … это … здоровы? Когда это я Вам так задолжал?

Хотя, если Вас интересует именно Seral.print, то тут всё ещё проще. Тут вообще никаких ссылок не надо, кроме ссылки на Ваш собственный диск у Вас под носом.

Полезьте на свой собственный диск и найдите там ардуиновский файл Print.cpp. У меня он находится по адресу

C:\Users\Eugene\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino\Print.cpp

у Вас - ХЗ, ищите.

Откройте файл и найдите в нём функцию печати целого ( int ) и полюбуйтесь на неё.

size_t Print::print(int n, int base)
{
  return print((long) n, base);
}

Любуйтесь до тех пор, пока не наступит просветление.

А когда наступит, придите сюда и извинитесь перед @b707 и перед мною.

1 лайк

Вопрос свой перечитай в заголовке. На него тебе ответили, что это не Сериал “перестает работать”, это выбран неверный тип переменной.

Но, блин, Вы же сами написали:

Т.е. у Вас и светодиод не мигает - тоже сериал виноват?

2 лайка