0
\$\begingroup\$

I am trying to interface GSM SIM900 with AVR Controller. Till now I have done programming which transmit the command ATD and it makes a call to my cell. GSM response to this command by sending OK. I want to make a program which also receives this response OK and to do that I have written my code but it is not working as it should be. I am attaching my code please tell me where I am doing wrong.

CODE:

void serial_Init()
{
    UCSRB = (1<<TXEN)|(1<<RXEN);
    UCSRC = (1<<UCSZ1)|(1<<UCSZ0)|(1<<URSEL);
    UBRRL = 51;
}

void serial_Tx(char *str)
{
    for (unsigned int i=0;str[i]!=0;i++)
    {
        UDR=str[i];
        while(!(UCSRA&(1<<UDRE)));
    }
}

 char serial_Rx()
 {
    //Wait untill a data is available

    while(!(UCSRA & (1<<RXC)))
    {
         //Do nothing
    }

     //Now USART has got data from host
    //and is available is buffer

    return UDR;
 }

 void uart_put(char data)
 {
     UDR=data;
     while(!(UCSRA&(1<<UDRE)));
 }

int main(void)
{
    DDRA = 0x00;
    DDRC = 0xFF;
    PORTA = 0xFF;
    serial_Init();
    char data[2];
    while(1)
    {
        if(PINA==0xFE)
        {
            serial_Tx("ATD<number>;\r\n");
            for(int j=0;j<=100;j++)
            {
                data[j] = serial_Rx();
                uart_put(data[j]);  
            }
        }
    }
}

Upate Current result on hyperterminal:

enter image description here

\$\endgroup\$
  • \$\begingroup\$ You always place the received character at the end of your char array: data[10] = serial_Rx(UDR);. So the next received character will overwrite the previous, and the first nine chars will always be empty/memory garbage. Also serial_RX() has no parameters but you passed UDR to it in the main. \$\endgroup\$ – Bence Kaulics Jul 27 '15 at 7:08
  • \$\begingroup\$ @BenceKaulics the char I am receiving also need to be displayed at hyper terminal that's why I am receving it and the storing it in data and then transmiting the same. In serial_Rx I am returning UDR so I have used it in main.Is it wrong? \$\endgroup\$ – anna carolina Jul 27 '15 at 7:15
  • \$\begingroup\$ It is not generating error but serial_Rx will work without UDR in the paramlist because UDR is a register of the uC, you can reach it from anywhere in your code without passing it as an argument. The real problem is what I have mentioned first. You are using a char array, length of 10 and you put the received char to the last position, the first nine remains empty. After that, you send the whole array to hyperterminal, but you should send the last one only because the others are empty. You need a function that sends only one char and with that you can send to the last char of your array. \$\endgroup\$ – Bence Kaulics Jul 27 '15 at 7:45
  • \$\begingroup\$ @BenceKaulics Ok i got your point. If I do it like this: data= Serial_Rx(UDR). and then print this data, is this thing correct? \$\endgroup\$ – anna carolina Jul 27 '15 at 7:48
  • \$\begingroup\$ Oh and I have almost forget. Data is an array of chars, 10 long. So it has 10 slots, from 0 to 9. So it is wrong to use data[10] = serial_RX(); because index 10 means slot 11 which is do not exists and you are overindexing the array. Here is a tutorial about Arrays in C. \$\endgroup\$ – Bence Kaulics Jul 27 '15 at 7:52
1
\$\begingroup\$

Your receiver code still has a mistake in it.

for(int j=0;j<=100;j++)
{
    data[j] = serial_Rx();
    uart_put(data[j]);  
}

In this loop you are still overindexing the data array, which is only a size of 2. You should increase its size to 101 to match your for loop. Probably a smaller value will be sufficient too, but the size of the array should match the for loop.
As I see on hyperterminal, Local Echo is disabled on your module so it will reply to most of the commands with "\r\nOK\r\n" or "\r\nERROR\r\n". (With Local Echo enabled it would return the command too). So the longest message that you could receive is the error message which is 9 characters long.

char data[10];       //9 for data 1 for terminating null character 

for(int j=0;j<9;j++)
{
    data[j] = serial_Rx();
}
data[9] = '\0';     // close string with a terminating null character

