CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Simple SIM800L code (GSM)(read and write SMS)
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Omarfarouk



Joined: 24 Mar 2022
Posts: 10

View user's profile Send private message Send e-mail

Simple SIM800L code (GSM)(read and write SMS)
PostPosted: Mon May 09, 2022 9:36 am     Reply with quote

I am trying to make up a simple code for the SIM800L. I am currently using the PIC16F722A, I know it's RAM might not be able to fit in extra code, but for now I want to make up a simple code with it.

I was able to send a message to my phone but I am not able to read correctly from the module.

Would love to listen to all your suggestions!

Code I was able to make up till now:
Code:

#include <16F722A.h>
#device ADC=16

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES NOBROWNOUT               //No brownout reset
#FUSES BORV19                   //Brownout reset at 1.9V
#FUSES NOVCAP                   //VCAP pin disabled

#use delay(internal=4000000)

//#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=GSM,errors)
#use rs232(baud=9600, bits=8, xmit=PIN_C6, rcv=PIN_C7,  stream=GSM)
#use rs232(baud=9600, bits=8, xmit=PIN_B2,  stream=BUG)     //debug screen

#define inA PIN_A2
#define inB PIN_A3

char copy[30];
char AT[] = "AT\r\n";             // Every AT command starts with "AT"
char TXT[] = "AT+CMGF=1\r\n";     // set TXT messages
char TO[] = "AT+CMGS=\"+44xxxxxxx\"\r\n";
char SET[] = "AT+CNMI=1,2,0,0,0\r\n";
int x = 0;

void GSM_Send(char *s) {
// Send command or data string
   while(*s) {                      // as long as you don't encounter NULL
      fputc(*s++);                  // send characters to RS232
   }
}

void send(){
   GSM_Send(AT);
   delay_ms(200);
   GSM_Send(TXT);
   delay_ms(200);
   GSM_Send(SET);
   delay_ms(200);
   GSM_Send(TO);
   delay_ms(200);
   
   if (x == 1) {
   fputs("Module ON");}
   if (x == 2) {
   fputs("PORT A ON");}
   
   delay_ms(100);
   fputc(0x1A);                              // send CTRL+Z to send the message
   delay_ms(200);
   x = 0;
}

void inputs(){
   if(input_state(inA) == 1){
      x = 2;
      send();
      delay_ms(3000);
   }
}

void read() {                             //help needed!!!! how to read SMS?
   if(kbhit(GSM)){
      //fgets(copy,GSM);                  //does not work
      fscanf(GSM,"%s",&copy);             //want to scan text from module
      fprintf(BUG, "%s\r\n", copy);       //print GSM string
      delay_ms(10);
   }
}

void main() {
   delay_ms(5000);
   x=1;
   send();
   while(TRUE){
      inputs();
      read();
   }

}


Last edited by Omarfarouk on Tue May 10, 2022 5:44 am; edited 2 times in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon May 09, 2022 9:50 am     Reply with quote

You've defined two serial streams, but you're not using them in your
fputc() and fputs() statements. If you don't specify the stream in the
function parameters, then CCS will use the last stream that was
defined.

Also, both your serial streams (GSM and BUG) are software UARTs.
Omarfarouk



Joined: 24 Mar 2022
Posts: 10

View user's profile Send private message Send e-mail

PostPosted: Tue May 10, 2022 2:52 am     Reply with quote

Quote:

You've defined two serial streams, but you're not using them in your
fputc() and fputs() statements.


I am using them in the read() it sends me messaged with no problem. but yea I will specify it to avoid confusion.

Quote:

both your serial streams (GSM and BUG) are software UARTs.

could you illustrate ? how will it affect me or how will it help me ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19515

View user's profile Send private message

PostPosted: Tue May 10, 2022 4:03 am     Reply with quote

Your 'remmed' out #use is the correct one to use for your GSM connection.
Move the connections so you have C6 & C7 to the GSM device, and put
your debug somewhere else.

For the debug output only a software UART is 'OK'.

