#1 2025-08-04 06:56:50

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Чому повідомлення не вдається розкласти в масив без затримки часу

Проводив перевірку на платі UNO.
Плата отримує повідомлення у вигляді числового масиву не визначеної довжини (приклад: 2/954/67). Код, що використовується, працює тільки якщо використовую рядок затримки часу - якщо цей рядок закоментувати, то дані не потрапляють в масив.

int rArr[10]; // = {0}; 
int rIndex; // = 0; 

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

void loop() {
  String textcom = "";
  rArr[10] = {0};
  rArr[1] = 1;
  rIndex = 0;
  while (Serial.available() > 0) {
    int inChar = Serial.read();

    delay(1);  //-------------???

    if ((inChar == 47) || (inChar == 'n')){ // 47  - код "/"
      rArr[rIndex ] = textcom.toInt();
      textcom = "";
      rIndex++;   
    }else{
      textcom += (char)inChar;
    }
    if (inChar == 'n') {
      Serial.println(rArr[0]);
      Serial.println(rArr[1]);
      Serial.println(rArr[2]);
    }
  }
}

Чому це відбувається і як прискорити роботу коду?

Неактивний

#2 2025-08-04 08:01:29

jokeer
Гість

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Щось мені здається, що ви неправильно використовуєте loop. При кожному проході виконується ініціалізація змінних.Або винесіть ініціалізацію в setup, або зробіть всередині безкінечний цикл

#3 2025-08-04 09:28:44

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Зауваження про rArr[10] = {0}? Ініциалізація виконується до setup, а у loop - виконую стирання. Випробую виконати стирання після вдалого считування:
    if (inChar == 'n') {
      Serial.println(rArr[0]);
      Serial.println(rArr[1]);
      Serial.println(rArr[2]);
      rArr[10] = {0};
    }
Зверну увагу на те, що затримка часу після rArr[10] = {0} нічого не дає, а працює лише у тому місці, що я виклав.

Остання редакція filat18 (2025-08-04 09:29:36)

Неактивний

#4 2025-08-04 10:45:23

jokeer
Гість

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

while (Serial.available() > 0)

Поки ви з цього циклу не вивалюєтесь, все нормально.

#5 2025-08-04 11:10:16

jokeer
Гість

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

А як тільки ваш код встигає читати всі символи - loop добігає до кінця, починається з початку, всі буфери зачищаються.

#6 2025-08-04 11:29:19

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 571

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Як уже сказано, у вас змінні інціалізуються на кожній ітерації, тобто після кожної порції вхідних даних. Коли додаєте delay(1), за цю мілісекунду решта даних встигає прийти у буфер, тому "while (Serial.available() > 0)" продовжує виконуватись і обробляє всі дані, що прийшли.
Скидання змінних на початкові значення тільки після обробки попередніх має виправити проблему.

Також тут у вас доступ за межі масиву:

rArr[10] = {0};

Це Undefined Behavior ("невизначена поведінка"). У масива з 10 елементів індекс останнього елемента 9.

Оффтоп:
* замість (inChar == 47) можна написати (inChar == '/') без коментаря, що це за символ.

* У вас немає контролю вхідних даних. Достатньо велика кількість елементів у вхідних даних також призведе до переповнення масиву, а достатньо довге значення елементу - до переповнення памʼяті.

* Парсити такий формат вхідних даних можна (і потрібно) без проміжної змінної типу String. При отриманні кожної цифри додавайте її до накопичуваного значення:

arr[rIndex] *= 10;
arr[rIndex] += inChar - '0';

Активний

