"Чёрная магия" при передаче данный в приложение

suiginto
suiginto аватар
Offline
Зарегистрирован: 04.05.2017

Не знаю к чему это относится. к программированию или вопросам с железом, поэтому в общий.

Создал приложение в Qt, в нем использую библиотеку QSerialPort (позволяет приложению работать с последовательным портом) и QSerialPortInfo (помогает работать с QSerialPort).
Схема взаимодействия ардуины (что уна, что мега одинаково)  и приложения:
Приложение отправляет по порту (USB кабель обыкновенный) 0 (стартовое число).
Ардуина принимает данные, переводит в число, увеличивает на 1, отправляет по порту приложению.
Приложение принимает данные (записывает в текстовик данные с ардуины, и также записывает данные которые отправит ей), переводит в число, увеличивает на 1, и отправляет ардуине.
и тд до миллиона.

По идее в текстовике должен быть список от 0 до миллиона но увы происходит чёрная магия. В какой-то момент (максимум где-то с 20000) , случается следующее:

Например число 23456 приходит в компьютер в виде 234, и отдельно 56. естественно всё идёт по известному направлению, внезапные цифры, далее могут вообще начать появляться отрицательные числа. Бывает что ардуина отправляет:

..., 128, 130, 13, 132, 134, ... (приложение вывело 13, и при этом спокойно продолжило счёт как ни в чём не бывало)

Вот тут вообще цирк

402
404
406
408
410
4           (откуда и почему)
412
414
416
418
420
422
424
426
428
4282
4284
42842         (внезапная двойка)
-226922        (чёрная магия)
-303122
-16554
-165542
-17010
-170082
265282
31402
314042
-136362
-52882
126562
-45082
204562
79562
140282
92122
265882
37402
-281322
-191762
-17066
-170642
259682
-2460
-24602
-246002
161442
303722
-23956
-239562
22584
225842
292362
302202
-254762
7384
73842
8308
83082
17548
175482
-21124
-211242
0
-146322
-1
-152482
0
-214082
-174
-174722
218882
22276
222762
26156
261562
-580
-5802
-5800
-58002
7536
75362
9828
98282
32748
327482
-196
-1962
-1960
-19602
-19600
-196002
60
6082
6084
60842
-4692
-46922
18616
186162
-10444
-104442
4166
41662
-23872
-238722
-27914
-27912
-279122
-16976

Протокол передачи данных по идее должен не допускать подобных ситуаций, но всё таки бывает, и я хз почему так происходит.

Кто знает что с этим делать и почему так происходит прошу, подскажите.

Вот cpp проекта


#include <iostream>
#include <QSerialPort>
#include <QDebug>
//#include <QByteArray>
#include <QSerialPortInfo>
#include <fstream>


QByteArray inputArdPc(QSerialPort *arduinoi);
ofstream outputFile("outputFile.txt");

