Как мне сделать правильный сканер длинных строк для обнаружения определенных тегов в потоке символов?
Вот мой код:
#include <SoftwareSerial.h>
SoftwareSerial BTserial(2, 3); // прием, передача
void setup() {
Serial.begin(9600);
BTserial.begin(9600);
Serial.println("Communication with HC-05 successfully started.");
}
bool is_uploading_book = false;
char c = ' ';
String recv_scanner = "";
void loop(){
if (BTserial.available()) {
c = BTserial.read();
recv_scanner += c;
if(recv_scanner.length() >= 50) {
recv_scanner.remove(0, 1);
}
// Мне кажется, что-то не так с recv_scanner.
if(is_uploading_book) {
Serial.write(c);
} else {
if(recv_scanner.indexOf("(__BOOK_UPLOAD_START__)") >= 0) {
is_uploading_book = true;
}
}
Serial.println("\n\n" + recv_scanner + "\n\n");
}
}
Я отправляю книгу (длинный текст) в виде символов один за другим с телефона на модуль HC-05 (и на Arduino).
Моя цель – определить тег "(_BOOK_UPLOAD_START_)" во входящих символах.
Arduino позволяет мне получать символы книги по одному в каждом цикле.
Я сохраняю эти символы в строке с именем recv_scanner, где я допускаю максимальный размер 50 символов, и как только он достигает этих 50 символов, я начинаю удалять первый символ и добавлять новый на каждый цикл (поэтому он может видеть только 50 символов за раз, потому что максимальная длина строки составляет 200, а для моих целей достаточно 50).
Дело в том, что он работает неправильно.
Когда я Serial.println recv_scanner в каждом цикле (чтобы я видел, как он продвигается), он начинает выглядеть так:
⸮qsnieq⸮cn .laslvanm.⸮⸮⸮d⸮alvuv⸮⸮urosqiam tr⸮⸮⸮
(Все начинается нормально, а затем медленно переходит в это)
Я понятия не имею, что это такое.
Насколько я предполагаю, он должен показывать 50 символов, и они должны быть частью книг.
Есть ли что-то фундаментальное в строках, о чем я не знаю?
Я делаю это, чтобы определить тег (_BOOK_UPLOAD_END_) в конце книги и завершить загрузку.
Не стесняйтесь задавать мои вопросы, если что-то непонятно.
@JingleBells, 👍2
2 ответа
Лучший ответ:
Еще один вариант — не сохранять символы в буфере, а вместо этого проверять каждый из них по мере их поступления, чтобы определить, является ли символ следующим в последовательности тегов.
Преимущество этого заключается в том, что вам не нужен буфер для сопоставления с тегом, и вам не нужно постоянно искать строку в буфере.
const char* TAG_STR="(__BOOK_UPLOAD_START__)";
int tag_pos = 0;
char c;
bool is_uploading_book = false;
void loop() {
if (BTserial.available()) {
c = BTserial.read();
if (is_uploading_book) {
//делаем книги
} else {
if (c == TAG_STR[tag_pos]) {
// c соответствует следующему символу в строке
tag_pos++;
} else if (c == TAG_STR[0]) {
// совпал первый символ в середине, перезапустить
tag_pos = 1;
} else {
// Не соответствует следующему символу, поэтому мы должны начать сначала
tag_pos = 0;
}
// Следующим совпадающим символом является терминатор строки
// поэтому мы должны сопоставить всю строку.
if (TAG_STR[tag_pos] == '\0') {
is_uploading_book = true;
}
}
}
}
Я думаю, что ваша проблема заключается в использовании класса String
. Каждый раз, когда вы объединяете переменную String
, новый буфер для результата выделяется в фоновом режиме посредством динамического выделения памяти. Это может привести к фрагментации кучи , которая довольно быстро съедает вашу память (особенно на небольших компьютерах, таких как Uno).
Вместо этого в качестве кольцевого буфера следует использовать массив char
. Давайте сначала определим буфер и инициализируем его нулем:
#define BUFFER_SIZE 50
char buffer[BUFFER_SIZE] = {0};
Теперь мы определяем переменные положения, где действительные данные начинаются и заканчиваются в буфере:
uint8_t buffer_start = 0;
uint8_t buffer_end = 0;
Когда buffer_start == buffer_end
, буфер пуст. Когда buffer_end
находится на одну позицию раньше buffer_start
, буфер заполнен. Чтобы поместить в буфер один символ, делаем следующее (обратите внимание, что c
— это та же переменная, что и в вашем коде):
if( ((buffer_end + 1) % BUFFER_SIZE) == buffer_start)
{
buffer_start = (buffer_start + 1) % BUFFER_SIZE;
}
buffer[buffer_end] = c;
buffer_end = (buffer_end+1) % BUFFER_SIZE;
Что мы здесь делаем? Сначала нам нужно проверить, заполнен ли наш буфер. Мы делаем это, сравнивая buffer_start
со следующей позицией вашего buffer_end
. Вычисление новой позиции — вот где происходит волшебство. Сначала мы увеличиваем buffer_end
на единицу, а затем берем модуль с размером буфера. Модуль - это остаток от целочисленного деления. Когда позиция находится в конце массива buffer
, это будет (49 + 1) % 50
, что равно нулю. Это переносит конец массива в конец массива для нас.
Если наш буфер заполнен, мы увеличиваем начальный счетчик буфера. Это означает, что мы отказываемся от одного символа в начале нашего буфера, чтобы добавить его в конце (именно это вы и хотите сделать). (Обратите внимание, что обычно с кольцевыми буферами вы не хотите выбрасывать данные, поэтому вы отказываетесь помещать больше данных в буфер, когда он заполнен).
Затем мы сохраняем полученный символ в наш буфер и увеличиваем buffer_end
, как описано выше.
Приведенный выше принцип также называется циклическим буфером FIFO (First In First Out), хотя в реальном буфере FIFO отсутствуют некоторые функции, которые здесь вам не нужны.
Теперь, когда у нас есть кольцевой буфер, который заполняется данными, нам также нужно реализовать функцию для поиска строки в буфере, что эквивалентно String.indexOf()
метод. Это может выглядеть примерно так:
int find_string(char str[], const char search[]){
uint8_t start = buffer_start;
uint8_t search_pos = 0;
uint8_t result_pos = -1;
while(start != buffer_end){
if(str[start] == search[search_pos]){
if(search_pos == 0) result_pos = start;
search_pos++;
if(search[search_pos] == '\0') return result_pos;
} else {
result_pos = -1;
search_pos = 0;
}
start = (start + 1) % BUFFER_SIZE;
}
return -1;
}
Обратите внимание, что эта реализация протестирована лишь приблизительно, а не полностью.
Здравствуйте! Большое спасибо за подробный ответ. Я изо всех сил пытаюсь понять, что и как добавить в функцию find_string(). если (найти_строку (?, ?)) { ... }, @JingleBells
Не могли бы вы предоставить код для моего конкретного случая, потому что я не уверен, как правильно его реализовать., @JingleBells
Первый параметр — буфер, второй — строка для поиска. Вы можете использовать его так же, как метод String.indexOf()
., @chrisl
https://i.ibb.co/bJ3kS9v/img.png Я не думаю, что это должно произойти. Я делаю что-то неправильно?, @JingleBells
Если if(is_uploading_book) { } не имеет проверки find_string, он работает нормально. Дело в том, что мне нужна проверка find_string., @JingleBells
Кажется, что-то еще не так. Вы уверены, что в HC-05 настроена правильная скорость передачи данных?, @chrisl
Я использовал решение Крейга, и оно отлично работает. Моя основная проблема возникла, когда в части if (is_uploading_book) { } я попытался снова проверить второй тег (UPLOAD_BOOK_END). В любом случае, это работает. Большое спасибо за ваше время и внимание. Если вы знаете, что происходит, мне любопытно услышать., @JingleBells
Извините, я честно не знаю, что там произошло. Оба решения не используют String
, что хорошо. Хорошо, что теперь у вас есть рабочее решение, @chrisl
Давайте [продолжим это обсуждение в чате](https://chat.stackexchange.com/rooms/105550/discussion-between-novalium-company-and-chrisl)., @JingleBells
- Проблемы с надежным подключением с использованием HC-05 в качестве ведущего устройства Bluetooth
- Bluetooth-модуль HC-05 неправильно считывает данные с моего телефона Android
- Мой модуль Bluetooth HC-05 не работает
- Проблемы с последовательной связью от Arduino к Bluetooth HC-05
- Соединение Bluetooth с HC-05 сопряжено, но не подключено (в Linux)
- Не удается вручную подключить ведущий HC-05 к подчиненному.
- Модуль HC-05 не получает команд и не спаривается
- Передача/прием данных Nodemcu(V3) + модуль Bluetooth HC-05
Работает отлично!!! Спасибо!, @JingleBells
В этом случае вряд ли возникнет проблема, но алгоритм может дать сбой для таких входных данных, как
__BOOK__BOOK_UPLOAD_START__
. В tag_pos 7_
не соответствуетU
, поэтому алгоритм возвращается к tag pos 1. Но тогдаB
не соответствует_
, и обнаружение не производится. Эта проблема будет возникать каждый раз, когда в строке поиска повторяется первый символ, и ее можно избежать, сравнив строку поиска с самой собой., @jpa@Крейг Извините за беспокойство. Не могли бы вы взглянуть на это: https://justpaste.it/3wobv, @JingleBells
@jpa да, это предел этого метода., @Craig