Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
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.
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. }
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 12 Feb 2019Nothing 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 12 Feb 20195 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 12 Feb 2019