int main()
{
    QSerialPort *arduino = new QSerialPort;
    static const quint16 arduino_uno_vendor_id = 9025;
    static const quint16 arduino_uno_product_id = 67;
    QString arduino_port_name = "";
    bool arduino_is_available = false;

    qDebug() << "Number of available ports: " << QSerialPortInfo::availablePorts().length();
    foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()){
        qDebug()<< endl << "Has vendor ID: " << serialPortInfo.hasVendorIdentifier();
        if(serialPortInfo.hasVendorIdentifier()){
            qDebug() << "Vendor ID: " << serialPortInfo.vendorIdentifier();
        }
        qDebug() << "Has Product ID: " << serialPortInfo.hasProductIdentifier();
        if(serialPortInfo.hasProductIdentifier()){
            qDebug() << "Product ID: " << serialPortInfo.productIdentifier();
        }
    }

    foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()){
        if(serialPortInfo.hasVendorIdentifier() && serialPortInfo.hasProductIdentifier()){
            if(serialPortInfo.vendorIdentifier() == arduino_uno_vendor_id){
                if(serialPortInfo.productIdentifier() == arduino_uno_product_id){
                    arduino_port_name = serialPortInfo.portName();
                    arduino_is_available = true;
                    qDebug() << "Conect: yes" ;
                }
            }
        }
    }

    if(arduino_is_available){
        // open and configure the serialport
        arduino->setPortName(arduino_port_name);
        arduino->open(QSerialPort::ReadWrite);
        arduino->setBaudRate(QSerialPort::Baud115200);
        arduino->setDataBits(QSerialPort::Data8);
        arduino->setParity(QSerialPort::NoParity);
        arduino->setStopBits(QSerialPort::OneStop);
        arduino->setFlowControl(QSerialPort::NoFlowControl);
        qDebug() << "Inicialization: yes" ;
    }else{
        // give error message if not available
        qDebug() << "Port error, Couldn't find the Arduino!";
    }
    int h = -1;
    while(h != 666){
        arduino->waitForReadyRead(500);
        if(arduino->isWritable()){
            h = -1;
            cout << "\nPrint comand\n";
            char chararr[10];
            cin >> chararr;
            //QByteArray QBAi = chararr;
            QByteArray QBAi = QByteArray::number(0);
            qint64 flag = 0;
            while (QBAi.toInt() != 1000000){
                if(arduino->isWritable()){
                    flag = arduino->write(QBAi);
                    int udlod = inputArdPc(arduino).toInt();
                    ++udlod;
                    QBAi = QByteArray::number(udlod);
                }
            }

//sendCommand();
            int f = sendCommand( "command",  3);
            cout << f;
            QString che = "fghjk";
            std::string tststr = getStringCommand(arduino);

             if(arduino->isReadable()){
                qDebug() << "Get data:";
                QByteArray QBAtest;
                arduino->waitForBytesWritten(1);
                arduino->flush();
                arduino->waitForReadyRead(1);

                while (arduino->bytesAvailable() || arduino->waitForReadyRead(10)) {
                    while (arduino->bytesAvailable()) {
                        qDebug() << "Buf size: " << arduino->bytesAvailable();
                        qDebug() << "Set " << arduino->readAll() << endl;
                    }
                }
            }
        }
    }
    outputFile.close();
    arduino->close();
    delete arduino;
}

QByteArray inputArdPc(QSerialPort *arduinoi){
    QByteArray QBAtest = "";
    if(arduinoi->isReadable()){
        arduinoi->waitForBytesWritten(1);
        //arduinoi->flush();
        arduinoi->waitForReadyRead(1);
        int bufSize = 0;
        while(arduinoi->bytesAvailable() || arduinoi->waitForReadyRead(5)){
            qint64 intege = arduinoi->bytesAvailable();
            bufSize += static_cast<int>(intege);
            QBAtest += arduinoi->readAll();
arduinoi->waitForReadyRead(1);
            outputFile << QBAtest.toInt() << endl;
        }
        return QBAtest;
    }
    return QBAtest;
}

 

Logik
Offline
Зарегистрирован: 05.08.2014

//число 23456 приходит в компьютер в виде 234, и отдельно 56

Задумайтесь что значить отдельно? Вы просто в своем коде проверяете принятую информацию в тот момент времени, когда 234 уже приняты а 56 - еще нет. Вероятно проблема на 2-х сторонах обмена. Смотрим на вызов arduinoi->bytesAvailable(), а где проверка сколько там байт принято? Вся остальная магия с отрицательными - отсюда прет. Решение проблемы - придумываем или выбираем протокол, как набор правил для целостной передачи. 

ПС. Надо отметить детальное описание проблемы, не часто нас тут радуют этим.

b707
Offline
Зарегистрирован: 26.05.2017

suiginto пишет:

Протокол передачи данных по идее должен не допускать подобных ситуаций.

насколько я вижу из кода, у вас какой-либо "протокол передачи" начисто отсутствует. Протокол должен четко указывать начало и конец передаваемых данных, а по хорошему еще отлавливать ошибки, например при помощи контрольной суммы. Вы же просто шлете в сериал байты, не заботясь о том, где у них начало, где конец.

