0

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
oodessit
  • 1
  • 4
  • 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 Apr 22 '19 at 16:40
  • 2
    I'd make sure `buffer` was null terminated and check `count ` if I were you. – user4581301 Apr 22 '19 at 16:40
  • 1
    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 Apr 22 '19 at 16:41
  • How does `parse()` work? – Galik Apr 22 '19 at 16:44
  • @oodessit Why not using a `uint8_t` buffer? – πάντα ῥεῖ Apr 22 '19 at 16:44
  • @user4581301 buffer is null terminated and I always check `count`, this code is simplified a lot as I mentioned – oodessit Apr 22 '19 at 16:49
  • Given that this works in other langauges then the data is obviously fine. It must be how you are *interpreting* the data. Can you show us how you convert your `char` array into numerical values? – Galik Apr 22 '19 at 16:50
  • What data do you expect? What data do you seem to get? It would really help if you could create a proper [mcve] to show us, one that we can test ourselves and that replicates your problem. Also please ead about [how to ask good questions](http://stackoverflow.com/help/how-to-ask), as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). And of course, please [learn how to debug your programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). Knowing how to do that is a crucial part of being a programmer. – Some programmer dude Apr 22 '19 at 16:56
  • @Someprogrammerdude I expect to see values about 0 +- 5 and I get 1690 almost always as I mentioned in my question – oodessit Apr 22 '19 at 17:02
  • 1
    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 Apr 22 '19 at 17:35
  • You have potential for undefined behavior when you don't check the return value of `std::find`. – Galik Apr 22 '19 at 17:46
  • It may simplify the code to read just `8` bytes at a time and process those before reading the next `8` bytes. – Galik Apr 22 '19 at 17:52
  • @Someprogrammerdude `sizeof(long) == 4` on my platform and if it was true the issue had to be reproducible 100% of times but it's not. What is the most weird thing in this quiestion – oodessit Apr 22 '19 at 17:59
  • @Galik I agree that reading only 8 bytes at a time would be useful but it doesn't really solve the issue. I added return value check for `std::find` and it didn't help – oodessit Apr 22 '19 at 18:01
  • Then debugging is the only way to help you find out the problem. Use a debugger to step through the code, line by line, stepping into your functions, and see what they do. What values your variable are initialized or assigned with. It's usually hard work, especially if you can't replicate it reliably or of it's a timing issue, but it's really the only way to figure out what's wrong with your code. – Some programmer dude Apr 22 '19 at 18:20
  • @Someprogrammerdude the thing is that mess starts right after calling `::read` :) But not every time. That is dead end which I reached during debugging – oodessit Apr 22 '19 at 18:34
  • From your code i see you are not setting the serial port as it should. Please look at this example https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c where your serial port is correctly set up because the serial port in linux is usually a pain to set up and use. Secondly, the BAUD rate is very non standard so you might want to check if your virtual driver supports it and how can you change it to something more viable and in range. – JD_GRINDER Apr 24 '19 at 04:24
  • Your termios initialization code has (at least) two serious errors. (1) You use the termios structure `SerialPortSettings` without first initializing it. So your program is manipulating garbage data. Your program needs to call **tcgetattr()**. (2) Your program tries to clear **ICANON** and other attributes from the wrong member. **ICANON** is in c_lflag rather than c_iflag. Where did you copy this bad code from? – sawdust Apr 24 '19 at 22:37

1 Answers1

0

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!

oodessit
  • 1
  • 4