|
|
View previous topic :: View next topic |
Author |
Message |
Omarfarouk
Joined: 24 Mar 2022 Posts: 10
|
Simple SIM800L code (GSM)(read and write SMS) |
Posted: Mon May 09, 2022 9:36 am |
|
|
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",©); //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
|
|
Posted: Mon May 09, 2022 9:50 am |
|
|
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
|
|
Posted: Tue May 10, 2022 2:52 am |
|
|
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: 19505
|
|
Posted: Tue May 10, 2022 4:03 am |
|
|
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
|
|
Posted: Tue May 10, 2022 5:42 am |
|
|
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
|
|
Posted: Wed May 11, 2022 7:12 am |
|
|
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: 19505
|
|
Posted: Thu May 12, 2022 3:44 am |
|
|
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: 9225 Location: Greensville,Ontario
|
|
Posted: Thu May 12, 2022 5:41 am |
|
|
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
|
|
Posted: Thu May 12, 2022 12:40 pm |
|
|
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
|
|
Posted: Thu May 12, 2022 12:46 pm |
|
|
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
|
|
Posted: Thu May 12, 2022 12:49 pm |
|
|
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
|
|
Posted: Fri May 13, 2022 8:33 am |
|
|
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
|
|
Posted: Fri May 13, 2022 9:00 am |
|
|
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
|
UPDATE! |
Posted: Mon May 16, 2022 9:45 am |
|
|
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
|
|
Posted: Tue May 17, 2022 12:44 pm |
|
|
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. |
|
|
|
|
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
|