For something involving transmit & receive a software UART is awful.
Problem is you have to actually be _waiting_ with the code sitting looping
looking for data to arrive, or characters will be missed or corrupted.
A software UART only supports half duplex (so can only send or receive
not both together). It has no buffering.
Omarfarouk



Joined: 24 Mar 2022
Posts: 10

View user's profile Send private message Send e-mail

PostPosted: Tue May 10, 2022 5:42 am     Reply with quote

I have updated it to use it's main UART.

Do you have any idea how to work around with a simple approach for reading incoming text from the GSM ?

How to copy the GSM serial to a string ?
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Wed May 11, 2022 7:12 am     Reply with quote

Quote:

How to copy the GSM serial to a string ?


Put the answer from GSM module in a buffer in serial receive interrupt.
Ttelmah



Joined: 11 Mar 2010
Posts: 19515

View user's profile Send private message

PostPosted: Thu May 12, 2022 3:44 am     Reply with quote

Look at the source code for gets (in string.h). This shows how to receive
characters into a string.
A 'string' doesn't actually exist in C. All this is in C is an array of characters
with a null (0) terminator. So all you do is receive the characters one by
one into an array, and when you see the line feed, put a 0 into the array,
and go to handle this data.
temtronic



Joined: 01 Jul 2010
Posts: 9228
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu May 12, 2022 5:41 am     Reply with quote

also.....
whenever you're receiving anything from a 'serial' connection, you should use a 'timed receive' function otherwise the PIC could sit there waiting forever for the 'end of data'.

CCS provides an example in the FAQ/ Q&A section of the manual. In a nutshell, you decide how long you can wait for the data,then set a timeout value to say 2x that amount. The function will exit either when the data has come in OR the timeout occours. If a timeout occours, a 'flag' is set to indicate 'timedout'.

Say you're running at 9600 baud, that's about 1 character per millisecond, say you expect to see a maximum of 12 characters, 12 milliseconds, so set the timeout value to 24 milliseconds.
benoitstjean



Joined: 30 Oct 2007
Posts: 566
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Thu May 12, 2022 12:40 pm     Reply with quote

If I may add my two cents, I have been using long-enough the SIMCOM modems to tell you right away that most likely the strings you receive start and end with \r\n (carriage return followed by linefeed) probably like many other (if not all other) modems.

So if you send 'AT', the modem will respond 0x0A 0x0D O K 0x0A 0x0D (so \r\nOK\r\n).

Now you need to figure-out a way to count the incoming data and make sure that you analyze each character as they arrive and when you get to the last expected \n, then you know the string has been received. I don't use the handshaking lines on the PIC UART, I just used TX and RX and figured-out my own protocol.

If you were to activate the UART multiplexer, then that's a whole different beast. The multiplexer must be intialized in a very specific way and then each packet will start and end with \xF9.

Also, I strongly suggest you get your hands on a logic analyzer because some of the SIMCOM responses have sort of 'typos' so the formatting is not always the same. This means that you almost have to analyze everything you are testing so that you can find the 'problematic' messages. One of these is the +CGPS message (not sure it is available on the SIM800): Many responses usually start with a + followed by a comma, a space then parameters such as this:

+CMGSO: 200

But in the case of the +CGPS response, for whatever reason, there's no space between the : and parameters so it looks like this +CGPS:452296.... instead of +CGPS: 452266....

Then you've got other responses that for whatever reason will have two sets of \n\r at the end like \n\r\n\r.

This all means that you will have to create special cases for problematic responses but this may be difficult to nail-down unless you have a logic analyzer like the Saleae devices or whatever else is available to you.

And I think the SIM800 is super old 2G technology. I suggest you go with the SIM7600 or greater. These things are awesome.

Good luck.

Ben
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Thu May 12, 2022 12:46 pm     Reply with quote

This doesn't use timed receive, but it should work at least as a starting point:

Code:

#include <main.h>                        // processor, clock, uart, etc. setup

