Проблемы обмена данных по Serial

stef776

✩✩✩✩✩✩✩
8 Мар 2023
4
0
Дисклеймер
Здравствуйте, пишу первый раз, поэтому прошу прощения, если не там разместил или неправильно оформил.
Описание проекта
Ардуино. Есть математическая модель емкости, реализуемая на ардуино. Моделируется отдельно процесс налива и слива жидкости из емкости. Все функции скетча ардуино сделаны отдельными функциями для возможности "пересобрать" скетч при необходимости.
Python. Программа на питоне управляет симуляцией на ардуино, общаясь через последовательный порт. Также происходит индикация параметров процесса, получаемых от ардуино.
Информационные потоки: ардуино - отправляет "0,0" в ответ на аналогичный входной текст (служит для подтверждения подключения и успешного обмена); постоянно отправляет данные о параметрах процесса. (на данный момент также повторяет входные сигналы, для отладки). Python - отправляет управляющие сигналы по нажатиям на кнопки.
Кодировка парсера: key + ',' + data + ';'.
data внутри себя тоже может быть разделена запятой для последующего извлечения составляющих.
key 0: код для подключения и отключения. Если питон передает data = "0", происходит запрос подтверждения подключения. Если питон передает data = "1", он сообщает о намерении отключиться от ардуино, запускает на ней процесс перезагрузки (через сторожевого пса). Ардуино отправляет "0,0" как знак успешного получения "0,0" от питона.
Участок кода Python с key = 0:
def connect():                                      #нажатие на кнопку "connect"
    if len(QSerialPortInfo.availablePorts()) != 0:  #если есть доступные порты
        serial.setPortName(ui.comL.currentText())   #выбор имени порта в соответствии с выбранным в списке
        serial.open(QIODevice.ReadWrite)            #открытие соединения
        serial.setDataTerminalReady(False)
        serialSend(0, 0)  # отправка на ардуино
