5 years, 1 month ago.

How to receive serial message over 16 chars in length?

I am catching input with an interrupt but I can't catch input over 16 chars with the LPC1768. How would I catch input over 16 chars, preferably in OS5? OS2 methods will also be appreciated, thank you.

code

#include "mbed.h"
#include "typeFind.h"
#include "common.h"

#include "stdlib.h"
#include <string.h>

LocalFileSystem local("local");                 // define local file system 

RawSerial SBPTest(USBTX, USBRX);


unsigned char i = 0;
char inputBuff[16] = { 0 };

void newSBPCommand();
void printBinary(int byteNo);

int main() {
   
   SBPTest.attach(&newSBPCommand);          //interrupt to catch input
   
   while(1) { 
            
        if (inputBuff[1] != 0x00) {            //check once the length of message byte arrives
            if (inputBuff[1] <= 0x0D) {       //Length of data is the second byte, if it is less than 13, then there is 16 or less bytes in message
                printBinary(16);
            }   
            else if (inputBuff[1] > 0x0D) {  //If there is more than 16 bytes of data, then it is more than one message, and thats when length is more than 13 
                printBinary(20);
            }
        }
    }
}


void newSBPCommand() {
    while (SBPTest.readable()) {
        //signal readable
        inputBuff[i] = SBPTest.getc(); 
        i++;
    }

}


void printBinary(int byteNo) {
    FILE* WriteTo = fopen("/local/log1.txt", "a");
    for (i = 0; i < byteNo; i++) {
        for (int j = 7; j >= 0; j--) {
            fprintf(WriteTo, "%d", (inputBuff[i] >> j) & 1 ? 1: 0);
        }
    }  
    fclose(WriteTo);   
    inputBuff[1] = 0x00;
    i = 0; 
}

2 Answers

5 years, 1 month ago.

You have a number of bugs in there, buffer overflows, re-using the same variable name, trying to print out 20 bytes from a 16 byte buffer after only 2 bytes have been received.

I'm making some guesses as to what you actually want to do and I've not tried this in a compiler so it's probably got a few syntax errors etc... but I think you need something along the lines of:

#include "mbed.h"

// not sure why all of these other includes are here
#include "typeFind.h"
#include "common.h"
 
#include "stdlib.h"
#include <string.h>
 
LocalFileSystem local("local");                 // define local file system 
 
RawSerial SBPTest(USBTX, USBRX);
 

// unsigned char i = 0;
// why use an unsigned char? There is no need to limit it to 8 bits and this is a 32 bit processor so use int.
// Never use one character names for variables except for say the counter in a 1 line for loop.
// This is changed in an interrupt and read in the main loop. It needs to be volatile to work correctly,
volatile int inputCount = 0;

// char inputBuff[16] = { 0 };
// Better to define a buffer size constant rather than hard code it. That way you can easily change it.
// You create a buffer of 16 bytes and then in comments indicate you expect 20 bytes some times.
// Where are the other 4 bytes going to be stored?
// No real need to initalise the values.
#define _InputBuffeSize_ 32
char inputBuff[_InputBuffeSize_ ];

void newSBPCommand();
void printBinary(int byteNo);
 
int main() {
   
   SBPTest.attach(&newSBPCommand);          //interrupt to catch input
   
   while(1) { 

        // from what it looks like you have a packet with a structure of [byte][length][data (length bytes)][byte]
        // or something similar. The total length is the value of the second byte plus 3
        if (inputCount > 1)   { // have got at least 2 bytes
          int length = inputBuff[1]+3;
          if (inputCount >= length) {
               __disable_irq();  // don't add new bytes while we are printing things out
                printBinary(length);
                inputCount = 0;  // reset the count
                __enable_irq();
            }
        }
    }
}
 
 
void newSBPCommand() {
    while (SBPTest.readable()) {
        //signal readable
        if (inputCount  <_InputBuffeSize_) // check for buffer overflow. Previous code would have corrupted memory after 16 bytes
          inputBuff[inputCount] = SBPTest.getc(); 
        else
          SBPTest.getc();   // ran out of space, just throw the data away.
        inputCount++;
    }
}

