Mit app inventor и ардуино проблема парсинга

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
Здравствуйте уважаемые форумчане! Я не программист и только осваиваю ардуино. Делаю управление теплицей.
Всё работает, но хочется большего. Сделал в Mit app inventor приложение для управления.
На приёме от ардуино работает без проблем, но мне нужно отправлять из приложения пакет данных (название программы, температуры, и тд.)
Пакет выглядит так, пример: Текст | (разделитель) целое число | целое число | целое число | целое число /n (знак конца строки) всего 17 аргументов: текст и 16 чисел.
На ардуино я получаю этот пакет и даже вывожу на экран. Всё работает.
Но вопрос для вас очень простой, а я уже третий день сижу учу мат часть и пока глухо. Как мне выделить из строки отдельно текст и отдельно числа в виде переменных ( я потом сохраняю это в EEPROM)? У меня в памяти EEPROM хранится программы и их значения, а с помощью телефона я редактирую название программы и могу менять параметры. Пожалуйста, если не трудно напишите как это осуществить. Мне только нужно разделить эту принятую в ардуино грёбаную строку.
Понимаю что вопрос для первоклашки, но не закидывайте сразу тапками, я только учусь!
Принимаю я так:
if(Serial.available()){
myString = Serial.readStringUntil(myChar); lcd.print(myString);
}
А дальше все перепробовал и не получилось :(
Потом я должен записать это в виде EEPROM.put(a,text1 ); EEPROM.put(a,num1 ); EEPROM.put(a,num2 ); ..... EEPROM.put(a,num16 );
 

Alex_HF

★✩✩✩✩✩✩
11 Мар 2023
40
16

C++:
String application_command = "{10,12; 4,5; 2}";

// https://stackoverflow.com/questions/9072320/split-string-into-string-array
String getValue(String data, char separator, int index)
{
  int found = 0;
  int strIndex[] = {0, -1};
  int maxIndex = data.length()-1;

  for(int i=0; i<=maxIndex && found<=index; i++){
    if(data.charAt(i)==separator || i==maxIndex){
        found++;
        strIndex[0] = strIndex[1]+1;
        strIndex[1] = (i == maxIndex) ? i+1 : i;
    }
  }

  return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}

....
    
  String part01 = getValue(application_command,';',0);
  String part02 = getValue(application_command,';',1);
  String part03 = getValue(application_command,';',2);
 

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
Оформи код соответствующим тэгом
Всё это я уже проходил, вот код от Алекса, который я пробовал и он отлично подходит, но проблема элементарная - связать (заменить) строку char str[] = "1234,3.14,hello,4567,lolkek,qwerty"; с тем что я принимаю и всё! Я реально не понимаю как, я понимаю, что это же элементарно, но все мои попытки окончились ничем.

// тест парсера строк
#include <GParser.h>

void setup() {
Serial.begin(9600);
// строка для примера
// данные разделены разделителем, например запятой
// могут быть получены из Serial/UDP/TCP/MQTT итд
char str[] = "1234,3.14,hello,4567,lolkek,qwerty";
// кормим строку парсеру, указываем разделитель (умолч. запятая)
GParser data(str, ',');
// разделяем
// ВНИМАНИЕ! Операция "ломает" строку, заменяя разделители на NULL
int am = data.split();
// получаем количество данных
Serial.println(am); // выводим количество
// можем обратиться к полученным строкам как data или data.str
for (byte i = 0; i < am; i++) Serial.println(data);
// также можно получить их в виде int и float чисел
// передав индекс строки
Serial.println(data.getInt(0));
Serial.println(data.getFloat(1));

// можно сравнить со строкой (номер парс строки, строка для сравнения)
if (data.equals(2, "hello")) Serial.println("true");
else Serial.println("false");
data.restore(); // восстановить исходный вид строки (вернуть разделители)
Serial.println();

void loop() {
}
 
Изменено:

Alex_HF

★✩✩✩✩✩✩
11 Мар 2023
40
16
Сложно помогать если нет полного кода, но попробуем
Принимаю я так:
if(Serial.available()){
myString = Serial.readStringUntil(myChar); lcd.print(myString);
}
ну и напишите дальше
C++:
if (Serial.available()) {

    myString = Serial.readStringUntil(myChar); lcd.print(myString);

    GParser data(myString, ','); // <------ разбор вашей строки
    int am = data.split();

    #define NUM1_ADDR 0 // адрес переменной №1 в EEPROM
    #define NUM2_ADDR XXX // адрес переменной №2 - не 1!! а NUM1_ADDR + размер переменной (байт=1, int=2)

    #define TEXT_ADDR xxxxxx // адрес текста

    EEPROM.put(NUM1_ADDR, data.getInt(1));
    EEPROM.put(NUM2_ADDR, data.getInt(2));
    EEPROM.put(TEXT_ADDR, data.getText(0));

}
 

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
К сожалению код содержит 1998 строк и я не думаю что он поможет, Бог с эти EEPROM, с ним я разобрался, с ардуино читаю и записываю туда без проблем, а вот строку из приложения тупо разобрать пока никак. Пусть для примера хотя бы выведет корректно на экран, а там уже разберусь сам. Если нужен код, говорите какую часть кода нужно и я смогу выложить. Когда принимаю строку myString она выглядит корректно, но целиком с разделителями.
 
Изменено:

Alex_HF

★✩✩✩✩✩✩
11 Мар 2023
40
16
как и где объявлена переменная myString
если это String то пишем так

GParser data(myString.c_str(), ',');
 

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
Оформи код соответствующим тэгом
@Alex_HF,
сделал как вы сказали, отдельно сделал скетч
Послал строку в виде TEXT | 11 | 12 | ..... 17 | и в конце /n
ардуино вывел Текст сразу после принятия корректно, но вместо чисел только 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
А в следующем цикле уже вместо текста пошли отправленные цифры, с каждым циклом void loop() меняющиеся на следующие по списку



#include <GParser.h>// парсер строк
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
String myString;
char myChar = '|';

void setup() {
Serial.begin(9600);
lcd.init(); // Инициализация lcd
lcd.backlight(); // Включаем подсветку

Serial.setTimeout(100);
}

void loop() {
if(Serial.available()){
myString = Serial.readStringUntil(myChar);

GParser data(myString.c_str(),'|');// кормим строку парсеру, указываем разделитель
int am = data.split(); // получаем количество данных (строк должно быть у меня 17)
// Serial.println(am); // выводим количество если нужно
// Serial.println(data); // выводим строку под номером (всего у меня 17
// Serial.println(data.getInt(0));// также можно получить их в виде int
// Serial.println(data.getFloat(1));// также можно получить их в виде float чисел
// if (data.equals(2, "hello")) Serial.println("true"); // можно сравнить со строкой (номер парс строки, строка для сравнения)
// else Serial.println("false");
// data.restore(); // восстановить исходный вид строки (вернуть разделители)

lcd.clear();
lcd.print(data[0]); //
delay(10000);

lcd.clear();
lcd.setCursor(0, 0);
lcd.print(data.getInt(1));
lcd.setCursor(0, 1);
lcd.print(data.getInt(2));
lcd.setCursor(0, 2);
lcd.print(data.getInt(3));
lcd.setCursor(0, 3);
lcd.print(data.getInt(4));
lcd.setCursor(5, 0);
lcd.print(data.getInt(5));
lcd.setCursor(5, 1);
lcd.print(data.getInt(6));
lcd.setCursor(5, 2);
lcd.print(data.getInt(7));
lcd.setCursor(5, 3);
lcd.print(data.getInt(8));
lcd.setCursor(10, 0);
lcd.print(data.getInt(9));
lcd.setCursor(10, 1);
lcd.print(data.getInt(10));
lcd.setCursor(10, 2);
lcd.print(data.getInt(11));
lcd.setCursor(10, 3);
lcd.print(data.getInt(12));
lcd.setCursor(15, 0);
lcd.print(data.getInt(13));
lcd.setCursor(15, 1);
lcd.print(data.getInt(14));
lcd.setCursor(15, 2);
lcd.print(data.getInt(15));

delay(20000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(data[16]);
delay(10000);
}
}
 

Alex_HF

★✩✩✩✩✩✩
11 Мар 2023
40
16
Послал строку в виде TEXT | 11 | 12 | ..... 17 | и в конце /n
Я вижу пробелы между разделителями TEXT_|_11_| - наверное их быть не должно

ну и собственно виновник торжества:
LiquidCrystal_I2C lcd(0x27, 20, 4);
String myString;
char myChar = '|';

...
myString = Serial.readStringUntil(myChar); <- считывать строку пока не встретим символ myChar - а он у вас такой же как и разделители в строке

измените на myString = Serial.readStringUntil('\n');
 
Изменено:
  • Лойс +1
Реакции: amss

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
@Alex_HF,
пробелов не должно быть строка отправляется так как на картинке, может в буфере еще что то? как его очищать перед приемом? И как вывести на lcd то что пришло целиком как есть? Помогите пожалуйста, - уже мозги кипят третий день из-зи фигни какой то... Проект готов на 99% на ардуино с кнопками и датчиками, релюшками и прочим всё работает, сохраняет, редактирует, осталось продублировать тоже самое с телефона. Прием работает четко, а с отправкой такая засада.
 

Вложения

Изменено:

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
@Alex_HF,
Спасибо!!!!! Вы спасли мою голову от взрыва! Я уже кучу примеров из инета перепробовал, уже даже на испанском :) зато знаю что означает слово палабра! Всё заработало как нужно.
И на последок один вопрос. Если одновременно отсылаются в реальном времени данные по блютус и вдруг я решаю принять другие, как себя поведет буфер?
 
  • Лойс +1
Реакции: Alex_HF

Alex_HF

★✩✩✩✩✩✩
11 Мар 2023
40
16
Насколько я могу судить из приложения посылается строка. Она уже сформирована и изменена быть не может - соответственно и принята тоже без изменений. Как только ваш парсер в ардуине ее обработает - соответственно и примутся изменения. Если что-то менялось локально с помощью кнопок и т.д. в это время - изменения перепишутся на значения из парсера.
Также не забудьте сделать проверку парсера на кол-во аргументов
int am = data.split(); // получаем количество данных (строк должно быть у меня 17)
if (am == 17) {
// применяем параметры
...
}
 
  • Лойс +1
Реакции: amss

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
@Alex_HF,
А может очищать еще и буфер как то, чтобы не было проблем после отправки и принятия?
 

Alex_HF

★✩✩✩✩✩✩
11 Мар 2023
40
16
Я думаю этого не требуется. Я не знаю подробности приложения, но насколько смог понять по скрину данные отправляются по нажатию кнопки - нажали кнопку -> сформировали строку -> отправили по блютуз -> все. Следующее нажатие на кнопку сформирует новую строку.
На стороне ардуины строка сама заново инициализируется при вызове myString = Serial.readStringUntil('\n');
Естественно, это работает только если со стороны приложения данные больше не отправляются на ардуину другими функциями.

Ну или чтоб прям успокоиться напишите
C++:
void loop() {

  if (Serial.available()) {

        myString = Serial.readStringUntil('\n');

        while (Serial.available()) Serial.read();  // <- вычитываем в никуда все что вдруг осталось

   ...

  }
}
 
Изменено:

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
@Alex_HF,
Спасибо, закончу - отпишусь. Просто у меня постоянно отправляется в приложение пакет данных примерно так же. И прогнозирую, что будут сбои, когда один пакет принимается, а другой отправляется в один момент времени.
 

Alex_HF

★✩✩✩✩✩✩
11 Мар 2023
40
16
Каналы передачи/приема в блютуз модуле разделены и могут использоваться одновременно
 
  • Лойс +1
Реакции: amss

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
@Alex_HF,
Здравствуйте нарисовалась еще одна проблема при посылке данных с ардуино в приложение. Отправляется пакет данных постоянно в цикле Loop в нем тексты и цифры, разделенные разделителем " | " отправляю просто :
Serial.print(str6); //22 Название программы № 7
Serial.print("|");//разделитель переменных
Serial.print(str7); //23 Название программы № 8
Serial.print("|");//разделитель переменных
Приложение принимает нормально, но иногда в разных промежутках времени происходит сбой - проскок индекса (то есть на месте данных одного элемента возникает другой) как будто меняется порядок отправки пакета. Пробовал отправлять статический пакет из фиксированных данных - не помогло. Пакет состоит из 41 данного разделенного " | " может просто не успевает принять одно и отправляет поверх другое?
 

Alex_HF

★✩✩✩✩✩✩
11 Мар 2023
40
16


можете также вставить delay(1) после нескольких Serial.print
ну а по-правильному пакет передавать например начиная с некоего маркера
C++:
Serial.print('\a');  // маркер начала пакета
...
Serial.print('\n');  // маркер конца пакета
В приложении надо проверять маркеры и количество переданных параметров.
Возможно, стоит увеличить период отправки пакета - например раз в 1сек.
 
  • Лойс +1
Реакции: amss

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
@Alex_HF,
Спасибо, всё работает, с маркерами начала и конца отлично и четко. осталось немного напильником доточить и всё ок. Например название программы в EEPROM сохраняется со сдвигом на одну ячейку, но думаю разберусь сам. Огромное человеческое спасибо!!!

Ну и еще небольшая хотелка у меня. Как можно реализовать редактирование уже внесенного текста в TextBox. Я циклично записываю в TextBox температурные режимы (то есть число) и нажимая редактировать его чтобы изменить данные он постоянно обновляет и не дает изменить что либо. Пока решение - заливать текст в подсказку, но как то не очень, особенно если нужно изменить одно показание и нужно забивать все 16 TextBox-ов. Может есть идеи?
 

Вложения

  • Лойс +1
Реакции: Alex_HF

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
@Alex_HF,
Жаль, мне понравилось с ним работать. Делает приложения для андроид без проблем.
 

Boroda22

★✩✩✩✩✩✩
23 Фев 2022
251
42
Делает приложения для андроид без проблем.
это очень простое приложение. пока еще никто не создал такой конструктор, в котором можно реализовать что-то стоящее. Если хотите кодить-садитесь за учебники, а не вот это вот всё....
 

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
@Boroda22,
Для моих задач подошло и ладно, а учебники штудировать ради одного проекта, как то поверьте не очень разумно. Главное что я все свои задачи решил, благодаря подсказкам Alex_HF, за что ему огромное спасибо и без этого пафоса как у вас. Лет 25 назад я бы составил вам конкуренцию и поумничал. Но к сожалению Бейсик ушел в мир иной.

Кстати последнюю мою хотелку я решил. Если кому будет интересно - просто добавил переменную. При сохранении она меняется а при записи проверяется. То есть значения в TextBox записываются только один раз и обновляются после нажатия сохранить.
 

Boroda22

★✩✩✩✩✩✩
23 Фев 2022
251
42
Лет 25 назад я бы составил вам конкуренцию и поумничал. Но к сожалению Бейсик ушел в мир иной.
я уже в те года к дельфийскому присматривался, а вы процедурщину вспомнили. Ну да ладно, каждому своё.
Для моих задач подошло и ладно, а учебники штудировать ради одного проекта, как то поверьте не очень разумно.
ключевое тут-"и ладно", вы сами ограничиваете в своих возможностей.
 

amss

✩✩✩✩✩✩✩
9 Апр 2023
14
2
Я не программист, я технарь, хотя и мог им стать, но честно говоря, скучно ужасно сидеть у монитора сутками и пялиться в код. Так что каждому своё. Я уважаю любые профессии и увлечения. Главное что я сделал что хотел, всё работает и даже последний мой вопрос также решил самостоятельно. Так что всем спасибо!
 

monetizedeal

✩✩✩✩✩✩✩
Parsing data between MIT App Inventor and Arduino can sometimes be challenging especially if you are dealing with serial communication over Bluetooth or USB. Some common issues and solutions related to your parsing problems:

1. Mismatch in Data Format
  • Problem: The data format sent from the App Inventor app doesn't match what the Arduino expects.
  • Solution: Make sure the data sent from App Inventor uses a consistent format like plain text, CSV, or JSON. For example, if sending "123,456", the Arduino should parse it expecting a comma-separated format.
Example:

// Arduino Code

if (Serial.available()) {

String data = Serial.readStringUntil('\n'); // Reads until newline character

int value1 = data.substring(0, data.indexOf(',')).toInt();

int value2 = data.substring(data.indexOf(',') + 1).toInt();

Serial.println(value1 + value2); // For example, adding two numbers

}

2. Missing Delimiters
  • Problem: The data is sent as a continuous stream without clear separators.
  • Solution: Use a delimiter (like ,, ;, or |) to separate values and use Arduino's readStringUntil or similar functions to parse.

3. Incorrect Baud Rate
  • Problem: The baud rate in App Inventors Bluetooth setup does not match the Arduino serial baud rate.
  • How you can fix: You need to correct that both sides are using the same baud rate 9600 or 115200.

4. Buffer Overflow
  • Problem: The data sent is too large for Arduino's serial buffer.
  • Solution: Send smaller chunks of data and use flow control to manage communication. You can also increase the buffer size in Arduino if needed.

5. App Inventor Blocks for Parsing
  • Use the Text blocks in App Inventor to format data properly before sending.
    • Concatenate strings or numbers into a single message, eg, "123,456\n".
    • Use App Inventor's Split Text block to parse received data.
Example Block Setup in App Inventor:

Send Data: Combine data into a string before sending.

call BluetoothClient.SendText join("123", ",", "456", "\n")


  1. Receive Data: Use the BluetoothClient.ReceiveText block to process data and split it using delimiters.
6. Debugging
  • Use Arduino's Serial Monitor to debug incoming data.
  • Add print statements in App Inventor to check outgoing messages.

Example End-to-End Setup
  1. MIT App Inventor:
    • Send: "123,456\n"
    • Receive: Split data into components using Split at Delimiter.
  2. Arduino Code:
void setup() {

Serial.begin(9600); // Match baud rate with App Inventor

}


void loop() {

if (Serial.available()) {

String data = Serial.readStringUntil('\n'); // Read until newline

int value1 = data.substring(0, data.indexOf(',')).toInt();

int value2 = data.substring(data.indexOf(',') + 1).toInt();

Serial.print("Value 1: ");

Serial.println(value1);

Serial.print("Value 2: ");

Serial.println(value2);

}

}

Additionally, you can earn extra money to cover your expenses by exploring opportunities through top forex Telegram channels where you can gain insights into forex trading and start making informed investments.