Например, если вы непрерывно шлете в порт числа 1 2 3 4 - какое это число, по вашему? 1234? а почему не 2341 ? или даже два числа - 34 и 12 ? Как программа на другом конце линии должна различать эти варианты?

Цитата:
Кто знает что с этим делать.

придумать свой протокол с маркерами начала и конца сообщения

 

suiginto
suiginto аватар
Offline
Зарегистрирован: 04.05.2017

arduino->setParity(QSerialPort::NoParity);
arduino->setStopBits(QSerialPort::OneStop);

В этих строчках по идее задаются всякие стопбайты, чётность и тд. Но как и что с ними делать я не понял даже поковыряв документацию на оф. сайте.

b707
Offline
Зарегистрирован: 26.05.2017

suiginto пишет:

arduino->setParity(QSerialPort::NoParity);
arduino->setStopBits(QSerialPort::OneStop);

В этих строчках по идее задаются всякие стопбайты, чётность и тд. Но как и что с ними делать я не понял даже поковыряв документацию на оф. сайте.

эти параметры вам трогать не надо. Это нижний уровень передачи, он работает на уровне сборки байта из отдельных битов. Это все уже реализовано в драйвере последовательного порта.

Вам нужно придумать протокол верхнего уровня - как из отдельных байтов собрать число

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

А можно  так: В такой ситуации сначала Serial.flush () - дождаться конца передачи. А потом прочитать строку: Serial.readString() ? Я слабый в программировании (поэтому, титаны, не пинайте пожалуйста), но у меня такая конструкция работает (правда, с проверкой начала, конца и длины строки).

negavoid
Offline
Зарегистрирован: 09.07.2016

Sonologist пишет:

А можно  так: В такой ситуации сначала Serial.flush () - дождаться конца передачи. А потом прочитать строку: Serial.readString() ? Я слабый в программировании (поэтому, титаны, не пинайте пожалуйста), но у меня такая конструкция работает (правда, с проверкой начала, конца и длины строки).

Всё правильно, это и есть та самая, ваша личная, высокоуровневая реализация протокола обмена :)

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

negavoid пишет:

Всё правильно, это и есть та самая, ваша личная, высокоуровневая реализация протокола обмена :)

Я уже упоминал что пока еще новичок, причем, очень даже слабый новичок. Отсюда даже не понял: это поощрение или ирония? В первом случае - спасибо, во втором - очень хотелось бы нормальных  комментариев: в чем я не прав?  Проблема тут в том, что скетч нормально работает (а без флаша - не нормально). Выскакивала бы ошибка - я б ее нашел и устранил, а тут... А то "художника каждый может обидеть" :)

negavoid
Offline
Зарегистрирован: 09.07.2016

Гляжу иногда и ваши темы, вы хорошо и быстро учитесь. Это было поощрение.

negavoid
Offline
Зарегистрирован: 09.07.2016

Ага, это хороший проект, мне тоже нравится :)

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

negavoid пишет:

Гляжу иногда и ваши темы, вы хорошо и быстро учитесь. Это было поощрение.

Спасибо на добром слове. Но увы, учусь я далеко не так быстро, как надо, да и с "хорошо" - тоже сомнение: за такое "хорошее обучение" я своих курсантов на повторку отправляю :)

suiginto
suiginto аватар
Offline
Зарегистрирован: 04.05.2017

Logik пишет:

//число 23456 приходит в компьютер в виде 234, и отдельно 56

Задумайтесь что значить отдельно? Вы просто в своем коде проверяете принятую информацию в тот момент времени, когда 234 уже приняты а 56 - еще нет. Вероятно проблема на 2-х сторонах обмена. Смотрим на вызов arduinoi->bytesAvailable(), а где проверка сколько там байт принято? Вся остальная магия с отрицательными - отсюда прет. Решение проблемы - придумываем или выбираем протокол, как набор правил для целостной передачи. 

ПС. Надо отметить детальное описание проблемы, не часто нас тут радуют этим.

