Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Convert 2 bytes QByteArray read from serial port into a int



  • Dear all,

    I have read a lot about how to convert a QByteArray into a int but without success in my case... That's why I post on this topic

    First, I have an arduino (MEGA) code which send and int with seria.write() function. For that, I realize an "union"

    union INT{
      int value;
      byte stream[2];
    };
    
    INT my_int
    

    And I stream it ( my_int.value= 125) over serial port:

    void setup(){
    serial.begin(9600);
    my_int.value = 125;   //my_int.stream =[7D, 00]
    
    void loop(){
    
    for (int i = 0; i<2; i++){
     Serial.write(my_int.stream[i]);
    }
    
    }
    

    On Qt side, located in my computer, I receive correctly the byte stream thanks to QSerialPort and readAll() function. Then I store it in a QByteArray as mentionned in QIODevice Class.

    I have a serialport class (serialport.h & serialport.cpp). I have a slot called onReadData() which is triggered by &QSerialPort::readyRead() signal. This is the nominal way to do as explained in many tutorials about how to receive datas from serial port on QT.

    This slot looks like:

    void SerialPort::onReadData()
    {
    
        if(arduino->bytesAvailable()>0){        
    
            QByteArray incomingdata = arduino->readAll();           
           qDebug()<<"incomingdata  = " << incomingdata ;
           qDebug()<<"incomingdata in HEX format  = " << incomingdata.toHex();
    

    This is working well !

    1-qDebug() shows:  incomingdata = "}\x00" 
    2-qDebug() shows:  incomingdata in HEX format = "7d00" 
    

    My problems start NOW !
    From my QByteArray incomingdata, how can I get back my_int = 125 ?

    I believed a union function was possible but it's not the case with QObject
    I tried with QDatastream but without success. I have tried both BigEndian and LittleEndian Byteorder.

            QByteArray ba("7d00");
            QDataStream ds(ba);
            ds.setByteOrder(QDataStream::BigEndian);  //same fail with LittleEndian
            qint16 x
                    ds>>x;
            qDebug() << "x = "<< x;
    

    I also tried with memcpy method and i doesn't work ... In fact, I get a value but this is not 125...

            QByteArray ba("7d00");
            qint16  outIntVar;
            memcpy(&outIntVar, ba.data(), sizeof(qint16));
    

    So I hope someone can tell me how to do to reach my goal and also what I am doing wrong ...
    After succeeding in this step I will have a larger byte stream coming from my arduino with a bytes frame containing int and float.

    Thanks in advance for your help.

    Jo



  • stuff to read up on...not just ask somoene to tell you how to do...

    data endian format
    native CPU word size
    sizeof(int) on each platform in question
    host byte order and network byte order

    and making the assumption that you will always be able to read exactly two bytes from the serial port is a bad idea. Data can arrive at any time, and in any quantity.



  • @JorisC said in Convert 2 bytes QByteArray read from serial port into a int:

    I believed a union function was possible but it's not the case with QObject

    First, you should know that int size depend on processor architecture.
    This means that for 16 bits processors, as on the Arduino board, int has 16 bits, but on you PC it will have 32bits!
    So this has nothing to do with Qt/QObject, this is processor/compiler specific.

    if you want it to work with any compiler, use short int, which will ever been 16 bits:

    union INT{
      short int value;
      byte stream[2];
    };
    

    Second: byte order may vary according to CPU architecture ==> cf. https://en.wikipedia.org/wiki/Endianness

    Third, a serial port is an asynchronous stream. When QSerialPort fires an event, it only means that it have received data, but this does not mean all data sent by Arduino have been received, maybe they will come with some delay.
    Up to you to handle this on the right way, there are many example available around internet.



  • @KroMignon said in Convert 2 bytes QByteArray read from serial port into a int:

    @JorisC said in Convert 2 bytes QByteArray read from serial port into a int:

    I believed a union function was possible but it's not the case with QObject

    What is a union function?

    First, you should know that int size depend on processor architecture.
    This means that for 16 bits processors, as on the Arduino board, int has 16 bits, but on you PC it will have 32bits!
    So this has nothing to do with Qt/QObject, this is processor/compiler specific.

    if you want it to work with any compiler, use short int, which will ever been 16 bits:

    A short int is at least 16 bits wide. It could be larger with a standard compliant compiler. In general, the integer types that don't explicitly specify a size have minimum but not maximum allowed widths.
    If the goal is exactly 16 bits, use int16_t or uint16_t (if available...)

    https://en.cppreference.com/w/cpp/language/types
    https://en.cppreference.com/w/cpp/types/integer



  • @jeremy_k said in Convert 2 bytes QByteArray read from serial port into a int:

    A short int is at least 16 bits wide. It could be larger with a standard compliant compiler. In general, the integer types that don't explicitly specify a size have minimum but not maximum allowed widths.
    If the goal is exactly 16 bits, use int16_t or uint16_t (if available...)

    Yes, your are right, short int size may change but this implies that you setup your compiler to do it so.
    As you can see on the first link you give, for all compiler short int is 16 bits wide per default. ;)



  • Thanks to all for your time,

    I fully agree with what you told about int size and endianess problematic. I will set my int as uint16.
    I also can manage the serial reception in order to get the entire bytes stream.

    The question, if I refrase, is:
    What is the best way and best practice in Qt to convert the entire 2 byte stream representing my int (125) into this qint16 value.. Qdatastream? Memcpy?


  • Lifetime Qt Champion

    @JorisC This depends on the endianess of the data and your os.
    I would use boost::endian::big_to_native / little_to_native: https://www.boost.org/doc/libs/1_72_0/libs/endian/doc/html/endian.html#conversion_byte_reversal_functions



  • @JorisC said in Convert 2 bytes QByteArray read from serial port into a int:

    What is the best way and best practice in Qt to convert the entire 2 byte stream representing my int (125) into this qint16 value.. Qdatastream? Memcpy?

    "Best way" is always a little bit relative, in terms of performances, less lines of code or easy to understand...

    As you will get the data in to QByteArray on Qt side, I think is to use qFromBigEndian<>() (cf. https://doc.qt.io/qt-5/qtendian.html#qFromBigEndian-1)
    For example: ` value = qFromBigEndian<quint16>(ba);



  • @JorisC said in Convert 2 bytes QByteArray read from serial port into a int:

    The question, if I refrase, is:
    What is the best way and best practice in Qt to convert the entire 2 byte stream representing my int (125) into this qint16 value.. Qdatastream? Memcpy?

    assuming the following restriction:
    specified and enforced little endian short int of 16 bits on both ends

    I prefer
    int v = 0;
    memcpy(&v, &buffer[0], sizeof(short int));

    or
    v = buffer[0] | (buffer[1] << 8);
    and it would be safer to explicitly cast the types of buffer[n] to avoid problems with upcasting and bit operations...but my example is lazy.

    frame checking needs to be done on serial stream, either through inserting STX,ETX characters around the values, and/or, building in fixed and detectable idle periods between sending of values.


Log in to reply