Now, the response is presented in the array as a proper string. It is ready to be processed either by your own functions or by the standard C String library (#include <string.h>). Or you can still send the whole string to hyperterminal as well serial_Tx(data).

Process the result of the previous code with strstr() function of string.h

    if(strstr(data,"OK") != NULL)
    {
        // it contains "OK"
        // do stuff
    }

More accurate way to receive serial data is to enable UART receive complete interrupts by setting RXCIE bit in the UCSRB register: UCSRB |= (1<<RXCIE); If you look at the possible responses, the effective information is enclosed between a '\n' and a '\r' characters which makes excellent start and end conditions when parsing the GSM's response in the interrupt service routine.

The complete code is the following:

void serial_Init()
{
    UCSRB |= (1<<TXEN)|(1<<RXEN)|(1<<RXCIE);    // enable receive interrupts
    UCSRC |= (1<<UCSZ1)|(1<<UCSZ0)|(1<<URSEL);
    UBRRL = 51;
}

void serial_Tx(char *str)
{
    for (unsigned int i=0;str[i]!=0;i++)
    {
        UDR=str[i];
        while(!(UCSRA&(1<<UDRE)));
    }
}

char serial_Rx()
{
    while(!(UCSRA & (1<<RXC)));
    return UDR;
}

void uart_put(char data)
{
    UDR=data;
    while(!(UCSRA&(1<<UDRE)));
}

///////////////////////////////////
// global variables for UART rx
///////////////////////////////////
int buffer_index = 0, started = 0;
unsigned char buffer[250];
unsigned char k;

ISR(USART_RXC_vect)
{
    k = UDR;

    if(k == '\r' && started == 1)  // stop if is has already started and "\r" received
    {
        started = 2;               // finished, ready for further processing
        buffer[buffer_index] = '\0';            // terminate string
    }                              
    else if(started == 1)          // if started save data
    {                              
        buffer[buffer_index++] = k;             
    }                              
    else if(k == '\n')             // start saving data at first "\n"
    {                              
        started = 1;               // set process started
    }
}

int main(void)
{
    DDRA = 0x00;
    DDRC = 0xFF;
    PORTA = 0xFF;
    serial_Init();
    sei();

    while(1)
    {
        if(PINA==0xFE)
        {
            buffer_index = 0;
            started = 0;
            serial_Tx("ATD<number>;\r\n");
        }

        if(started == 2)        // check if rx is finished
        {
            // check if OK
            serial_Tx(buffer);
        }
    }
}

ISR explanation:

So the structure of the messages we are expecting is the following: <\r><\n><data><\r><\n>. Based on this, we want to start saving the incoming data after the first \n was detected and keep it up until we reach a \r.
Now the roles of the used variables:

  • k is a temporary variable into which the received chars are loaded, then we can check if it is a \n, \r or data char and handle accordingly.
  • started is for tracking the different phases:
    started = 0 means, we did not receive the first \n so far, so we keep waiting (else if(k == '\n') branch ). Once a \n arrives we set it to 1 indicating that we can start loading the received chars (k) into our array.
    started = 1 means that the start condition has been fulfilled, data can be stored in the array.
    started = 2 when the processing has been started, so if the previous value was 1 and we received a \r (if(k == '\r' && started == 1) branch). Value 2 stops the loading of the array and indicates to the main function that the complete data is presented in the array.

I have a GSM module connected to an AVR too, so I tested the code of the ISR and get the following result:

enter image description here

This way data will contain "OK" or "ERROR".

\$\endgroup\$
  • \$\begingroup\$ Hey first of all thanks for this great info. But unfortunately i didnt receive any call on my number.I am just receiving a series of AT<number> on terminal. Can you explain what the ISR is doing actually and what are the roles of variable started and k? Thanks \$\endgroup\$ – anna carolina Jul 28 '15 at 5:36
  • \$\begingroup\$ one thing also. In this if(strstr(data,"OK") != NULL), I am using an led and it gets turn on when it found OK. But what I have done I have used it for ERROR also like if(strstr(data,"ERROR") != NULL) and I have made some changes in AT<number> command so that is gives error but then led dosent turn on \$\endgroup\$ – anna carolina Jul 28 '15 at 6:14
  • \$\begingroup\$ I have edited the answer and added an explanation about the ISR, I hope it is not confusing. You can try to increase the size off the array because maybe you receive more \r\n and there is not enough room for "ERROR". On the other hand if the if(strstr(data,"OK") != NULL) works then add an else branch, since you could receive "ERROR" or "OK" if it is not "OK" then "ERROR". \$\endgroup\$ – Bence Kaulics Jul 28 '15 at 9:16
  • 1
    \$\begingroup\$ OK thanks for logic analyzer, and I have connected my headphones to the module and audio is coming. \$\endgroup\$ – anna carolina Jul 29 '15 at 6:12
  • 1
    \$\begingroup\$ you have not included sei();. You are using UDR1 instead of UDR and USART_RX_vect instead of USART_RXC_vect \$\endgroup\$ – anna carolina Aug 3 '15 at 5:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.