ну так стоит алгоритм примерно следующий. Принимать данные пока данные для принятия есть. когда данные заканчиваются (тоже хз как он понимает что они кончились, то он получившуюся строку отправляет на обработку). Но как это реализовано в библиотеке я хз. Просто когда я думаю о протоколе своём. То мне кажется что я мастырю протокол в протоколе. А это нагрузки , это ест скорость и вообще фи.   

svm
Онлайн
Зарегистрирован: 06.11.2016

Вот здесь посмотри  http://arduino.ru/forum/obshchii/multimetr-arduinoandroid  в скетче строки 60-69. Передаются через блютус с андроида. Начало "S" конец "Q" данные "D". Между ними данные.

Logik
Offline
Зарегистрирован: 05.08.2014

suiginto пишет:

Logik пишет:

//число 23456 приходит в компьютер в виде 234, и отдельно 56

Задумайтесь что значить отдельно? Вы просто в своем коде проверяете принятую информацию в тот момент времени, когда 234 уже приняты а 56 - еще нет. Вероятно проблема на 2-х сторонах обмена. Смотрим на вызов arduinoi->bytesAvailable(), а где проверка сколько там байт принято? Вся остальная магия с отрицательными - отсюда прет. Решение проблемы - придумываем или выбираем протокол, как набор правил для целостной передачи. 

ПС. Надо отметить детальное описание проблемы, не часто нас тут радуют этим.

ну так стоит алгоритм примерно следующий. Принимать данные пока данные для принятия есть. когда данные заканчиваются (тоже хз как он понимает что они кончились, то он получившуюся строку отправляет на обработку). Но как это реализовано в библиотеке я хз. Просто когда я думаю о протоколе своём. То мне кажется что я мастырю протокол в протоколе. А это нагрузки , это ест скорость и вообще фи.   

Верно кажется. Это называется стек протоколов, в котором каждый сверху опирается на нижний. Это типовой подход.

Признаком завершения данных может быть уникальный набор символов, заведомо отсутствующий в данных или таймаут. Таймаут предпочтительней в том смысле что , его как правило все равно придётся проверять чтоб определять наличие коннекта.

 

b707
Offline
Зарегистрирован: 26.05.2017

suiginto пишет:

проблема так и не решена,  хотя  советуют колхозить свой протокол передачи данных. Хотя я думаю есть готовые решения без костылей. Но их ещё никто не высказал


то есть вы считаете, "колхозить свой протокол" - это костыль? - вы ошибаетесь. Никаких простых "готовых решений" не существует, если вас интересует надежность -придется разрабатывать протокол самому или в крайнем случае скопипастить у кого-то готовый

nik182
Offline
Зарегистрирован: 04.05.2015

На самом деле протоколов поверх rs232 или RS485 немного. Интернет даёт 3-4. Посмотреть описание можно быстро и прикинуть что больше подходит.

suiginto
suiginto аватар
Offline
Зарегистрирован: 04.05.2017

Logik пишет:

suiginto пишет:

Logik пишет:

//число 23456 приходит в компьютер в виде 234, и отдельно 56

Задумайтесь что значить отдельно? Вы просто в своем коде проверяете принятую информацию в тот момент времени, когда 234 уже приняты а 56 - еще нет. Вероятно проблема на 2-х сторонах обмена. Смотрим на вызов arduinoi->bytesAvailable(), а где проверка сколько там байт принято? Вся остальная магия с отрицательными - отсюда прет. Решение проблемы - придумываем или выбираем протокол, как набор правил для целостной передачи. 

ПС. Надо отметить детальное описание проблемы, не часто нас тут радуют этим.

ну так стоит алгоритм примерно следующий. Принимать данные пока данные для принятия есть. когда данные заканчиваются (тоже хз как он понимает что они кончились, то он получившуюся строку отправляет на обработку). Но как это реализовано в библиотеке я хз. Просто когда я думаю о протоколе своём. То мне кажется что я мастырю протокол в протоколе. А это нагрузки , это ест скорость и вообще фи.   

