Tiago Queiroz
Tiago Queiroz

Reputation: 151

Qt program (on Raspberry Pi) running slowly when there is slow data input (serial port)

I'm developing a program that runs on a Raspberry Pi and interacts with an Arduino using the Pi's serial port.

My architecture is that: The Pi is connected to a PanStamp (Arduino + wireless transceiver) and there is a Satellite (a PanStamp with a few sensors) sending data wirelessly. The Pi can activate and deactivate the satellite. The satellite sends data about 5 times per second, the PanStamp conneted to the Pi receives this data and send it using the serial port. Every second the Pi's PanStamp also send some sensor's readings.

To do that I'm using Qt framework, qextserialport (serial port library that implement Qt's signals/slots) wiriginpi to control the GPIO and some TCP and UDP sockets to send some data through internet.

Everything looks to be working well when the satellite is sending data.

However when I turn the satellite off, the whole program gets quite slow. It only shows data about every two seconds... The normal operation should be receive a reading every second (the data is being send by the PanStamp with the right timming, I checked it with a oscilloscope).

I have a packet count that is sent with the data, and there is not packets being lost. So it looks to be a kind of buffer or something like that is holding down the data flow.

The most odd thing is that when I turn the satellite on again ("hi speed" data flow) Pi shows a "burst of data" like everything was buffered and being processed slowly. The size of the brust also looks to be proportional to the time that the satellite was off.

Does anyone have any idea about what is going on/how can I test it?

Some code:

Serial port initialization:

/* Initialize Serial Port to talk with PanStamp */
this->port = new QextSerialPort(portName, QextSerialPort::EventDriven);
port->setBaudRate(BAUD9600);
port->setFlowControl(FLOW_OFF);
port->setParity(PAR_NONE);
port->setDataBits(DATA_8);
port->setStopBits(STOP_1);

if (port->open(QIODevice::ReadWrite) == true)
{
    /* Connect the arriving data signal with the incoming data function */
    connect(port, SIGNAL(readyRead()), this, SLOT(incoming_data()));

    /* Connect state change signal with its function */
    connect(port, SIGNAL(dsrChanged(bool)), this, SLOT(onDsrChanged(bool)));

    /* Debug */
    qDebug() << "listening for data on" << port->portName();
}
else
{
    qDebug() << "device failed to open:" << port->errorString();
    exit(1);
}

Function incoming_data() (Called every time there is data to be read):

/**
 * @brief SerialHandler::incoming_data
 *
 * It is called every time that a byte is received.
 *
 * It is responable to syncronize with the wireless link
 * and call store_incoming_data with the right data.
 *
 * To syncronize it waits for two 0xBA, when it happens
 * get_data is set to true. The next byte is the data size.
 *
 * The function waits to all data arrive, read it and pass it
 * to store_incoming_data that parses it and sore on memory/disk.
 *
 */
void SerialHandler::incoming_data()
{

QByteArray bytes;
unsigned char b;
int i;

/* Wait for sync condition */
if(!get_data)
{
    debug_msg(".", port->bytesAvailable());
    fflush(stderr);
    if(last_byte == 0xBA)
    {
        bytes = port->read(1);
        b = (unsigned char) bytes.at(0);
        if(b == 0xBA)
        {
            get_data = true;
            return;
        }
        else
            last_byte = b;
    }
    else
    {
        bytes = port->read(1);
        b = bytes.at(0);
        fflush(stdout);
        last_byte = b;
    }
}
else
{
    debug_msg(",");
    /* Read the data size */
    if(port->bytesAvailable() && (data_size < 0))
    {
        bytes = port->read(1);
        data_size = (int) bytes.at(0);
        debug_msg("Size: %i -> %02X\n", data_size, (unsigned char) data_size);
    }
    fflush(stderr);

    /* Read data and call store_incoming_data */
    if(port->bytesAvailable() >= data_size)
    {
        bytes = port->read(data_size);

        if(data_size == 0x34)
            store_incoming_stellite_data(bytes);
        else
        {
            if(bytes.at(0) == HUB_DATA)
                store_incoming_hub_data(bytes);
            else
            {
                printf("data_size: %i\tDebug panStamp data: ", data_size);
                for(i = 0; i < bytes.size(); i++)
                    printf("[%02X]", (unsigned char) bytes.at(i));
                printf("\n");
            }
            fflush(stdout);
        }

        get_data = false;
        last_byte = 0;
        data_size = -1;
        fflush(stderr);
    }
}
}

