0
votes

So I have embedded device based on Raspberry Pi (Modberry) and 4 sensors connected via one USB-hub. These sensors send data all the time via serial port which I have to read and analyze. Data is divided by chunks: 7 bytes each and space between them. First 4 bytes are time and 3 bytes are value which is measured by sensor. Normal values should be about 0 +- 5. Baudrate is 921600. When I read only one sensor, for example "/dev/ttyUSB0", everything is fine but when I start to increase amount of readable sensors things go completely wrong. 4 bytes which represent time are always delivered in correct way and are NEVER wrong but last 3 bytes come with weird values 99% of time and start spamming like this:

[2019-04-22 17:48:02.264] Device: /dev/ttyUSB3, Time: 12226408, Value: 1690
[2019-04-22 17:48:02.265] Device: /dev/ttyUSB2, Time: 12217312, Value: 1690
[2019-04-22 17:48:02.265] Device: /dev/ttyUSB2, Time: 12217316, Value: 1690

Interesting thing is that it doesn't happen 100% of times after restarting application but about 80%.

I made an application also in Python and Java which doesn't have this issue. They are all running on same device. I was trying to run reading every sensors in separate thread but it also didn't help.

I simplified my code a lot and removed all error checks in this chunk.

void openSerial()
{
const int fileDescriptor = ::open(mParams.path.c_str(), O_RDONLY | O_NOCTTY);
termios SerialPortSettings;

SerialPortSettings.c_cflag &= ~PARENB;
SerialPortSettings.c_cflag &= ~CSTOPB;
SerialPortSettings.c_cflag &= ~CSIZE;
SerialPortSettings.c_cflag |= CS8;
SerialPortSettings.c_cflag &= ~CRTSCTS;
SerialPortSettings.c_cflag |= CREAD | CLOCAL;
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
SerialPortSettings.c_oflag &= ~OPOST;
SerialPortSettings.c_cc[VMIN] = 10;
SerialPortSettings.c_cc[VTIME] = 0;

cfsetispeed(&SerialPortSettings, 921600);
tcsetattr(fileDescriptor, TCSANOW, &SerialPortSettings);
tcflush(fileDescriptor, TCIOFLUSH);
}

static const int BUFFER_SIZE = 256;
void readSerial()
{
    char buffer[BUFFER_SIZE + 1] = { 0 };
    int count = 0;

    count = read(mfd, buffer, BUFFER_SIZE);

    std::vector<char> rawBuffer;
    for (int i = 0; i < count; i++) {
        rawBuffer.push_back(buffer[i]);
    }

    parse(rawBuffer);
}

void parse(std::vector<char> rawBuffer)
{
    auto currentSpace = std::find(rawBuffer.begin(), rawBuffer.end(), ' ');
    auto nextSpace = std::find(currentSpace + 1, rawBuffer.end(), ' ');
    size_t counter = 0;
    while (currentSpace != rawBuffer.end()) {
        const int dist = std::distance(currentSpace, nextSpace);

        if (dist == 8) {
            XD1000Data data;
            data.time = parseTime(&(*(currentSpace + TIME_SHIFT)));
            data.value = parseValue(&(*(currentSpace + VALUE_SHIFT)));
        } else {
                printf("packet size error, dist %d, rawBuffer.size %d, counter %d", dist, rawBuffer.size(), counter);
        }
        counter++;
        currentSpace = nextSpace;
        nextSpace = std::find(currentSpace + 1, rawBuffer.end(), ' ');
    }

    rawBuffer.clear();
}

long parseTime(char *buffer)
{
    long dest[4];
    long time;

    parseBuffer<long, 4>(buffer, dest);

    time = dest[3] + (dest[2] << 6) + (dest[1] << 12) + (dest[0] << 18);

    return time;
}

int parseValue(char *buffer)
{
    int dest[3];
    int value;

    parseBuffer<int, 3>(buffer, dest);

    value = dest[2] + (dest[1] << 6) + (dest[0] << 12);

    return (short)((value & 0x1000) ? (value | 0xf000) : (value & 0x0fff));
}

template <typename T, size_t size> void parseBuffer(char *buffer, T *dest)
{
    for (size_t i = 0; i < size; i++) {
        if (isupper(buffer[i]))
            dest[i] = buffer[i] - 'A';
        else if (islower(buffer[i]))
            dest[i] = buffer[i] - 'a' + 26;
        else if (isdigit(buffer[i]))
            dest[i] = buffer[i] - '0' + 52;
        else if (buffer[i] == '+')
            dest[i] = 62;
        else if (buffer[i] == '/')
            dest[i] = 63;
        else // error
            ;
    }
}

I'm 100% sure that sensors are not sending this 1690 data, this behaviour is not observed in any other apps with any amount of sensors or even Minicom. And this is how it should actually work and it does some rare times:

Device:/dev/ttyUSB3 14006188 -5
Device:/dev/ttyUSB3 14006192 -6
Device:/dev/ttyUSB3 14006196 -5
Device:/dev/ttyUSB3 14006200 -6
Device:/dev/ttyUSB3 14006204 -5
Device:/dev/ttyUSB3 14006208 -6
Device:/dev/ttyUSB3 14006212 -5
Device:/dev/ttyUSB0 14006152 -1
Device:/dev/ttyUSB0 14006156 -2
Device:/dev/ttyUSB0 14006160 -1
Device:/dev/ttyUSB0 14006164 0
1
I was wondering about some hardware issues for example USB-hub bandwith but doesn't make any sense because Python and Java apllivation work nice. Perhaps some adjustments while opening serial would be useful ?oodessit
I'd make sure buffer was null terminated and check count if I were you.user4581301
If it works in Java then it might be useful to show us the core Java code than correctly reads the values alongside the equivalent C++ code that fails. Just the reading/decodong code.Galik
How does parse() work?Galik
One problem is that you assume that sizeof(long) == 4. That's not always true, on a 64-bit Linux system then sizeof(long) is typically equal to 8.Some programmer dude

1 Answers

0
votes

The issue was in wrong way opening serial ports. So I took opening code from this question and now everything works fine:

How to open, read, and write from serial port in C?

Thank you guys @JD_GRINDER and @sawdust for giving me right direction!