#7 2025-08-04 11:39:09

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 571

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Також замість

  while (Serial.available() > 0) {
    int inChar = Serial.read();

можна написати в один рядок

  for (int inChar; (inChar = Serial.read()) != -1;) {

Активний

#8 2025-08-04 11:57:37

jokeer
Гість

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Не люблю запису в 1 рядок wink Це не те що має смисл економити. Перфокарти вже давно не використовуються.

#9 2025-08-04 12:15:21

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 571

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

jokeer пише:

Не люблю запису в 1 рядок wink Це не те що має смисл економити. Перфокарти вже давно не використовуються.

Можна і в два:

int inChar;
while ((inChar = Serial.read()) != -1) { 

Або, якщо "паралельно" з посимвольною обробкою даних потрібно ще щось виконувати в loop():

int inChar = Serial.read();
if (inChar != -1) {

Тут сенс не в кількості рядків, а в зайвій перевірці. Serial.read() і так перевіряє наявність даних, і повертає -1 якщо даних нема. Звісно,  тут це не критично, працюватиме і так. Але лаконічніший код зазвичай легше читається. Ну, то справа смаку.

Активний

#11 2025-08-04 14:16:25

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 571

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

dimich пише:

Парсити такий формат вхідних даних можна (і потрібно) без проміжної змінної типу String. При отриманні кожної цифри додавайте її до накопичуваного значення:

arr[rIndex] *= 10;
arr[rIndex] += inChar - '0';

Зауважу, це працюватиме тільки для невідʼємних чисел. Якщо потрібно також відʼємні, то можна або трохи ускладнити парсер, або таки парсити з atoi() чи strtol(). Але буфер типу String все одно не потрібний: у вас валідне значення для int не перевищує 6 символів (якщо відсутні лідируючі нулі). Як краще реалізувати - залежить від того, як вам потрібно обробляти невідповідність вхідних даних до очікуваного формату.

Активний

#12 2025-08-04 14:20:42

jokeer
Гість

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

int rArr[10] = {0}; // у прикладах
rArr[10] = {0}; // у вас

Бачите різницю? Вона є.

#13 2025-08-04 21:07:20

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 571

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

filat18 пише:

Запомним, что значения ячеек массивов не заполняются нулями автоматически.

Взагалі-то глобальні обʼєкти, а також локальні обʼєкти з класом розміщення static ініціалізуються нулями автоматично. Але нічого страшного, якщо ініціалізувати явно - компілятор все одно заоптимізує. До того ж, це правило хорошого тону, щоб тим, хто читає код, було зрозуміліше.

filat18 пише:

Чтобы это произошло, необходимо заполнить хотя бы один елемент.

Тільки це не "заповнення", а ініціалізація.

filat18 пише:

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

Ключове слово тут: "объявить". У вас масив обʼявляється на самому початку:

int rArr[10];

І він у вашому випадку неявно ініціалізується нулями.

А тут:

rArr[10] = {0};

неіснуючому елементу з індексом 10 ви намагаєтесь присвоїти brace-enclosed initializer list з одним елементом int, який конвертується у просто int. Доступ до неіснуючого елемента масива - це помилка, яка призводить до Undefined Behavior. Навіть якщо наче б то "все працює".

filat18 пише:

https://geekmatic.in.ua/arduino_arrays_lesson

Я б не радив вивчати C++ по якихось сумнівних джерелах.

Активний

#14 2025-08-05 09:16:46

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Було багато різнопланових зауважень без глобальної прив'язки. Ось код - як я зрозумів запропоновані редагування:

int rArr[10] = {0};
int rIndex = 0; 

void setup() {
  Serial.begin(250000); 
  rIndex = 0;
}

void loop() {
  while (Serial.available() > 0) {
    int inChar = Serial.read();
    rArr[rIndex] *= 10;
    rArr[rIndex] += inChar - '0';
    rIndex++;
    if (inChar == 'n') {
      Serial.println(rArr[0]);
      Serial.println(rArr[1]);
      Serial.println(rArr[2]);
      Serial.println(rArr[3]);
      Serial.println(rArr[4]);
      Serial.println(rArr[5]);      
      Serial.println(rArr[6]);
      for (int i = 0; i < 10; i++) {
        rArr[i] = 0;
      }
      rIndex = 0;
    }
  }  
  delay(1);
}

Звіт по праці цього коду ніяк не пов'язаний відправленним повідомленням...  sad

Остання редакція filat18 (2025-08-05 09:20:15)

Неактивний

#15 2025-08-05 09:30:58

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Ось варіат коду, що в мене стабільно працює:

int rArr[5] = {0};
int rIndex = 0;

void setup() {
  Serial.begin(250000);
  for (int i = 0; i < 5; i++) {
    rArr[i] = 0;
  }
  rArr[1] = 1;
  rIndex = 0;
}

void loop() {
  String textcom = ""; // переменная для сбора текста из сом-порта    
  while (Serial.available() > 0) {
    int inChar = Serial.read();    
    if ((inChar == '/') || (inChar == 'n')){
      if (textcom != ""){
        rArr[rIndex ] = textcom.toInt();
        textcom = "";
        delay(1); // ------------ чому без цього радка результат не стабільний????
      }
      rIndex++;   
    }else{
      textcom += (char)inChar;
    }
    if (inChar == 'n') {
      String text = String(rArr[0]) + "_" + String(rArr[1]) + "_" + String(rArr[2]) + "_" + String(rArr[3]) + "_" + String(rArr[4]); 
      Serial.println(text);
      for (int i = 0; i < 5; i++) {
        rArr[i] = 0;
      }
      rArr[1] = 1;
      rIndex = 0;
    }
  }
  delay(1);
}

Неактивний

#16 2025-08-05 09:31:58

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

як можно зменшити затримку часа?

Неактивний

#17 2025-08-05 09:34:27

jokeR
Учасник
Зареєстрований: 2024-12-12
Повідомлень: 155

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Ну так ви зробили щось не те що вам пропонували.
Ось наприклад я взяв такий код

#include <stdio.h>
int rArr[10]; // = {0}; 
int rIndex; // = 0; 
int main()
{
  rArr[10] = {0};
  rArr[1] = 1;
  rIndex = 0;
    return rIndex+rArr[0];
}

і прогнав його через godbolt.org

__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
rArr:
        .zero   20
rIndex:
        .zero   2
main:
        push r28
        push r29
        in r28,__SP_L__
        in r29,__SP_H__
.L__stack_usage = 2
        sts rArr+20+1,__zero_reg__ ; тут видно, що rArr[10] = {0};  попадає мимо масиву. насправді обнуляється rIndex
        sts rArr+20,__zero_reg__
        ldi r24,lo8(1)
        ldi r25,0
        sts rArr+2+1,r25
        sts rArr+2,r24
        sts rIndex+1,__zero_reg__ ; rIndex = 0; - обнуляється ще раз
        sts rIndex,__zero_reg__
        lds r18,rArr
        lds r19,rArr+1
        lds r24,rIndex ж 
        lds r25,rIndex+1
        add r24,r18
        adc r25,r19
        pop r29
        pop r28
        ret

Вам просто повезло.

Неактивний

#18 2025-08-05 09:42:26

jokeR
Учасник
Зареєстрований: 2024-12-12
Повідомлень: 155

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

Ну, а delay() у вас там навіщо?

void loop() {
  String textcom = ""; // переменная для сбора текста из сом-порта    
  while (Serial.available() > 

Пояснюю ще раз.
Як тільки ця умова перестане виконуватись - ви вивалитесь з функції loop(). Це відбудеться, коли ви читаєте з порту швидше ніж туди пишуть. При наступному запуску (це відбувається дуже швидко) String textcom  знову очиститься.
Не треба робити ініціалізацію таким чином. Робіть її в setup. І очищайте буфер коли вам потрібно.
А нестабільність тому, що вам треба підібрати точно таку затримку, щоб читати з точно такою швидкістю, з якою туди пишуть. Очевидно, це неможливо.

Остання редакція jokeR (2025-08-05 10:13:30)

Неактивний

#19 2025-08-05 10:03:28

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

jokeR пише:

Ось наприклад я взяв такий код ....

ось тут я не розумію, як цей код пов'язон читанням повідоилень і як  він реагує на закінчення повідомлення?

Неактивний

#20 2025-08-05 10:09:03

jokeR
Учасник
Зареєстрований: 2024-12-12
Повідомлень: 155

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

filat18 пише:
jokeR пише:

Ось наприклад я взяв такий код ....

ось тут я не розумію, як цей код пов'язон читанням повідоилень і як  він реагує на закінчення повідомлення?

Це відноситься до експлуатації undefined behavior. Вам просто повезло що байти удачно розмістились.

Неактивний

#21 2025-08-05 11:18:54

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

jokeR пише:
filat18 пише:
jokeR пише:

Ось наприклад я взяв такий код ....

ось тут я не розумію, як цей код пов'язон читанням повідоилень і як  він реагує на закінчення повідомлення?

Це відноситься до експлуатації undefined behavior. Вам просто повезло що байти удачно розмістились.

як це відстежити на етапі випробування коду?

Неактивний

#22 2025-08-05 11:28:09

jokeR
Учасник
Зареєстрований: 2024-12-12
Повідомлень: 155

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

В тому й річ що ніяк. На то воно й undefined. Стежте за попередженнями компілятора, хоча це 100% гарантії не дасть, на цей код він не дає попереджень. Є статичні аналізатори, хттпс://cppcheck.sourceforge.io/demo/ наприклад. Цей ловить.

Cppcheck 2.10

[test.cpp:10]: (error) Array 'rArr[10]' accessed at index 10, which is out of bounds.

Done!

Неактивний

#23 2025-08-05 13:17:23

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 571

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

filat18 пише:

як можно зменшити затримку часа?

Ніяку затримку зменшувати не треба, бо її там взагалі не має бути.

Спробую ще раз. У вашій програмі дві помилки, через які програма не працює як очікується. Решта "зауважень" - просто підказки щодо вдосконалення коду, вони до проблеми не відносяться.

Перша помилка: ви обнуляєте rIndex не в тому місці. Його потрібно обнуляти перед отриманням нового рядка, тобто на початку роботи програми, а також після обробки попереднього рядка.

Зараз він у вас обнуляється перед очікуванням кожного символа. Прийшов перший символ на Serial, ви його прочитали. Нових символів у Serial ще нема. Виклик Serial.available() повертає 0, цикл while завершується. loop() виконується знову, rIndex обнуляється.   
Коли ви додаєте delay(1), то поки виконується ця затримка, в Serial встигає прийти решта символів. Цикл while в цьому випадку не завершується, rIndex не обнуляється, всі наявні символи обробляються. Це відповідь на ваше питання "чому це відбувається".

Але delay(1) не є коректним вирішенням проблеми. Просто перенесіть rIndex=0 в те місце, яке виконується після обробки всього рядка. тобто в кінець блоку під

if (inChar == '\n') {

Друга помилка: ви звертаєтесь до неіснуючого елемента масива rArr[10]. В C++ елементи масивів індексуються з нуля. Якщо у вас масив з 10 елементів:

int rArr[10];

то індекси його елементів від 0 до 9. Перший елемент rArr[0], останній елемент rArr[9]. А ви записуєте в rArr[10], тобто за межі масива.

(Тут вам повезло, що компілятор так розмістив змінні в памʼяті, що одразу за масивом rArr[] йде rIndex, і "rArr[10] = { 0 }" насправді також обнуляє rIndex, і ніяких інших даних не пошкоджує. Але ні в якому разі не можна на це розраховувати. Ще раз: доступ за межі масива, хоч запис, хоч читання - це помилка!).

Навіщо взагалі обнуляти той масив? Ви все одно перезаписуєте комірки новими значеннями тут:

rArr[rIndex ] = textcom.toInt();

rIndex у вас уже визначає, скільки комірок заповнені. Виводьте їх значення до rIndex:

    for (int i = 0; i < rIndex; i++) {
        Serial.println(rArr[i]);
    }

Я би навіть написав робочий скетч, якби були озвучені вимоги до реакції програми на некоректні вхідні дані. Що програма має робити, коли замість цифри прийшов інший символ, який не є '/' або '\n'. Коли число завелике і не вміщається в int. Коли приходить два '/' підряд. Коли приходить більше 10 чисел в одному рядку. Чи очікуються відʼємні значення з '-' на початку.

У реальній програмі ви ж прочитані значення не назад в Serial будете видавати, а якось використовувати. А то напишу як розумію сам, ви почнете використовувати цей код, потім виявиться, що обробляти помилкові дані вам потрібно по-іншому, і ви вирішите, що це я хєрню написав.

Остання редакція dimich (2025-08-05 13:20:19)

Активний

#24 2025-08-05 13:29:54

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

dimich пише:

Ніяку затримку зменшувати не треба, бо її там взагалі не має бути.

!? як що закоментувати позначений рядок - маю наступну відповідь, при однаковому сповіщєнні 147/258/369/321/654 , отримую наступний результат:
147_258_369_321_654
147_258_369_321_654
147_258_369_321_654
147_58_369_321_654
147_258_369_0_654
147_258_369_321_654
147_258_369_0_654
0_258_369_321_654
147_258_369_321_654
147_258_369_1_654
147_258_369_321_654
147_258_9_321_654
147_258_369_321_54
147_258_369_321_654

Остання редакція filat18 (2025-08-05 13:30:24)

Неактивний

#25 2025-08-05 13:35:34

filat18
Учасник
Зареєстрований: 2025-08-04
Повідомлень: 20

Re: Чому повідомлення не вдається розкласти в масив без затримки часу

dimich пише:

Спробую ще раз. У вашій програмі дві помилки, через які програма не працює як очікується...

Ваша старанність друкувати заслуговує на окрему повагу! Але чи не простіше було б просто надрукувати уривок коду, що дійсно працює?

Неактивний

Швидке повідомлення

Введіть повідомлення і натисніть Надіслати

Підвал форуму