Here are the other functions and data structs:

/* ========================================== Data Types ======================================= */
typedef unsigned char byte;
typedef union _float_byte
{
unsigned char asByte[4];
float asFloat;
} float_byte;

typedef union _int_byte
{
unsigned char asByte[2];
short int asInt;
} int_byte;

typedef union _uint_byte
{
unsigned char asByte[4];
unsigned int asUInt;
} uint_byte;

typedef struct _stellite_data
{
QTime piTime;
float_byte x0, y0, z0;
float_byte x1, y1, z1;
float_byte temp0, temp1;
float_byte pressure0;
float_byte pressure1;
float_byte pressure2;
uint_byte time_stamp;
int_byte packet_count;
QByteArray bytes;
byte sender;
byte status_byte;
} satellite_data;

typedef struct _hub_data
{
QTime piTime;
float_byte altitude;
byte status_byte;
QByteArray bytes;
uint_byte packet_count;

byte power_sat0;
byte power_sat1;
byte hack_hd0;
byte hack_hd1;
byte solenoid0;
byte solenoid1;
byte webcam;
} hub_data;

/* ========================================== END Data Types ======================================= */

/**
 * @brief SerialHandler::store_incoming_hub_data
 *
 * Store Hub's data.
 *
 * Packet description:
 * +---------------------+-----------------+
 * |  Altitude_Command   | Altitude (float)|
 * |      (1 byte)       |    (4 byts)     |
 * +---------------------+-----------------+
 *
 * @param data Data to be stored
 */
void SerialHandler::store_incoming_hub_data(QByteArray data)
{
hub_data *tmp = new hub_data;
char *datagram;
int datagram_size;

tmp->piTime = QTime::currentTime();

tmp->altitude.asByte[0] = data.at(1);
tmp->altitude.asByte[1] = data.at(2);
tmp->altitude.asByte[2] = data.at(3);
tmp->altitude.asByte[3] = data.at(4);

tmp->packet_count.asByte[0] = data.at(5);
tmp->packet_count.asByte[1] = data.at(6);
tmp->packet_count.asByte[2] = data.at(7);
tmp->packet_count.asByte[3] = data.at(8);

tmp->power_sat0 = GS_link->power_sat0;
tmp->power_sat1 = GS_link->power_sat1;
tmp->hack_hd0 = GS_link->hack_hd0;
tmp->hack_hd1 = GS_link->hack_hd1;
tmp->solenoid0 = GS_link->solenoid0;
tmp->solenoid1 = GS_link->solenoid1;
tmp->webcam = GS_link->webcam;

printf("HUB: %s:%i\t%u\t%.4f\t%i\t%i\t%i\t%i\t%i\t%i\t%i\n",
       tmp->piTime.toString().toStdString().c_str(),
       tmp->piTime.msec(),
       tmp->packet_count.asUInt,
       tmp->altitude.asFloat,
       tmp->power_sat0,
       tmp->power_sat1,
       tmp->hack_hd0,
       tmp->hack_hd1,
       tmp->solenoid0,
       tmp->solenoid1,
       tmp->webcam);
fflush(stdout);

/* Send to GS */
datagram_size = asprintf(&datagram, "H,ALT,%.4f,S0,%i,S1,%i,HD0,%i,HD1,%i,SOL0,%i,SOL1,%i,W,%i",
                         tmp->altitude.asFloat,
                         tmp->power_sat0,
                         tmp->power_sat1,
                         tmp->hack_hd0,
                         tmp->hack_hd1,
                         tmp->solenoid0,
                         tmp->solenoid1,
                         tmp->webcam);
GS_link->send_datagram(datagram_size, datagram);

hub_data_list.append(tmp);

if((hub_data_list.size()%60) == 0)
    log_hub_data("iSEDE_Hub.log");
free(datagram);

}

void SerialHandler::log_hub_data(QString file_name)
{
int i;
QFile file(file_name);
file.open(QIODevice::Append);
QTextStream file_out(&file);

printf("Logging Hub's data...\n");
for(i = last_hub_logged; i < hub_data_list.size(); i++)
{
    file_out << hub_data_list.at(i)->piTime.toString(Qt::ISODate).toStdString().c_str() << ":";
    file_out << hub_data_list.at(i)->piTime.msec() << ';';

    file_out << hub_data_list.at(i)->altitude.asFloat << ';';
    file_out << hub_data_list.at(i)->power_sat0 << ';';
    file_out << hub_data_list.at(i)->power_sat1 << ';';
    file_out << hub_data_list.at(i)->hack_hd0 << ';';
    file_out << hub_data_list.at(i)->hack_hd1 << ';';
    file_out << hub_data_list.at(i)->solenoid0 << ';';
    file_out << hub_data_list.at(i)->solenoid1 << ';';
    file_out << hub_data_list.at(i)->webcam << '\n';
}
last_hub_logged = i;
file.close();
printf("Done!\n");
}

