6 years, 1 month ago.

Serial Communication Issue

Hello, I am trying to work on a serial communication project using STM32 NUCLEO F767ZI development board. I am trying to send a string of 26 characters serially so that it can be converted to an integer array and further operations can be performed on it. I am using Bray's Terminal for the same. Whenever I send the 26 character string, it displays only the first 25 characters. Later after the string to int conversion it circular shifts the entire array by one i.e my last element to the first. What is the issue? Can you provide a better code for it?

#include "mbed.h"
#include<string>
#include "USBSerial.h"

Serial pc(USBTX, USBRX);

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);

int main()
{
    int t;
    int  r;
    char buff[26];
    int code[26];   
    while(1)
    {
        pc.printf("\n\r Select drink!");
        LOOPER: if(pc.readable())
        {
            pc.printf("\n\r readddd  ");
            //for(p=0;p<26;p++)
            pc.gets(buff,27);
            pc.printf("\r\n%s\n", buff);
            r=0;
            while(buff[r]!= '\0') 
                {
                    code[r] = buff[r] - '0';
                    pc.printf(" :%d",code[r]);
                    r++;
                }    
            if(code[1]==1 && code[2]==0 && code[3]==0)
            t = -1;
            else if(code[1]==0 && code[2]==1 && code[3]==0)
            t = 0;
            else if(code[1]==0 && code[2]==0 && code[3]==1)
            t = 1;
            
           // t=code[0]-code[1];    // -1 for juice   1 for shakes    0 for lemonade
            switch(t)
                {
                    case 1 : pc.printf("    \n\r Test successful for shakes");
                             led1 = 1;
                             wait(2);
                             led1 = 0;
                             break;
                    case -1: pc.printf("    \n\r Test successful for juices");
                             led2 = 1;
                             wait(2);
                             led2 = 0;
                             break;        
                    case 0 : pc.printf("    \n\r Test successful for lemonades");
                             led3 = 1;
                             wait(2);
                             led3 = 0;
                             break;
                    default: pc.printf("    \n\r Test failed!");
                             led1=1;led2=1;led3=1;
                             wait(2);
                             led1=0;led2=0;led3=0;                   
                             break;
                }
                buff[0] = '\0';
                memset(code, 0, sizeof(code));
                
        }
        goto LOOPER;    
    }
}    

3 Answers

6 years, 1 month ago.

In addition to the previous answers: If the first 3 bytes of your input don't happen to match on of the required patterns then t remains either undefined or at the previous value when you get to your switch statement.

And if you have a maximum string length define it on one place as a constant rather than hard coding the number multiple times, that can be a maintenance nightmare if you ever need to change it.

Putting it all together you end up with something like this:

#include "mbed.h"
#include<string>
#include "USBSerial.h"

#define max_inputLen 26  // define max length in one place not several.


Serial pc(USBTX, USBRX);

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);

int main()
{
    int t;  // could have a narrower scope, could have a more meaningful name.
    char buff[max_inputLen +1];  // plus null terminator
    int code[max_inputLen];
    while(1) {

        // Completely optional couple of lines that I like to include.
        // These flush out any existing serial input before asking the user a question.
        // This prevents old data that's still in the buffer from getting mistaken for a
        //     user reply.
        while(pc.readable())
            pc.getc();


        pc.printf("\r\n Select drink!");

// Label and GOTO removed, it's considered bad practice and also in this case was unnecessary.
// since you're not doing anything other than looping waiting for a character there
// is no need for the if pc.readable(), just wait until there is data.

// revised serial reading, read one byte at a time until we get an end of line or too
// much data. Since we are already looping may as well convert to ints in the same loop.
        int index = 0;
        char keyIn = pc.getc();  // use getc and read one char at a time
        while ((index<max_inputLen)&&(keyIn!='\r')&&(keyIn!='\n')) { // check exit condition
            buff[index] = keyIn;        // store the text.
            code[index] = keyIn - '0';  // convert to an int.
            index++;                    // increase the index.
            keyIn = pc.getc();         // get the next character.
        }
        buff[index] = 0; // null terminate the input string.

        pc.printf("\r\n%s\r\n", buff); // echo the input back

        // your test code - this could go into a separate function to keep things cleaner

        if(code[1]==1 && code[2]==0 && code[3]==0)
            t = -1;
        else if(code[1]==0 && code[2]==1 && code[3]==0)
            t = 0;
        else if(code[1]==0 && code[2]==0 && code[3]==1)
            t = 1;
        else
            t = -2; // set a default so that if nothing matches we don't use the old value.

        // t=code[0]-code[1];    // -1 for juice   1 for shakes    0 for lemonade
        switch(t) {
            case 1 :
                pc.printf("\r\n Test successful for shakes");
                led1 = 1;
                wait(2);
                led1 = 0;
                break;
            case -1:
                pc.printf("\r\n Test successful for juices");
                led2 = 1;
                wait(2);
                led2 = 0;
                break;
            case 0 :
                pc.printf("\r\nTest successful for lemonades");
                led3 = 1;
                wait(2);
                led3 = 0;
                break;
            default:
                pc.printf("\r\n Test failed!");
                led1=1;
                led2=1;
                led3=1;
                wait(2);
                led1=0;
                led2=0;
                led3=0;
                break;
        }
        /* not needed
        buff[0] = '\0';
        memset(code, 0, sizeof(code));
        */
    }
}

Accepted Answer
6 years, 1 month ago.

for (int i=0; i < 26; i++) { buff[i] = pc.getc(); }

May be better than gets().

p.s. There are barely any comments in your code explaining what you are trying to do, so it is very difficult to advise.

p.p.s. Using GOTO is pretty much frowned up in C/C++ and in fact, this is the first time I have come across it in years! I'd advise re-factoring your code to avoid its use.

6 years, 1 month ago.

Hello Rishabh,

I agree with Craig, but if you decide to stick with the gets function anyway then please notice that when you call a gets function in mbed, then you are actually calling a fgets function where the file stream is silently passed by mbed and that the fgets is quite different from gets:

  • Not only allows to specify the maximum size of str and includes in the string any ending newline character but a terminating null character is automatically appended after the characters copied to str.
  • The second argument passed to the gets is the maximum number of characters to be copied into str, including the terminating null-character.

If you send a string of 26 chars with a new line belonging to that string then the buff array must have enough room to store also the terminating null char appended automatically by the gets function. That's why I advice to declare it as below:

char buff[27];

NOTE: I also recommend to use a \r\n sequence in strings rather than the \n\r one.