#define BUFFER_SIZE 32                          // create 32 bytes large buffer (can be bigger, to be able to catch also SMS)
char buffer[BUFFER_SIZE];                   // string buffer
int8 next_in = 0;                               // max. next_in =  BUFFER_SIZE -1 !!!!!
int8 Got_Answer = 0;                     // flag for main
char tmp;                                       // received char, global for debugging purposes

//**********************************************
//                INTERRUPTS
//**********************************************

#INT_RDA
void  RDA_isr(void)
{

   tmp=getc();                                  // get received char and with that also clear interrupt flag
   
   if(tmp == 13){
      return;                                   // ignore carriage return (binary 13). If modem outputs that too after every message, prevent it to be stored in the buffer
   }

   buffer[next_in]= tmp;                        // move received char to the appropriate place in buffer
   
   if(tmp == 10){                               // if we got line feed (binary 10),response is complete, put NULL at the end of the message to be able to manipulate that string
      buffer[next_in] = '\0';
      next_in = 0;
      Got_Answer = 1;                           // answer from modem is in the buffer, null terminated. Signal to main to do something about it
      return;                                   // exit interrupt
   } 
   next_in++;                                   // if not CR or LF, increment IN pointer   
   if(next_in == BUFFER_SIZE) {                 // prevent rollover of the buffer, but if you ever come here, the data will be corrupted         
      next_in=0;
      };
}

//**********************************************
//           END INTERRUPTS
//**********************************************

void main()
{
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   while(TRUE)
   {
      if(Got_Answer){
         Got_Answer = 0;
         delay_cycles(1);
         // procss data
      }
   }
}


Last edited by PrinceNai on Sat May 14, 2022 8:15 am; edited 1 time in total
benoitstjean



Joined: 30 Oct 2007
Posts: 566
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Thu May 12, 2022 12:49 pm     Reply with quote

And I would also suggest you get your characters using the #INT_RDA interrupt. In fact, I personally think you should drive your entire system off interrupts.
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Fri May 13, 2022 8:33 am     Reply with quote

Quote:

So if you send 'AT', the modem will respond 0x0A 0x0D O K 0x0A 0x0D (so \r\nOK\r\n)


If what Benoitsjean says is true, then the code I posted will give you false Got_Answer = 1; BEFORE the actual response will start coming in. There should be another check in INT_RDA to ignore the first Line Feed. CR is ignored as it is. So when the first character received is Line Feed, ignore it.

Code:

void  RDA_isr(void)
{

   tmp=getc();                                  // get received char and with that also clear interrupt flag
   
   
   
   if(tmp == 13){
      return;                                   // ignore carriage return (binary 13). If modem outputs that too after every message, prevent it to be stored in the buffer)
   }
   
   if((tmp == 10) && (next_in == 0)){
      return;                                   // ignore the first line feed also, when no actual data is in the buffer yet
   }

   buffer[next_in]= tmp;                        // move received char to the appropriate place in buffer
   
   if(tmp == 10){                               // if we got line feed (binary 10),response is complete, put NULL at the end of the message to be able to manipulate that string
      buffer[next_in] = '\0';
      next_in = 0;
      Got_Answer = 1;                           // answer is in the buffer, null terminated. Signal to main to do something about it
      return;                                   // exit interrupt
   } 
   next_in++;                                   // else increment IN pointer   
   if(next_in == BUFFER_SIZE) {                 // prevent rollover of the buffer, but if you ever come here, the data will be corrupted         
      next_in=0;
      };
}


Last edited by PrinceNai on Sat May 14, 2022 8:15 am; edited 2 times in total
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Fri May 13, 2022 9:00 am     Reply with quote

If you want to parse many stings it can get difficult and time consuming. The way I built a system like this was to first write down all the commands or answers from the modem I want to work with and also all the specific SMS messages that were sent to the modem I wanted to do something with, like turning some relays on or off. Then I wrote a state machine in the interrupt routine that went through the characters and only set flags for the main when the correct sequence was received. Long code, but relatively easy to read and super fast.

I do agree that a logic analyzer is a must to catch all the odd answers. Or spy on the conversation on the PC with a terminal that shows special characters too, so that you can see everything what was sent from the modem.
Omarfarouk