Участок кода с key = 0 на ардуино:
char str [15];                    //создание символьного массива из 15 элементов
    int amount = Serial.readBytesUntil(';', str, 15);   //запись входных данных в массив
    Serial.flush();
    str[amount] = NULL;               //удаление символа конца строки (?)
    Serial.println(str);
    GParser data(str, ',');           //создание объекта GParser из массива str с символом разделителем ','
    data.split();            //разделение полученного текста на подстроки, разделенные ','
    switch (data.getInt(0)) {         //выбор дальнейших действий в зависимости от значения первого полученного символа (дескриптора)
      case 0:
        if (data.getInt(1) == 0){
          Serial.println("0,0");
        } else if (data.getInt(1) == 1){
          wdt_enable(WDTO_1S);
key 1: код для управления режимом симуляции и передачи параметров. Питон отправляет два числа: начальный уровень емкости (флоат) и режим (0 или 1). Режимы: 1 - налив, 0 - слив жидкости из емкости. Ардуино просто присваивает полученные значения в переменные. Ардуино по данному коду отправляет некоторые параметры: h, F1, F2.
Участок кода с key = 1 Python:
def save():
    global data
    if serial.isOpen():  # если есть соединение
        try:  # конструкция для отлова ошибок
            data = str(float(ui.h0.text().replace(",", ".")))  # перевод текста из поля ввода в число
            if ui.rezhim1.isChecked():
                data = data + ",1"
            elif ui.rezhim0.isChecked():
                data = data + ",0"
            else:
                error.setText("Выберите режим!")  # вывести окно ошибки с текстом "Выберите режим!"
                error.exec()
            serialSend(1, data)  # отправка на ардуино
Участок кода с key = 1 Arduino:
case 1:                         //если он равен нулю
        rezhim = data.getInt(1);        //ПРОВЕРИТЬ РАБОТАЕТ ЛИ ТАК
        h = data.getInt(2)/1000;
        break;
К слову режим вроде меняется, так что похоже работает.
Участок кода с key = 1 Arduino (передача):
void print_inf(){
  Serial.print(1);    //отправка дескриптора на компьютер
  Serial.print(",");
  if (isnan(h)||h<0){ h = prev_h;} else { prev_h = h;}   //конструкция, необходимая для обхода ошибки (в новых версиях Arduino IDE число не может быть равно 0, вместо этого выводится Nan
  Serial.print(h*1000);    //отправка текущего значения уровня на компьютер
  Serial.print(",");
  if (isnan(F1)){ F2 = prev_F1;} else { prev_F1 = F1;}    //то же аналогично для расхода жидкости на входе и на выходе
  Serial.print(F1,4);
  Serial.print(",");
  if (isnan(F2)){ F2 = prev_F2;} else { prev_F2 = F2;}
  Serial.print(F2,4);
  Serial.println(";");
key 2: Код для установки параметров емкости и передачи параметров с ардуино. Python отправляет параметры емкости, введенные пользователем. Ардуино принимает данные параметры, присваивает переменные. Также Ардуино отправляет значения степеней открытия клапанов.
Участок кода с key = 2 Python:
def save_param():
    global data
    if serial.isOpen():  # если есть соединение
        try:  # конструкция для отлова ошибок
            data = str(float(ui.de.text().replace(",", "."))) + "," # перевод текста из поля ввода в число
            data = data + str(float(ui.he.text().replace(",", "."))) + ","  # перевод текста из поля ввода в число
            data = data + str(float(ui.hd1.text().replace(",", "."))) + ","  # перевод текста из поля ввода в число
            data = data + str(float(ui.hd2.text().replace(",", ".")))  # перевод текста из поля ввода в число
            serialSend(2, data)  # отправка на ардуино
Участок кода с key = 2 Arduino (прием):
case 2:                         //если он равен 1
        de = data.getInt(1)/1000;     //запись диаметра емкости
        he = data.getInt(2)/1000;     //запись высоты емкости
        hd1 = data.getInt(3)/1000;    //запись высоты входного отверстия
        hd2 = data.getInt(4)/1000;    //запись высоты выходного отверстия
        break;
Участок кода с key = 2 Ардуино (передача):
  Serial.print(2);    //отправка дескриптора на компьютер
  Serial.print(",");
  if (isnan(mim_st)){ mim_st = prev_mim_st;} else { prev_mim_st = mim_st;}
  Serial.print(mim_st*100,0);
  Serial.print(",");
  if (isnan(meo_st)){ meo_st = prev_meo_st;} else { prev_meo_st = meo_st;}
  Serial.print(meo_st*100,0);
  Serial.println(";");
key 3: дескриптор для старта и остановки симуляции. Осуществляется нажатием одной кнопки (кнопка с фиксацией). Для старта Питон отправляет data = '1', для остановки '0'.
Участок кода с key = 3 Python:
def start():
    global data
    if serial.isOpen():  # если есть соединение
        serialSend(3, int(ui.start.isChecked()))  # отправка на ардуино
Участок кода с key = 3 Ардуино:
case 3:                         //если он равен нулю
        start = data.getInt(1);
        break;
Проблема
Не работает должным образом обмен данными. Например, не получается переключить режим работы. Не всегда получается перезагрузить ардуино при подключении.
Также, когда поставил обратную отправку получаемых данных на ардуино заметил, что обратная отправка данных осуществляется только при поступлении новых (код нажатия кнопки возвращается на комп только при нажатии следующей).
Текущий вариант кода не изначальный, к сожалению, я частично исковеркал его попытками все починить.
Ниже привожу код полностью и интерфейс программы на питоне.
Слезно прошу помощи, уже отчаялся
Весь код Python:
#Основной код проекта
from PyQt5 import QtWidgets, uic                    #Импорт необходимых библиотек
from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo
from PyQt5.QtCore import QIODevice
import pyqtgraph as pg

app = QtWidgets.QApplication([])                    #создание окна приложения
ui = uic.loadUi("design.ui")                        #загрузка настроек интерфейса из файла

plot_widget = pg.PlotWidget()                       #создание виджета для графика
ui.plotLay.addWidget(plot_widget)                   #добавление графика в интерфейс
t = []                                              #массив значений времени
h = []                                              #массив значений управляющего воздействия
t.append(0)                                         #добавление в массив начальной точки
h.append(0)                                         #добавление в массив начальной точки

data = ""

plot = plot_widget.plot(t,h)                        #создание объекта-графика
plot_widget.showGrid(x=True, y=True, alpha=1.5)     #вывод сетки на графике
plot_widget.setYRange(0, 10000, padding=0)          #установка границ графика

error = QtWidgets.QMessageBox()                     #создание окна ошибки
error.setWindowTitle("Ошибка")                      #заголовок окна
error.setIcon(QtWidgets.QMessageBox.Warning)        #иконка окна

serial = QSerialPort()                              #создание объекта для работы через последовательный порт
serial.setBaudRate(9600)                          #настройка скорости обмена данными

#Функции необходимые для работы
def serialSend(key, data):                          #функция для отправки данных на ардуино
    txs = str(key) + "," + str(data) + ";"          #сборка сообщения в соответствии с протоколом
    serial.write(txs.encode())                      #отправка сообщения
    #print("python sends: ", txs)

def onRead():                                       #функция для чтения входящих сообщений
    rx = str(serial.readLine(), "utf-8").split(';')[0]  #чтение входящего текста
    print(rx)
    if rx.split(',')[0] == "0":                   #при дескрипторе 1 (в процессе производится разделение по "," и определение дескриптора)
        ui.connL.setText("Connected")               #вывод статуса соединения "Connected"

    elif rx.split(',')[0] == "1":                   #при дескрипторе 3 (в процессе производится разделение по "," и определение дескриптора)
        ui.curr_h.setText(rx.split(',')[1])
        ui.F1.setText(rx.split(',')[2])
        #ui.F2.setText(rx.split(',')[3])
    elif rx.split(',')[0] == "2":  # при дескрипторе 3 (в процессе производится разделение по "," и определение дескриптора)
        ui.mim_st.setText(rx.split(',')[1])
        ui.meo_st.setText(rx.split(',')[2])

        t.append(float(rx.split(',')[2]))           #запись значения времени
        h.append(float(rx.split(',')[1]))           #и управляющего воздействия
        #dsolve(f, y[len(y)-1], x[len(x)-2])         #интегрирование управляющего воздействия
        #y.append(soln[1][len(soln[1])-1])           #запись результата в выходную переменную
        #serialSend(3, '%.3f' % y[len(y)-1])         #отправка полученного значения
        #ui.curzadL.setText('%.3f' % y[len(y)-1])    #вывод текущего значения регулируемой величины
        #ui.mistL.setText('%.3f' %(sp - y[len(y)-1]))#вывод текущей ошибки регулирования
        plot.setData(t,h)                           #обновление данных графика
        if len(t) > 100:                            #удаление первых элементов массивов, при размере больше 100
            t.pop(0)
            h.pop(0)


#Функции нажатий на кнопки
def disconnect():                                   #нажатие на кнопку "Disconnect"
    serialSend(0, 1)  # отправка на ардуино
    serial.close()                                  #закрытие соединения
    ui.connL.setText("not connected")               #обновление статуса на "not connected"
    t.clear()                                       #обнуление массивов
    h.clear()
    t.append(0)                                     #добавление начальной точки
    h.append(0)

def refresh():                                      #нажатие на кнопку "Refresh"
    portList = []                                   #массив со списком доступных для подключения портов
    ports = QSerialPortInfo.availablePorts()        #получение информации о доступных портах
    for port in ports:                              #заполнение массива
        portList.append(port.portName())
    ui.comL.clear()                                 #очистка списка портов в интерфейсе
    ui.comL.addItems(portList)                      #заполнение его данными из массива
    if len(ports) == 0:                             #если нет доступных портов
        disconnect()                                #вызвать функцию отключения

def connect():                                      #нажатие на кнопку "connect"
    if len(QSerialPortInfo.availablePorts()) != 0:  #если есть доступные порты
        serial.setPortName(ui.comL.currentText())   #выбор имени порта в соответствии с выбранным в списке
        serial.open(QIODevice.ReadWrite)            #открытие соединения
        serial.setDataTerminalReady(False)
        serialSend(0, 0)  # отправка на ардуино

def save():
    global data
    if serial.isOpen():  # если есть соединение
        try:  # конструкция для отлова ошибок
            data = str(float(ui.h0.text().replace(",", ".")))  # перевод текста из поля ввода в число
            if ui.rezhim1.isChecked():
                data = data + ",1"
            elif ui.rezhim0.isChecked():
                data = data + ",0"
            else:
                error.setText("Выберите режим!")  # вывести окно ошибки с текстом "Выберите режим!"
                error.exec()
            serialSend(1, data)  # отправка на ардуино
        except ValueError:  # при ошибке перевода
            error.setText("Введите число!")  # вывести окно ошибки с текстом "Введите число!"
            error.exec()
    else:  # если контроллер не подключен, вывести соответствующую ошибку
        error.setText("Контроллер не подключен")
        error.exec()

def save_param():
    global data
    if serial.isOpen():  # если есть соединение
        try:  # конструкция для отлова ошибок
            data = str(float(ui.de.text().replace(",", "."))) + "," # перевод текста из поля ввода в число
            data = data + str(float(ui.he.text().replace(",", "."))) + ","  # перевод текста из поля ввода в число
            data = data + str(float(ui.hd1.text().replace(",", "."))) + ","  # перевод текста из поля ввода в число
            data = data + str(float(ui.hd2.text().replace(",", ".")))  # перевод текста из поля ввода в число
            serialSend(2, data)  # отправка на ардуино
        except ValueError:  # при ошибке перевода
            error.setText("Введите все параметры корректно!")  # вывести окно ошибки с текстом "Введите все параметры корректно!"
            error.exec()
    else:  # если контроллер не подключен, вывести соответствующую ошибку
        error.setText("Контроллер не подключен")
        error.exec()

def start():
    global data
    if serial.isOpen():  # если есть соединение
        serialSend(3, int(ui.start.isChecked()))  # отправка на ардуино
    else:  # если контроллер не подключен, вывести соответствующую ошибку
        error.setText("Контроллер не подключен")
        error.exec()

ui.disconnB.clicked.connect(disconnect)             #подключение функций к кнопкам
ui.refrB.clicked.connect(refresh)
ui.connB.clicked.connect(connect)
ui.save.clicked.connect(save)
ui.start.clicked.connect(start)
ui.save_param.clicked.connect(save_param)
serial.readyRead.connect(onRead)                    #подключение функции чтения сообщений


def main():
    refresh()                                       #вызов функции обновления
    ui.show()                                       #вызов интерфейса
    app.exec()                                      #вызов окна приложения

if [B]name[/B] == "[B]main[/B]":
    main()
Весь код Ардуино:
#include "GParser.h"                  //подключение библиотеки для парсинга данных
#include "avr/wdt.h"
//Исходные данные
//Емкость
float de = 7.58;      //диаметр емкости
float se = 3.14*(de/2)*(de/2);      //площадь сечения емкости
float he = 8.83;      //высота емкости
//патрубки
float d1 = 0.025;    //внутренний диаметр входного патрубка
float d2 = 0.025;    //внутренний диаметр выходного патрубка
float s1 = 3.14*(d1/2)*(d1/2);      //площадь входного отверстия
float s2 = 3.14*(d2/2)*(d2/2);      //площадь входного отверстия
float hd1 = 0;     //высота расположения входного патрубка
float hd2 = 0;     //высота расположения выходного патрубка
//Датчики положения
#define MIM A0      //датчик положения МИМ
float prev_mim_st = 1, mim_st = 1;   //степень открытия МИМ
#define MEO A1      //датчик положения МЭО
float prev_meo_st = 1, meo_st = 1;   //степень открытия МЭО
int mim=0, mim_i=0, meo=0, meo_i=0;   //промежуточные переменные
float k = 0.01;     //коэффициент сглаживания

int p = 987;        //плотность жидкости
float Kvy = 16;     //условная пропускная способность клапана
float Kvy_i = 16;   //условная пропускная способность при данной степени открытия
//Программные переменные
float V1 = 0;      //Скорость наполнения через входной патрубок
float V2 = 0;      //Скорость истечения из выходного патрубка
float F1 = 0;      //Расход на входе
float prev_F1 = 0;
float F2 = 0;      //Расход на выходе
float prev_F2 = 0;
float g = 9.81;    //ускорение свободного падения

float dP = 0;     //общее гидравлическое сопротивление канала
float dPkl = 0;   //гидравлическое сопротивление клапана
float dPe = 0;   //гидравлическое сопротивление столба жидкости
//управление симуляцией
boolean state = true;   //состояние: 1 - норма, 0 - авария
boolean rezhim = false;  //режим: 1 - налив, 0 - слив
boolean start = false;  //начало симуляции

float h = 4;       //текущий уровень в емкости
float h_ekv = 4;       //уровень в емкости с учетом гидравл сопротивл клапана
float prev_h = 1.01;   //предыдущее значение этой переменной
float V = se*h;   //текущий объем

uint32_t timing = millis();    //переменная для начала отсчета таймера 1
uint32_t timing2 = millis();   //переменная для начала отсчета таймера 2
int stepp = 1;  //коэффициент ускорения/замедления переходного процесса

void setup() {    //настройки для обмена информацией между ардуино и компьютером
  wdt_disable();
  Serial.begin(9600);                                 
  Serial.setTimeout(50);
  Serial.flush();
  //Serial.println("h,F2,F1");
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}

void loop() {
  //if (Serial.available()>0) {           //если на плату пришел сигнал с компьютера
    //parser();
  //}
  if (millis() - timing > 100){   //таймер, запускающий команды ниже каждые 100 милисекунд
    timing = millis();
    
    if (start) {
      float t = (float(millis() - timing2)/1000);
      if (test()){
        if (rezhim == 1) {
          naliv();
          Serial.println("naliv");
          F2 = 0.0001;
          stepp = 1;
        } else {
          sliv();
          Serial.println("sliv");
          F1 = 0.0001;
          stepp = 500;
        }
        uroven();
        //klapan ();
      }
      print_inf();
    }
  }
}

//Опустошение емкости
void sliv () {
  dPe = p*g*(h-hd2);
  Kvy_i = mim_st*Kvy;
  dPkl = sq(F2)/sq(Kvy_i)*1000000;
  dP = dPe - dPkl;
  h_ekv = dP/(p*g);
  V2 = sqrt(2*g*(h_ekv-hd2));   //расчет скорости истечения жидкости в соответствии с формулой 
  F2 = s2*V2;   //пересчет скорости в объемный расход
}

//Наполнение емкости
void naliv (){
  dPe = p*g*(h-hd1)/1000000;
  Kvy_i = mim_st*Kvy;
  dPkl = sq(F1)/sq(Kvy_i);
  dP = dPkl + dPe;
  F1 = sqrt((0.0974-dP)/0.012);
}

//расчет уровня из расхода на входе и на выходе
void uroven () {
  h = (V - (F2-F1)*stepp)/se;   //расчет текущего уровня в сосуде
  V = se*h;   //расчет текущего объема в сосуде
}

//отправка сообщений на компьютер
void print_inf(){
  Serial.print(1);    //отправка дескриптора на компьютер
  Serial.print(",");
  if (isnan(h)||h<0){ h = prev_h;} else { prev_h = h;}   //конструкция, необходимая для обхода ошибки (в новых версиях Arduino IDE число не может быть равно 0, вместо этого выводится Nan
  Serial.print(h*1000);    //отправка текущего значения уровня на компьютер
  Serial.print(",");
  if (isnan(F1)){ F2 = prev_F1;} else { prev_F1 = F1;}    //то же аналогично для расхода жидкости на входе и на выходе
  Serial.print(F1,4);
  Serial.print(",");
  if (isnan(F2)){ F2 = prev_F2;} else { prev_F2 = F2;}
  Serial.print(F2,4);
  Serial.println(";");
  Serial.print(2);    //отправка дескриптора на компьютер
  Serial.print(",");
  if (isnan(mim_st)){ mim_st = prev_mim_st;} else { prev_mim_st = mim_st;}
  Serial.print(mim_st*100,0);
  Serial.print(",");
  if (isnan(meo_st)){ meo_st = prev_meo_st;} else { prev_meo_st = meo_st;}
  Serial.print(meo_st*100,0);
  Serial.println(";");
}

//регулирование клапанами
void klapan () {
  mim_i = analogRead(MIM);        //чтение сигнала с переменного резистора
  mim += (mim_i - mim) * k;       //экспоненциальный фильтр для сглаживания шумов
  mim_st = map(-mim, -250, -5, 0, 1);   //преобразование измерений в степень открытия        //НАСТРОИТЬ
  meo_i = analogRead(MEO);        //чтение сигнала с переменного резистора
  meo += (meo_i - meo) * k;       //экспоненциальный фильтр для сглаживания шумов
  meo_st = map(-meo, -250, -5, 0, 1);   //преобразование измерений в степень открытия        //НАСТРОИТЬ
}

//проверка ограничений
boolean test() {
  if (h < he - 0.3) {
    state = true;
  } else {
    state = false;
    V1 = 0;
    F1 = 0;
  }
  return state;
}

//чтение входных данных
void serialEvent() {
  while (Serial.available()) {
    char str [15];                    //создание символьного массива из 15 элементов
    int amount = Serial.readBytesUntil(';', str, 15);   //запись входных данных в массив
    Serial.flush();
    str[amount] = NULL;               //удаление символа конца строки (?)
    Serial.println(str);
    GParser data(str, ',');           //создание объекта GParser из массива str с символом разделителем ','
    data.split();            //разделение полученного текста на подстроки, разделенные ','
    switch (data.getInt(0)) {         //выбор дальнейших действий в зависимости от значения первого полученного символа (дескриптора)
      case 0:
        if (data.getInt(1) == 0){
          Serial.println("0,0");
        } else if (data.getInt(1) == 1){
          wdt_enable(WDTO_1S);
        }
        break;
      case 1:                         //если он равен нулю
        rezhim = data.getInt(1);        //ПРОВЕРИТЬ РАБОТАЕТ ЛИ ТАК
        h = data.getInt(2)/1000;
        break;
      case 2:                         //если он равен 1
        de = data.getInt(1)/1000;     //запись диаметра емкости
        he = data.getInt(2)/1000;     //запись высоты емкости
        hd1 = data.getInt(3)/1000;    //запись высоты входного отверстия
        hd2 = data.getInt(4)/1000;    //запись высоты выходного отверстия
        break;
      case 3:                         //если он равен нулю
        start = data.getInt(1);
        break;
     }
  }
}
1678286782521.png
 

Вложения

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@stef776,
Дисклеймер
Если есть проблема с каким то обменом, то принято написать тестовую программу воспроизводящую именно эту проблему. исключив все алгоритмы втекания и вытекания воды, а так получается большой код который у некоторых может отбить желание разбираться.

Разбираться во всей программе не могу, напишу то, что увидел только по части serialEvent
Вы пишите про формат key + ',' + data + ';'. , но где у вас проверяется, что вы получили что то начинающееся с key и заканчивающееся на ;
int amount = Serial.readBytesUntil(';', str, 15); Вернет вам содержимое буфера Serial в любом случае, не важно на что он начинается. А признака того, что окончилось на ; тоже нет.
Зачем вы вызываете Serial.flush();
str[amount] = NULL; //удаление символа конца строки (?)
Не удаление, а наоборот установка символа конца строки.
По парсингу тоже вопросы. Правда тут я не уверен, надо проверять.
switch (data.getInt(0))
data.getInt(0) вернет 0 и в том случае, если строка не может быть переведена в Int.

Резюме.
Напишите и отладьте сначала программу парсинга. Убедившись, что она правильно работает не только с "правильными" данными, но и игнорирует ошибочные.
 
  • Лойс +1
Реакции: stef776

stef776

✩✩✩✩✩✩✩
8 Мар 2023
4
0
@Bruzzer, Благодарю, действительно не подумал о вынесении парсинга в отдельную программу на тесты. На счет других замечаний также согласен, нос текущими сроками не уверен, что смогу этим заняться. Надеюсь смогу обратиться к вам в случае, если не удастся разобраться предложенным способом.
 

stef776

✩✩✩✩✩✩✩
8 Мар 2023
4
0
Кажется я разобрался с проблемами.
Первая проблема:
Также, когда поставил обратную отправку получаемых данных на ардуино заметил, что обратная отправка данных осуществляется только при поступлении новых (код нажатия кнопки возвращается на комп только при нажатии следующей).
Дело в том, что Python читал Serial построчно (Применялась функция serial.readLine()):
Обработчик Serial:
def onRead():                                       #функция для чтения входящих сообщений
    rx = str(serial.readLine(), "utf-8").split(';')[0]  #чтение входящего текста
    print(rx)
    if rx.split(',')[0] == "0":                   #при дескрипторе 1 (в процессе производится разделение по "," и определение дескриптора)
        ui.connL.setText("Connected")               #вывод статуса соединения "Connected"
Соответственно, когда на него приходило несколько строк, он обрабатывал только одну, другие оставались в буфере. При следующем поступлении входных данных он считывал не новую строку, а последнюю необработанную. Таким образом появлялась задержка.
Решение: замена serial.readLine() на serial.readAll(). Но вместе с этим необходимо будет заменить и последующий код.
Вторая проблема:
Не работает должным образом обмен данными. Например, не получается переключить режим работы. Не всегда получается перезагрузить ардуино при подключении.
Вероятная причина (помимо вероятных ошибок в коде): конструкция switch case как обработчик дескриптора на ардуино. При решении проблемы я тестировал код ардуино через монитор порта, и один и тот же код работал для дескриптора "1", но не работал для дескриптора "3".
Решение: замена switch case на if else. Заработало сразу и без проблем.
Благодарю за помощь Bruzzer
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@stef776,
Я так понял, это учебное задание, так что заботится об надежности особо не надо.
Но хотя бы для минимума рекомендую добавить
if(!Serial.find("key,"))
return;
перед
int amount = Serial.readBytesUntil(';', str, 15); //запись входных данных в массив

Так вы хоть будете обрабатывать только часть принятых данных начинающихся с key, И в добавок не записывать key, в начало str
В прежнем алгоритме у вас строка начиналась с key,
switch (data.getInt(0))
пытался получить getInt из строки 'key' , и соответственно всегда возвращал 0
 

stef776

✩✩✩✩✩✩✩
8 Мар 2023
4
0
Я так понял, это учебное задание, так что заботится об надежности особо не надо.
Да, верно подмечено.
Но уже после вашего предыдущего ответа я понял важность такой фильтрации информации и решил добавить стартовый символ '+'.
Этот выбор обусловлен тем, что в моем протоколе key - это число и поэтому он не позволяет отфильтровать все лишние данные (так как и нецелевые данные могут начинаться с чисел).
В прежнем алгоритме у вас строка начиналась с key,
switch (data.getInt(0))
пытался получить getInt из строки 'key' , и соответственно всегда возвращал 0
Не совсем так. У меня не передавалась строка 'key', а передавалось сразу число, являющееся ключом. Я просто использовал это слово для описания структуры сообщения здесь:
Кодировка парсера: key + ',' + data + ';'.