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
Я не программист, я технарь, хотя и мог им стать, но честно говоря, скучно ужасно сидеть у монитора сутками и пялиться в код. Так что каждому своё. Я уважаю любые профессии и увлечения. Главное что я сделал что хотел, всё работает и даже последний мой вопрос также решил самостоятельно. Так что всем спасибо!