Joined: 24 Mar 2022
Posts: 10

View user's profile Send private message Send e-mail

UPDATE!
PostPosted: Mon May 16, 2022 9:45 am     Reply with quote

I have been working on it ever since my post. Sorry for not actively replying at the time.

I was able to make up a version which actually somehow works on this tiny chip. Now it's taking up 98% of the ROM which I am sure is not the way to go but for now it is actually working. I will be taking out my debug which will give me a bit more space.

I am using an external EEPROM with the chip and unfortunately right now I am unable to take input from the original Rx so I am using pins B2 & B1. I will be making a new board with the right connections and transfer to using original Rx & Tx.

The code's idea is pretty basic: save phone numbers, send alerts to saved numbers for any changes to inputs, turn ON & OFF using basic commands

commands:
#A1 (ON input A)
#A0 (OFF input A) etc.
#1#07.... (save number 1)
#2#07.... (save number 1)

[code is not fully polished]{would love to hear out your feedback}{I was trying to make the simplest program possible for limited RAM & ROM}(in my case 2kb ROM 128bytes RAM)

Code:

#include <send.h>
#include <stdlib.h>
#include <string.h> 
//#include <input.c>
//#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=GSM,errors)
#use rs232(baud=9600, bits=8, xmit=PIN_B2, rcv=PIN_B1,  stream=GSM, errors)
#use rs232(baud=9600, bits=8, xmit=PIN_C7,  stream=BUG, errors)     //debug screen

#define button PIN_A2
#define LED PIN_A3

#define number_SIZE 11
#define BUFFER_SIZE 14

char buffer[BUFFER_SIZE];
char number[number_SIZE];
char tmp;
int8 next_in = 0;
int8 count = 0;
int1 new_data;

char AT[] = "AT\r\n";                                    // Every AT command starts with "AT"
char TXT[] = "AT+CMGF=1\r\n";                            // set TXT messages
char TO1[] = "AT+CMGS=\"";
char TO2[] = "\"\r\n";
char SET[] = "AT+CNMI=1,2,0,0,0\r\n";
int messages = 0;                                        //messages flag
int phone = 0;                                          //which phone

void GSM_Send(char *s) {
// Send commands
   while(*s) {                      // as long as you don't encounter NULL
      fputc(*s++);
   }
}

void send(){
   GSM_Send(AT);
   delay_ms(200);
   GSM_Send(TXT);
   delay_ms(200);
   GSM_Send(SET);
   delay_ms(200);
   GSM_Send(TO1);
   if(phone == 0){
      for ( int i=0;i<11;i++){
         printf("%d", read_ext_eeprom(i));
      }
   }
   if(phone == 1){
      for ( int i=11;i<22;i++){
         printf("%d", read_ext_eeprom(i));
      }
   }
   if(phone == 2){
      for ( int i=22;i<33;i++){
         printf("%d", read_ext_eeprom(i));
      }
   }
   GSM_Send(TO2);
   delay_ms(200);
   
   if (messages == 1) {
   fputs("Module on");}
   if (messages == 2) {
   fputs("Alarm A on");}
   if (messages == 3) {
   fputs("Alarm A off");}
   if (messages == 4) {
   fputs("Alarm B on");}
   if (messages == 5) {
   fputs("Alarm B off");}
   if (messages == 6) {
   fputs("New phone number saved.");}
   if (messages == 7) {
   fputs("Invalid number.");}
   
   delay_ms(100);
   fputc(0x1A);                              // send CTRL+Z to send the message
   delay_ms(3000);
}

void send_all(){
   phone = 0;
   send();
//!   delay_ms(10000);
//!   phone = 1;
//!   send();
//!   delay_ms(10000);
//!   phone = 2;
//!   send();
}

void inputs(){
   if(input_state(button) == 1){
      messages = 2;
      send_all();
      delay_ms(500);
   }
}