/**
 * @brief SerialHandler::store_incoming_data
 * @param data
 *
 * Parse the data.
 *
 * Payload description:
 * +---------------+------------------+---------------------+---------------+--------------+-------------+------------------+
 * | Packet Count  | Temperature (x2) | Accelerometers (x2) | Pressure (x3) |  Time Stamp  | Status byte | Sender's Address |
 * |  (2 bytes)    |     (8 bytes)    |     (24 bytes)      |   (12 bytes)  |   (4 bytes)  |  (1 byte)   |    (1 byte)      |
 * +---------------+------------------+---------------------+---------------+--------------+-------------+------------------+
 */
void SerialHandler::store_incoming_stellite_data(QByteArray data)
{
satellite_data *tmp = new satellite_data;

tmp->bytes.append(data);

/* Pi's time */
tmp->piTime = QTime::currentTime();

/* Packet count */
tmp->packet_count.asByte[0] = data.at(0);
tmp->packet_count.asByte[1] = data.at(1);

/* Temperature 0 */
tmp->temp0.asByte[0] = data.at(2);
tmp->temp0.asByte[1] = data.at(3);
tmp->temp0.asByte[2] = data.at(4);
tmp->temp0.asByte[3] = data.at(5);

/* Temperature 1 */
tmp->temp1.asByte[0] = data.at(6);
tmp->temp1.asByte[1] = data.at(7);
tmp->temp1.asByte[2] = data.at(8);
tmp->temp1.asByte[3] = data.at(9);

/* Accelerometer 0 */
tmp->x0.asByte[0] = data.at(10);
tmp->x0.asByte[1] = data.at(11);
tmp->x0.asByte[2] = data.at(12);
tmp->x0.asByte[3] = data.at(13);

tmp->y0.asByte[0] = data.at(14);
tmp->y0.asByte[1] = data.at(15);
tmp->y0.asByte[2] = data.at(16);
tmp->y0.asByte[3] = data.at(17);

tmp->z0.asByte[0] = data.at(18);
tmp->z0.asByte[1] = data.at(19);
tmp->z0.asByte[2] = data.at(20);
tmp->z0.asByte[3] = data.at(21);

/* Accelerometer 1 */
tmp->x1.asByte[0] = data.at(22);
tmp->x1.asByte[1] = data.at(23);
tmp->x1.asByte[2] = data.at(24);
tmp->x1.asByte[3] = data.at(25);

tmp->y1.asByte[0] = data.at(26);
tmp->y1.asByte[1] = data.at(27);
tmp->y1.asByte[2] = data.at(28);
tmp->y1.asByte[3] = data.at(29);

tmp->z1.asByte[0] = data.at(30);
tmp->z1.asByte[1] = data.at(31);
tmp->z1.asByte[2] = data.at(32);
tmp->z1.asByte[3] = data.at(33);

/* Pressure 0 */
tmp->pressure0.asByte[0] = data.at(34);
tmp->pressure0.asByte[1] = data.at(35);
tmp->pressure0.asByte[2] = data.at(36);
tmp->pressure0.asByte[3] = data.at(37);

/* Pressure 1 */
tmp->pressure1.asByte[0] = data.at(38);
tmp->pressure1.asByte[1] = data.at(39);
tmp->pressure1.asByte[2] = data.at(40);
tmp->pressure1.asByte[3] = data.at(41);

/* Pressure 2 */
tmp->pressure2.asByte[0] = data.at(42);
tmp->pressure2.asByte[1] = data.at(43);
tmp->pressure2.asByte[2] = data.at(44);
tmp->pressure2.asByte[3] = data.at(45);

/* Satellite time stamp */
tmp->time_stamp.asByte[0] = data.at(46);
tmp->time_stamp.asByte[1] = data.at(47);
tmp->time_stamp.asByte[2] = data.at(48);
tmp->time_stamp.asByte[3] = data.at(49);

/* Satellite status byte */
tmp->status_byte = data.at(50);

/* Sender's address */
tmp->sender = data.at(51);

    printf("%u\t%u\t%u\t%hi\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%s\t%s:%03i\n",
       tmp->sender,
       tmp->time_stamp.asUInt,
       tmp->time_stamp.asUInt/1000,
       tmp->packet_count.asInt,
       tmp->temp0.asFloat,
       tmp->temp1.asFloat,
       tmp->x0.asFloat,
       tmp->y0.asFloat,
       tmp->z0.asFloat,
       tmp->x1.asFloat,
       tmp->y1.asFloat,
       tmp->z1.asFloat,
       tmp->pressure0.asFloat,
       tmp->pressure1.asFloat,
       tmp->pressure2.asFloat,
       byte_to_binary(tmp->status_byte),
       tmp->piTime.toString(Qt::ISODate).toStdString().c_str(),
       tmp->piTime.msec());
fflush(stdout);

/* Create a datagram and send it */
char *datagram = NULL;
int datagram_size;
datagram_size = asprintf(&datagram, "S%u,C,%u,PC,%u,A0,%.4f,%.4f,%.4f,A1,%.4f,%.4f,%.4f,T0,%.4f,T1,%.4f,P0,%.4f,P1,%.4f,P2,%.4f,SB,%u\n",
                         tmp->sender - 7,
                         tmp->time_stamp.asUInt,
                         tmp->packet_count.asInt,
                         tmp->x0.asFloat,
                         tmp->y0.asFloat,
                         tmp->z0.asFloat,
                         tmp->x1.asFloat,
                         tmp->y1.asFloat,
                         tmp->z1.asFloat,
                         tmp->temp0.asFloat,
                         tmp->temp1.asFloat,
                         tmp->pressure0.asFloat,
                         tmp->pressure1.asFloat,
                         tmp->pressure2.asFloat,
                         tmp->status_byte);

GS_link->send_datagram(datagram_size, datagram);
free(datagram);

    data_list.append(tmp);
    debug_msg("**%i**\n", data_list.size());

    if(data_list.size()%100 == 0)
    {
        log_data("iSEDE.log");
    }


/**
 * @brief SerialHandler::log_data
 * @param file_name
 */
void SerialHandler::log_data(QString file_name)
{
int i;
QFile file(file_name);
file.open(QIODevice::Append);
QTextStream file_out(&file);

printf("Logging data....\n");

for(i = last_logged; i < data_list.size(); i++)
{
    file_out << data_list.at(i)->piTime.toString(Qt::ISODate).toStdString().c_str() << ":";
    file_out << data_list.at(i)->piTime.msec() << ";";

    file_out << data_list.at(i)->sender << ",";
    file_out << data_list.at(i)->time_stamp.asUInt << ",";
    file_out << data_list.at(i)->packet_count.asInt << ",";

    file_out << data_list.at(i)->temp0.asFloat << ",";
    file_out << data_list.at(i)->temp1.asFloat << ",";

    file_out << data_list.at(i)->x0.asFloat << ",";
    file_out << data_list.at(i)->y0.asFloat << ",";
    file_out << data_list.at(i)->z0.asFloat << ",";

    file_out << data_list.at(i)->x1.asFloat << ",";
    file_out << data_list.at(i)->y1.asFloat << ",";
    file_out << data_list.at(i)->z1.asFloat << ",";

    file_out << data_list.at(i)->pressure0.asFloat << ",";

    file_out << byte_to_binary(data_list.at(i)->status_byte);

    file_out << "\n";
}
last_logged = i;
file.close();

printf("Done!\n");
}

After lots of tests and researching I finally found what is going on: The readyRead() signal is emitted every time that there is a new data, however as the documentation says "readyRead() is not emitted recursively", so it looks like incoming_data() which is connected to readyRead() is only being called when some bytes arrives making my program "out of sync" with the packets and only being able to read some packets (the others stay on the buffer).

Nonetheless this "out of sync condition" does not happen when there is a huge amount of data arriving.

Here is of the thread I created I'm going to create anotherto address this specific problem.

Thanks a lot to everyone that helped me!

Upvotes: 0

Views: 2092

Answers (1)

user2781409
user2781409

Reputation: 11

If readyRead is emited by the first time block all signals until you read all data. Also You should write serialHandler as another thread, so serial is not blocking other operation

p.s qextserialport as i remember is experimental... so the best way is to write class using system api.

Upvotes: 1

Related Questions