Ви не увійшли.
Проводив перевірку на платі 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]);
}
}
}
Чому це відбувається і як прискорити роботу коду?
Неактивний
Щось мені здається, що ви неправильно використовуєте loop. При кожному проході виконується ініціалізація змінних.Або винесіть ініціалізацію в setup, або зробіть всередині безкінечний цикл
Зауваження про 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)
Неактивний
while (Serial.available() > 0)
Поки ви з цього циклу не вивалюєтесь, все нормально.
А як тільки ваш код встигає читати всі символи - loop добігає до кінця, починається з початку, всі буфери зачищаються.
Як уже сказано, у вас змінні інціалізуються на кожній ітерації, тобто після кожної порції вхідних даних. Коли додаєте delay(1), за цю мілісекунду решта даних встигає прийти у буфер, тому "while (Serial.available() > 0)" продовжує виконуватись і обробляє всі дані, що прийшли.
Скидання змінних на початкові значення тільки після обробки попередніх має виправити проблему.
Також тут у вас доступ за межі масиву:
rArr[10] = {0};
Це Undefined Behavior ("невизначена поведінка"). У масива з 10 елементів індекс останнього елемента 9.
Оффтоп:
* замість (inChar == 47) можна написати (inChar == '/') без коментаря, що це за символ.
* У вас немає контролю вхідних даних. Достатньо велика кількість елементів у вхідних даних також призведе до переповнення масиву, а достатньо довге значення елементу - до переповнення памʼяті.
* Парсити такий формат вхідних даних можна (і потрібно) без проміжної змінної типу String. При отриманні кожної цифри додавайте її до накопичуваного значення:
arr[rIndex] *= 10;
arr[rIndex] += inChar - '0';
Активний
Також замість
while (Serial.available() > 0) {
int inChar = Serial.read();
можна написати в один рядок
for (int inChar; (inChar = Serial.read()) != -1;) {
Активний
Не люблю запису в 1 рядок Це не те що має смисл економити. Перфокарти вже давно не використовуються.
Не люблю запису в 1 рядок
Це не те що має смисл економити. Перфокарти вже давно не використовуються.
Можна і в два:
int inChar;
while ((inChar = Serial.read()) != -1) {
Або, якщо "паралельно" з посимвольною обробкою даних потрібно ще щось виконувати в loop():
int inChar = Serial.read();
if (inChar != -1) {
Тут сенс не в кількості рядків, а в зайвій перевірці. Serial.read() і так перевіряє наявність даних, і повертає -1 якщо даних нема. Звісно, тут це не критично, працюватиме і так. Але лаконічніший код зазвичай легше читається. Ну, то справа смаку.
Активний
Також тут у вас доступ за межі масиву:
rArr[10] = {0};
Це Undefined Behavior ("невизначена поведінка"). У масива з 10 елементів індекс останнього елемента 9.
Неактивний
Парсити такий формат вхідних даних можна (і потрібно) без проміжної змінної типу String. При отриманні кожної цифри додавайте її до накопичуваного значення:
arr[rIndex] *= 10; arr[rIndex] += inChar - '0';
Зауважу, це працюватиме тільки для невідʼємних чисел. Якщо потрібно також відʼємні, то можна або трохи ускладнити парсер, або таки парсити з atoi() чи strtol(). Але буфер типу String все одно не потрібний: у вас валідне значення для int не перевищує 6 символів (якщо відсутні лідируючі нулі). Як краще реалізувати - залежить від того, як вам потрібно обробляти невідповідність вхідних даних до очікуваного формату.
Активний
int rArr[10] = {0}; // у прикладах
rArr[10] = {0}; // у вас
Бачите різницю? Вона є.
Запомним, что значения ячеек массивов не заполняются нулями автоматически.
Взагалі-то глобальні обʼєкти, а також локальні обʼєкти з класом розміщення static ініціалізуються нулями автоматично. Але нічого страшного, якщо ініціалізувати явно - компілятор все одно заоптимізує. До того ж, це правило хорошого тону, щоб тим, хто читає код, було зрозуміліше.
Чтобы это произошло, необходимо заполнить хотя бы один елемент.
Тільки це не "заповнення", а ініціалізація.
Например, если с самого начала нужно записать нули во все ячейки массива, то следует объявить его в программе с одним нулем в фигурных скобках.
Ключове слово тут: "объявить". У вас масив обʼявляється на самому початку:
int rArr[10];
І він у вашому випадку неявно ініціалізується нулями.
А тут:
rArr[10] = {0};
неіснуючому елементу з індексом 10 ви намагаєтесь присвоїти brace-enclosed initializer list з одним елементом int, який конвертується у просто int. Доступ до неіснуючого елемента масива - це помилка, яка призводить до Undefined Behavior. Навіть якщо наче б то "все працює".
https://geekmatic.in.ua/arduino_arrays_lesson
Я б не радив вивчати C++ по якихось сумнівних джерелах.
Активний
Було багато різнопланових зауважень без глобальної прив'язки. Ось код - як я зрозумів запропоновані редагування:
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);
}
Звіт по праці цього коду ніяк не пов'язаний відправленним повідомленням...
Остання редакція filat18 (2025-08-05 09:20:15)
Неактивний
Ось варіат коду, що в мене стабільно працює:
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);
}
Неактивний
Ну так ви зробили щось не те що вам пропонували.
Ось наприклад я взяв такий код
#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
Вам просто повезло.
Неактивний
Ну, а delay() у вас там навіщо?
void loop() {
String textcom = ""; // переменная для сбора текста из сом-порта
while (Serial.available() >
Пояснюю ще раз.
Як тільки ця умова перестане виконуватись - ви вивалитесь з функції loop(). Це відбудеться, коли ви читаєте з порту швидше ніж туди пишуть. При наступному запуску (це відбувається дуже швидко) String textcom знову очиститься.
Не треба робити ініціалізацію таким чином. Робіть її в setup. І очищайте буфер коли вам потрібно.
А нестабільність тому, що вам треба підібрати точно таку затримку, щоб читати з точно такою швидкістю, з якою туди пишуть. Очевидно, це неможливо.
Остання редакція jokeR (2025-08-05 10:13:30)
Неактивний
Ось наприклад я взяв такий код ....
ось тут я не розумію, як цей код пов'язон читанням повідоилень і як він реагує на закінчення повідомлення?
Неактивний
jokeR пише:Ось наприклад я взяв такий код ....
ось тут я не розумію, як цей код пов'язон читанням повідоилень і як він реагує на закінчення повідомлення?
Це відноситься до експлуатації undefined behavior. Вам просто повезло що байти удачно розмістились.
Неактивний
filat18 пише:jokeR пише:Ось наприклад я взяв такий код ....
ось тут я не розумію, як цей код пов'язон читанням повідоилень і як він реагує на закінчення повідомлення?
Це відноситься до експлуатації undefined behavior. Вам просто повезло що байти удачно розмістились.
як це відстежити на етапі випробування коду?
Неактивний
В тому й річ що ніяк. На то воно й 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!
Неактивний
як можно зменшити затримку часа?
Ніяку затримку зменшувати не треба, бо її там взагалі не має бути.
Спробую ще раз. У вашій програмі дві помилки, через які програма не працює як очікується. Решта "зауважень" - просто підказки щодо вдосконалення коду, вони до проблеми не відносяться.
Перша помилка: ви обнуляєте 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)
Активний
Ніяку затримку зменшувати не треба, бо її там взагалі не має бути.
!? як що закоментувати позначений рядок - маю наступну відповідь, при однаковому сповіщєнні 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)
Неактивний
Спробую ще раз. У вашій програмі дві помилки, через які програма не працює як очікується...
Ваша старанність друкувати заслуговує на окрему повагу! Але чи не простіше було б просто надрукувати уривок коду, що дійсно працює?
Неактивний