0
\$\begingroup\$

I am trying to communicate to SIM900 with PIC18F2520. I am able to send AT commands using UART but I also want to receive its response like OK or ERROR.

Following is the code for RX interrupt

#pragma code rx_interrupt = 0x8
void rx_int(void)
{
  _asm goto rx_handler _endasm
}
#pragma code
#pragma interrupt rx_handler
void rx_handler(void)
{
  while (!DataRdyUSART());   //if data is ready to receive
  for(k=0;k<=20;k++)
  {

    Rx[k] = getcUSART();    //read a byte from UART

  }
  UARTSend("We have read everything");   //I have a put a breakpoint here
  if(strstr(Rx,"OK") != NULL)
  {
    UARTSend("OK received"\n);
  }

   PIR1bits.RCIF = 0;   //Clear the interrupt flag 

}

Above is the code I am using for receiving data from SIM900. In the debug mode, I send UARTSend("AT\r");. I have a put a breakpoint at UARTSend("We have read everything");. At this line, I check for the Rx buffer in variable but it doesnt have OK instead it has few random chars while on the terminal I am getting proper response.

enter image description here

So how to read response from SIM900. The approach I am using it correct or not.Please help.

\$\endgroup\$
  • \$\begingroup\$ Why you are assuming that response will be 20 chars? You must buffer the response until "\r\n" 2 char sequence is detected. Then start checking for response content - is it "OK\r\n" or "ERROR\r\n" or something else... there are more to this, in short I suggest to do some google-fu - github.com/search?utf8=%E2%9C%93&q=sim900 \$\endgroup\$ – Flanker Jul 30 '16 at 6:37
1
\$\begingroup\$

A number of mistakes here:

  • Waiting for a byte to come in inside the RX interrupt; the fact that you're there pretty much tells you a byte has arrived.

  • Reading 20 bytes when all you have is notification that one byte has arrived.

  • Assuming that a response from the module will be exactly 20 bytes. You can see from your terminal that 'OK' + a bunch of CRLFs is returned when you send 'AT' so why read 20 bytes?

  • You appear to be using the same UART lines for talking to the SIM900 and your PC. Way to cause confusion.

  • You are sending data within the ISR to the PC. ISRs are meant to run quickly and return.

You need to use carefully chosen timeouts to know when the module is done talking to you. Your handler should look like this:

void rx_handler(void) {
  Rx[k++] = getcUSART(); //read a byte from UART
  lastRead = 0;
  PIR1bits.RCIF = 0; //Clear the interrupt flag
}

I assume k is a file scope variable and is declared volatile. Since you are using a PIC and so dont have the Arduino millis() or ARM SysTicks readily available, you can either implement something similar with a Timer peripheral or use delays as a short-term work-around.

Declare these:

  #define TIMEOUT 100
  volatile static unsigned int lastRead;

Your main() should look like this:

  Uart_Send("AT\r");
  while (lastRead < TIMEOUT){
    __delay_ms(1);
    lastRead++;
  }
  if (strstr(Rx, "OK") != NULL){
    while (1){
      LATBbits.RB0 = 0; // blink quickly
      __delay_ms(500);
      LATBbits.RB0 = 1;
      __delay_ms(500);
    }
  }
  else {
    while (1) {
      LATBbits.RB0 = 0; // blink slowly
      __delay_ms(1500);
      LATBbits.RB0 = 1;
      __delay_ms(1500);
    }
  }

Set up RB0 as an output pin and connect an LED. I have used LED blink rates to indicate success or failure of your command. If you want to read the actual reply, either get an LCD or set up a Software UART port for talking to the PC; IIRC, the peripheral libraries include a Soft_Uart library.

Understand that this code is only to test if you can talk to the module. For any interesting uses, you'll need to implement a ring/circular buffer and Software UART, if you want to talk to your PC as well. I suggest you find an existing SIM900 library and try porting the relevant functions, used for sending commands and parsing responses, to C18 C.

\$\endgroup\$
  • \$\begingroup\$ You left out sending a message from an ISR, which will probably block for many times longer than the interval until the next character. \$\endgroup\$ – Chris Stratton Jul 30 '16 at 18:18
  • \$\begingroup\$ The downvote, however, is for the unsafe parts of your proposal. Because the increment operation of the lastRead flag is not atomic, you can miss the notification of a character arriving between the time the value is read and the time it is written back. Also you fail to ever reset the reader - it will just keep collection characters, quickly exceed the buffer, and cause a crash. \$\endgroup\$ – Chris Stratton Jul 30 '16 at 18:23
  • \$\begingroup\$ @ChrisStratton I noticed the 'Uart_send()' part but forgot to include it when I got to the end of the list. If you read to the end of the code, you'd have noticed that I placed a while (1); at the end indicating that this is expected to run only once to test that AT works and then stall. I mentioned 'short-term work-around' and asked the OP to check out Software UART and consider implementing a proper ring buffer and something millis()-like. \$\endgroup\$ – TisteAndii Jul 30 '16 at 23:40
0
\$\begingroup\$

the main issue is 20 number of characters wont get received at the same time. So you should use while (!DataRdyUSART()); before each read.

while (!DataRdyUSART());   //if data is ready to receive
  for(k=0;k<=20;k++)
  {
    while (!DataRdyUSART());   //if data is ready to receive
    Rx[k] = getcUSART();    //read a byte from UART
  }

Now the next problem will be with the number 20. all responses wont have a length of 20. So try to read till you receive or

I suggest you to write these codes on main, instead of ISR. Just set a flag in ISR and check the status of the flag in main code. But this purely depend on the algorithm that you need implement, But make sure that multiple interrupts wont occur within execution of previous ISR

\$\endgroup\$

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.