void printBinary(int byteNo) {
    FILE* WriteTo = fopen("/local/log1.txt", "a");
     // you had reset your bytes received counter to 0 here by re-using the variable name i.
    for (int i = 0; i < byteNo; i++) {  
        for (int j = 7; j >= 0; j--) {
            fprintf(WriteTo, "%d", (inputBuff[i] >> j) & 1 ? 1: 0);
        }
    }  
    fclose(WriteTo);   
// Let the main code handle resetting the buffer, keep the print function to just printing things.
//    inputBuff[1] = 0x00;
//   i = 0; 
}

This still does have some issues - if more than 16 bytes arrive while outputting to a file data will be lost. If somehow the input gets the start of a second packet before we have checked and realized we need to save something then the start of the next packet will be lost and things will get out of sync. Also there is no synchronization process, you assume that the system will start up between packets or before the first one and will never drop a byte. If either of these two assumptions is broken things will go wrong and never recover.

All of this is fixable but whether it's worth the complexity depends on the application and data rates involved.

Accepted Answer

One other comment - When writing to the file you convert the bytes to binary and output then 1 bit at a time as text. Rather than writing to the file system one character at a time it may be more efficient to convert the whole byte into a c string and then write that in one go. If you wanted to put a space between bytes this would simply be a case of putting an extra character into the string that you print each byte. It also means you can replace the slow printf with the far faster puts command.

char byteBuffer[10];
byteBuffer[8] = ' '; // space between bytes
byteBuffer[9] = 0; // null terminate the sting (how c knows it's reached the end of a string)
for (int i = 0; i < byteNo; i++) {  
        for (int j = 7; j >= 0; j--) {
           if ( (inputBuff[i] >> j) & 1)
             byteBuffer[7-j]='1';
           else
             byteBuffer[7-j]='0';
        } 
        fputs(byteBuffer,WriteTo); // fputs takes string,file, the opposite way around to fprintf
      }
      fputs("/r/n",WriteTo); // put a new line at the end of the packet.
  }  
posted by Andy A 12 Feb 2019

This is of great help to me, thank you. Is there any project that you know that applies synchronisation when receiving data on serial, so that I could look into that?

posted by L B 12 Feb 2019

Nothing generic that you could use, it depends too much on the protocol used in the messages.

I assume that the first byte of your message is some form of constant header? If that is the case then you do something like this as a simple check:

#define _PacketStartMarker_ 0x55 // replace with whatever your start marker is

void newSBPCommand() {
    while (SBPTest.readable()) {

        if (inputCount  <_InputBuffeSize_) // check for buffer overflow. Previous code would have corrupted memory after 16 bytes
          inputBuff[inputCount] = SBPTest.getc(); 
        else
          SBPTest.getc();   // ran out of space, just throw the data away.
        inputCount++;

        if (inputCount == 1) // first byte - check for valid start byte
          if (inputBuff[0] != _PacketStartMarker_) 
             inputCount  = 0; // failed, throw the byte away
     }
}

That way you won't start filling the buffer until you get a valid start byte. Of course that byte may appear in the data as well so it's not perfect and could take a while to sync up again after an error but it should get there in the end.

If you also have end markers or checksums then you can check those once all the data is in and throw out packets that don't end correctly. You still lose data but at least you don't pass bad data on thinking it's good. That is as simple as a single if statement before calling your print function.

It is possible to do better by allowing your data to start after the start of the buffer, when a checksum fails you then look for alternative start markers within the failed packet and test those. But that can get complicated. Short low speed serial links are normally fairly reliable so it's probably not worth it.

posted by Andy A 12 Feb 2019

I was going to use the checksum but checking if start value is the same seems a good idea too, so I think i'll apply both if it isn't too much overhead. Thank you Andy this has been very helpful and so easy to grasp with your explanation.

posted by L B 12 Feb 2019
5 years, 1 month ago.

Your testing the character at position [i] on lines 26 and 29. If you want to test the amount of characters you need to test i.

if (i <= 0x0D)) {do something here}

If you want to look for a character or bunch of characters you can use:

if(strcmp(inputBuff,"ABC")==0){do something here}

This is looking for "ABC" in the container inputBuff on each pass.

These things aren't the issue. The function printBinary already prints out message as long as it is less or equal to 16 chars. The length of the data within my message is already specified by inputBuff[1]. My problem is that the protocol I am using sometimes sends over 16chars, in which case I don't know how to catch that.

posted by L B 12 Feb 2019