Верно кажется. Это называется стек протоколов, в котором каждый сверху опирается на нижний. Это типовой подход.

Признаком завершения данных может быть уникальный набор символов, заведомо отсутствующий в данных или таймаут. Таймаут предпочтительней в том смысле что , его как правило все равно придётся проверять чтоб определять наличие коннекта.

 


А не получится так что я своим протоколом перебью их протокол, он точно не полезет смотреть данные в которых может найти какой то символ особый который сочтет за команду. Хотя щас думаю что нет.
Мдамуж конечно, протокол ascii тогда чем занимается? Я вот смотрел его описание, вроде там все есть, закрывающие и тд вещи. Или у меня он по 1 символу передает?

b707
Offline
Зарегистрирован: 26.05.2017

suiginto пишет:
Мдамуж конечно, протокол ascii тогда чем занимается? Я вот смотрел его описание, вроде там все есть, закрывающие и тд вещи. Или у меня он по 1 символу передает?

а можно увидеть ссылку на описание "протокола ascii" ?  - жутко интересно

suiginto
suiginto аватар
Offline
Зарегистрирован: 04.05.2017

b707 пишет:

suiginto пишет:
Мдамуж конечно, протокол ascii тогда чем занимается? Я вот смотрел его описание, вроде там все есть, закрывающие и тд вещи. Или у меня он по 1 символу передает?

а можно увидеть ссылку на описание "протокола ascii" ?  - жутко интересно


Фак, запутали. https://rusautomation.ru/omx-380pm вот тут я увидел что по рске485 он поддерживает аски, и подумал что это протокол.
Есть если я правильно щас понял то это коробка просто отправляет символы в виде чисел на аски таблице? И ардуинка также работает с приложением? Просто это както совсем не явно описано это, то что рска у ардуины 232 это есть, а вот что она шлет неизвестно, есть там какие-то протоколы, нету?
На всякий лучше сделаю свой протокол с контрольной суммой и символами. Есть мысль переводить данные в коды аски таблицы, складывать их и отправлять первой цифровой которую устройство схватит и глянет сходится или нет, ну и спец.символов закрывающих добавить. Модбас например почти как хтмл все тегами делает

Logik
Offline
Зарегистрирован: 05.08.2014

//А не получится так что я своим протоколом перебью их протокол, он точно не полезет смотреть данные в которых может найти какой то символ особый который сочтет за команду

Да. Это одна из проблем которую решают при проектировании протоколов. Придуманы несколько подходов

.
//Мдамуж конечно, протокол ascii тогда чем занимается? Я вот смотрел его описание, вроде там все есть, закрывающие и тд вещи. 

Это не совсем протокол, но один из.подходов иллюстрирует. Для передачи текста годится. В тексте нет символов совпадающих с этими управляющими. И потому путаница исключена. А как быть с произвольными двоичными данными? Например перекодировать их в текст некоторым образом. Читаем Base64. А можно и другими хитрыми путями.

b707
Offline
Зарегистрирован: 26.05.2017

suiginto пишет:
то это коробка просто отправляет символы в виде чисел на аски таблице? И ардуинка также работает с приложением? Просто это както совсем не явно описано это, то что рска у ардуины 232 это есть, а вот что она шлет неизвестно, есть там какие-то протоколы, нету?

старайтесь использовать общепринятые термины, если хотите, чтобы вас кто-то понял. Что такое "рска у ардуины 232" -о чем вы вообще? Какое отношение имеет эта ссылка к вашей программе на QT ?

Цитата:
Есть мысль переводить данные в коды аски таблицы, складывать их и отправлять первой цифровой которую устройство схватит и глянет сходится или нет, ну и спец.символов закрывающих добавить.

ничего не понял

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

Цитата:
Есть мысль переводить данные в коды аски таблицы, складывать их и отправлять первой цифровой которую устройство схватит и глянет сходится или нет, ну и спец.символов закрывающих добавить.

ничего не понял

Потому что у тебя модбас не тегами.

MaksVV
Offline
Зарегистрирован: 06.08.2015

Можно такой вариант