void read() {
   if(kbhit(GSM)){
      tmp = getc();
      count++;
      if(count>51){
      buffer[next_in]= tmp;
      next_in++;
      }
      if(next_in == 14 && count>51 || tmp == '\n' && count>51){
         buffer[next_in] = '\0';             // terminate string with null
         next_in = 0;
         count = 0;
         fprintf(BUG, "%s\r\n", buffer);
         new_data = 1;
         delay_ms(1500);
      }
   }
}

void reset(){
   buffer[0]=0x00;
   new_data = 0;
}

void new_number() {
   int n = 0;
   for (int i = 3; i < 14 && n < 11 && (buffer[i] == '0' || buffer[i] == '1'||buffer[i] == '2'||buffer[i] == '3'||buffer[i] == '4'||buffer[i] == '5'||buffer[i] == '6'||buffer[i] == '7'||buffer[i] == '8'||buffer[i] == '9'); i++) {
      number[n] = buffer[i];
      n++;
      delay_ms(20);
   }
   number[n] = '\0';
   fprintf(BUG, "%s\r\n", number);
   reset();
   delay_ms(300);
   if (n != 11){
      messages = 7;
   }  else {
      fprintf(BUG, "a phone num\r\n");
      if(phone == 0){
         for (int i=0 ; i<11 ; i++) { write_ext_eeprom(i,(number[i]-'0')); }
         messages = 6;
      }
      if(phone == 1){
         int n = 11;
         for (int i=0 ; i<12 ; i++) { write_ext_eeprom(n,(number[i]-'0')); n++; }
         messages = 6;
      }
      if(phone == 2){
         int n = 22;
         for (int i=0 ; i<12 ; i++) { write_ext_eeprom(n,(number[i]-'0')); n++; }
         messages = 6;
      }
   }
   send_all();
}

void Data() {
   if(new_data == 1){
      if(buffer[0] == '#'){
         if(buffer[1] == 'A'){
            if(buffer[2] == '0'){fprintf(BUG, "Alarm A OFF\r\n");output_low(LED);}
            if(buffer[2] == '1'){fprintf(BUG, "Alarm A ON\r\n"); output_high(LED);}
            reset();
         }
         if(buffer[1] == 'B'){
            if(buffer[2] == '0'){fprintf(BUG, "Alarm B OFF\r\n");}
            if(buffer[2] == '1'){fprintf(BUG, "Alarm B ON\r\n");}
            reset();
         }
         if(buffer[1] == '1' && buffer[2] == '#'){
            phone = 0;
            new_number();
         }
         if(buffer[1] == '2' && buffer[2] == '#'){
            phone = 1;
            new_number();
         }
         if(buffer[1] == '3' && buffer[2] == '#'){
            phone = 2;
            new_number();
         }
      }
      else{
         reset();
      }
   }
}

void main() {
   delay_ms(2000);
   init_ext_eeprom();
   delay_ms(6000);
   messages=1;
   send();
   delay_ms(5000);
   output_low(LED);
   while(TRUE){
      inputs();
      read();
      Data();
   }
}
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Tue May 17, 2022 12:44 pm     Reply with quote

Glad it works. I'd go to a bigger PIC with your new board, since leaving the debug code out won't give you back 50% of ROM :-). I made a similar project with some extra options and it works. 95% of the time. All the routines work. Receiving part works. Sending works. Up until a point where modem loses connection or outputs "ERROR" instead of "OK" or doesn't send out SMS as it should. I spent 95% of the time hunting down those 5% of cases where it didn't work, where the modem was in some kind of a tilt. And to be honest, never quite made it "bomb proof". It was good enough for turning some lights on or off, but never for a fire alarm it was meant to be.

As for the code, you initialize modem every time you send a message. You can do it only once, at the beginning. Sending will be much faster. You probably don't have enough space to check if the number is already in EEPROM. Also to check if modem is online before you send and also periodically, so that you can be fairly certain that you can receive a message.

Idea for the new board: add a FET to power the modem via PIC. That will give you an option to perform a hard reset. Of course connect hardware RS232 pins to your modem. Hardware makes it so much faster and easier.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group