/******************************************************************************* Simple terminal program based on a terminal and SMS sending program Copyright (C) 2003-2011 Jiri Pittner This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUFL 256 #define MAXLINE 512 //GSM requires #define MAXSMS 161 #define MAXNUMBER 16 #define MAXPDU (MAXSMS*7/8*2+32)+128 #define CELL_ID_LENGTH 4 #define OPER_ID_LENGTH 7 #define MAXLIST (20*MAXPDU) //porting code originally written for atmel avr #define PSTR(X) X #define strcpy_P strcpy #define strcat_P strcat #define itoa10(n,s) sprintf(s,"%d",n) void xerror(error_text) char error_text[]; { fprintf(stderr,"smsm: %s\n",error_text); exit(10); } void usage() { printf("Usage: smsm [-0][-H] [-u] [-D device] [-S host theirport ourport] [-A] [-h handshake] [-Bbaud] [-U startupdelay] [-d number[:number:...]] [-V] [-I] [-l] [-p] [-m maxpiece] [-r storage_type] [-s storage_type position [number name|file]] [-f name] [name|number 0) { if(FD_ISSET(f,&readfds)) { r=read(f,buf,bufsize); if(r==0) {fprintf(stderr,"Device disconnected!\n"); exit(1);} if(hex) { //@@@@must not use printf but sprintf and write for(int i=0; i"); z= !strstr(buf,"OK"); x= !strstr(buf,"ERROR"); y= !strstr(buf,"RING") && !strstr(buf,"BUSY"); delay_cs(100); if(r==0) ++t; //if read returns repeatedly 0 timeout if(t>60) goto do_timeout; } while(z && x && y && w); //break on sms-prompt or termination if(x==0) status=2; //error if(y==0) status=3; //ring if(z==0 || w==0) status=0; //OK #ifdef debug fprintf(logfilepointer,"DEBUG From phone: %s\n",buf0); fflush(logfilepointer); #else fputs(buf0,stdout); #endif } else { do_timeout: status=1; //timeout phone_not_on += 1; #ifdef DEBUG fprintf(logfilepointer,"Phone not answering: timeout\n"); fflush(stderr); #endif } signal(SIGALRM,SIG_IGN); //switch off deadline timer //to be sure that phone is ready to receive new data if(status!=1) delay_cs(20); return status; } void phone_reset(void) { char buf[BUFL]; strcpy_P(buf,PSTR("AT\r")); communicate(buf,buf+BUFL,150); strcpy_P(buf,PSTR("ATZ\r")); communicate(buf,buf+BUFL,150); strcpy_P(buf,PSTR("ATZ\r")); { uint8_t r; r=communicate(buf,buf+BUFL,150); //if timeout mark phone as off if(r==1) {phone_not_on=1; return;} } delay_cs(1000); //some AT commands report error until the phone is connected to the network //we will poll, do not use incoming message notification //strcpy_P(buf,PSTR("AT+CNMI=1,1,0,0,1\r")); communicate(buf,buf+BUFL,200); } void get_location(char *text) { char buf[BUFL],*p, *q; strcpy_P(buf,PSTR("AT+COPS?\r")); communicate(buf,buf+BUFL,100); p=strstr(buf,"COPS:")+5; p=strchr(p,'"')+1; q=strchr(p,'"'); *q=0; strncpy(text,p,OPER_ID_LENGTH); text[OPER_ID_LENGTH]=0; //ericsson_A2628 does not give cell id in +creg and it is thus useless #if !defined (ericsson_a2618s) strcat_P(text,PSTR(":")); text+=strlen(text); #if defined( nokia_6310i) || defined (siemens_c45) strcpy_P(buf,PSTR("AT+CREG=2\r")); communicate(buf,buf+BUFL,100); #endif strcpy_P(buf,PSTR("AT+CREG?\r")); communicate(buf,buf+BUFL,100); p=strstr(buf,"CREG:")+5; p=strrchr(p,',')+2; strncpy(text,p,CELL_ID_LENGTH); text[CELL_ID_LENGTH]=0; #endif } uint8_t get_number(uint8_t loc, char *number, char *label) { char buf[BUFL],*p,*q; strcpy_P(buf,PSTR("AT+CPBR=")); itoa10(loc,buf+strlen(buf)); strcat_P(buf,PSTR("\r")); communicate(buf,buf+BUFL,100); p=strchr(buf,','); if(!p) return 1; p+=2; if(*p == '+') ++p; //skip international + if relevant q=strchr(p,'"'); *q=0; strcpy(number,p); if(label) { p=strchr(q+2,','); if(!p) *label=0; else { p+=2; q=strchr(p,'"'); *q=0; strcpy(label,p); } } return 0; } uint8_t put_number(uint8_t loc, char *number, char *label) { char buf[BUFL],*p,*q; strcpy_P(buf,PSTR("AT+CPBW=")); itoa10(loc,buf+strlen(buf)); strcat_P(buf,PSTR(",\"")); strcat(buf,number);//number contains + strcat_P(buf,PSTR("\",145,\"")); strcat(buf,label); strcat_P(buf,PSTR("\"\r")); return communicate(buf,buf+BUFL,100); } /*PDU-coding and SMS-specific routines*/ uint8_t letter2bin (char c) { return c>'9' ? c+10-(c>='a'?'a':'A') : c-'0'; } uint8_t octet2bin(char* octet) { return (letter2bin(octet[0])<<4) | letter2bin(octet[1]); } void bin2letter(char *c, uint8_t b) { *c = b<10? '0'+b : 'A'+b-10; } void bin2octet(char *octet, uint8_t bin) { bin2letter(octet,bin>>4); bin2letter(octet+1,bin&0x0f); } void pdu2ascii(char* pdu0, char* ascii) /* converts a PDU-String to Ascii */ { /* the first octet is the length of the ascii */ uint8_t nremain,i,*binary; uint16_t len,count; char *pdu; //hexa to binary conversion count=octet2bin(pdu0); pdu=pdu0+2; len=strlen(pdu); if((count<<3)-count> (len<<2)) {strcpy_P(ascii,PSTR("ERROR:unexpected end of PDU")); return;} binary=(uint8_t *) pdu0; while(*pdu) { *binary++ =octet2bin(pdu); pdu+=2; } /* now convert from 8-Bit to 7-Bit */ binary=(uint8_t *)pdu0 -1; nremain=0; for(i=0;i 0) //some bits in current binary { ascii[i] |= (*binary>>(8-nremain)); } if(nremain < 7) //we need a new binary for this char { ascii[i] |= *++binary < len/2) {strcpy(ascii0,"unexpected end of PDU\n"); return;} char *ascii=ascii0; while(*pdu) { uint8_t c=octet2bin(pdu); if(c) *ascii++ =c; //discard zero bytes of wide chars pdu+=2; } *ascii=0; } void swapchars(char* string) /* Swaps every second character, argument must have even length */ { char c; while((c=string[0])) { string[0]=string[1]; string[1]=c; string+=2; } } void decodepdu(char *ascii, char *phone, char *date, char *pdu0) { uint8_t i; char *pdu=pdu0; //skip SMS center info i=octet2bin(pdu); pdu+=(i+2)<<1; //decode sender phone number i=octet2bin(pdu); strncpy(phone,pdu+4,i); phone[i]=0; swapchars(phone); if(i&1) {++i; phone[i-1]=0;} pdu+=i+6; uint8_t dcs=octet2bin(pdu); pdu+=2; strncpy(date,pdu,12); date[12]=0; swapchars(date); pdu+=14; if(dcs & 12 ) pdu2wide(pdu,ascii,dcs); else pdu2ascii(pdu,ascii); } void ascii2pdu(char* ascii,char* pdu) { uint8_t *tmp,*tmp0; uint8_t nfree; char c; //message length bin2octet(pdu,strlen(ascii)); pdu+=2; //bit shifts, rewrite into ascii to save space nfree=0; tmp0=(uint8_t *)ascii; tmp=tmp0-1; while((c= *ascii++)) { //part of ascii which goes to current byte if(nfree>0) { *tmp |= c<<(8-nfree); } //part of ascii which goes to next byte if(nfree<7) { *++tmp = (c&0x7f) >> nfree; } nfree=(1+nfree)&7; } //print in hexa for (; tmp0<=tmp; ++tmp0, pdu+=2) bin2octet(pdu,*tmp0); pdu[0]=0; } /* make the PDU string. The destination variable pdu has to be big enough. */ void makepdu(char* message, char* nummer, char* pdu, uint8_t numtype, uint8_t class0) { uint8_t l; //header strcpy_P(pdu,PSTR("001100")); pdu+=6; //phone number l=strlen(nummer); bin2octet(pdu,l); pdu+=2; bin2octet(pdu,numtype); pdu+=2; strcpy(pdu,nummer); if (l&1) strcat_P(pdu,PSTR("F")); swapchars(pdu); //some other constants if(class0) strcat_P(pdu,PSTR("0010A7")); else strcat_P(pdu,PSTR("00F1A7")); //message length and text ascii2pdu(message,pdu+strlen(pdu)); } int read_sms(char *number, char *ascii, uint8_t delete, char *pdu_debug) { char pdu[MAXLIST]; char *p; uint8_t loc; strcpy_P(pdu,PSTR("AT+CMGL=4\r")); communicate(pdu,pdu+MAXPDU,7000); //do not wait for OK, it might be after too a long text, switch off watchdog if ((p=strstr(pdu,"+CMGL:"))) { p+=6; loc=atoi(p); p=strchr(p,'\n')+1; //terminate the PDU if several messages are contained char *q=strstr(p,"+CMGL:"); if(q) *q=0; if(pdu_debug) strncpy(pdu_debug,p,MAXPDU); char date[16]; decodepdu(ascii,number,date,p); } else return 1; //delete after it has been read if(delete) { delay_cs(80); strcpy_P(pdu,PSTR("AT\r")); communicate(pdu,pdu+MAXPDU,100); delay_cs(80); strcpy_P(pdu,PSTR("AT\r")); communicate(pdu,pdu+MAXPDU,100); strcpy_P(pdu,PSTR("AT+CMGD=")); itoa10(loc,pdu+strlen(pdu)); strcat_P(pdu,PSTR("\r")); communicate(pdu,pdu+MAXPDU,300); delay_cs(80); } return 0; } void dial(char *number) { char buf[BUFL]; uint8_t i; strcpy_P(buf,PSTR("AT+CBST=7,0,1\r")); //suitable for calling both gsm and fixed network lines communicate(buf,buf+BUFL,100); strcpy_P(buf,PSTR("ATD+")); strcat(buf,number); strcat_P(buf,PSTR("\r")); communicate(buf,buf+BUFL,6000); delay_cs(500); //wait another 5 seconds strcpy_P(buf,PSTR("+++ATH\r")); communicate(buf,buf+BUFL,100); strcpy_P(buf,PSTR("ATZ\r")); communicate(buf,buf+BUFL,100); } int send_sms(char *number, char *ascii0,uint8_t class0) //its argument ascii will be overwritten { char buf[BUFL], pdu[MAXPDU], ascii[MAXSMS]; uint8_t r; strncpy(ascii,ascii0,MAXSMS-1); ascii[MAXSMS-1]=0; makepdu(ascii,number,pdu,0x91,class0); strcpy_P(buf,PSTR("AT+CMGS=")); itoa10((int)strlen(pdu)/2-1,buf+strlen(buf)); strcat_P(buf,PSTR("\r")); r=communicate(buf,buf+BUFL,350); { char ctrlz[2]={'Z'-'@',0}; strcat(pdu,ctrlz); } return communicate(pdu,pdu+MAXPDU,950); } void reset_termios(int f, speed_t baudrate) { struct termios devicetermios, ttytermios; if(tcgetattr(f,&devicetermios)) perror("cannot tcgetattr 1"); memcpy(&devicetermios0,&devicetermios,sizeof(struct termios)); cfsetispeed(&devicetermios,baudrate); cfsetospeed(&devicetermios,baudrate); devicetermios.c_iflag |= IGNBRK; devicetermios.c_iflag &= ~BRKINT; devicetermios.c_iflag &= ~ICRNL; devicetermios.c_iflag &= ~IMAXBEL; devicetermios.c_iflag &= ~IXON; devicetermios.c_iflag &= ~IXOFF; devicetermios.c_oflag &= ~OPOST; devicetermios.c_oflag &= ~ONLCR; devicetermios.c_cflag |= CLOCAL; devicetermios.c_cflag &= ~CRTSCTS; devicetermios.c_cflag &= ~PARENB; devicetermios.c_cflag &= ~PARODD; devicetermios.c_lflag &= ~ISIG; devicetermios.c_lflag &= ~ICANON; devicetermios.c_lflag &= ~IEXTEN; devicetermios.c_lflag &= ~ECHO; devicetermios.c_lflag &= ~ECHOE; devicetermios.c_lflag &= ~ECHOK; devicetermios.c_lflag &= ~ECHOCTL; devicetermios.c_lflag &= ~ECHOKE; if(tcsetattr(f,TCSANOW,&devicetermios)) perror("cannot tcsetattr 1"); } int main(int argc, char **argv) { int interactive=0; char device[64]="/dev/rfcomm0"; char baud[16]="115200"; speed_t baudrate=B115200; int aoption=0; logfilepointer=stderr; int omittermios=0; uint8_t class0=0; int dohex=0; int theirport; int ourport; char remotehost[64]; char number[MAXNUMBER]; number[0]=0; while((--argc>0)&&((*++argv)[0]=='-')){ if (argc==0) usage(); switch ((*argv)[1]){ case 'S': usesockets=1; omittermios=1; ++argv; --argc; strcpy(remotehost,*argv); ++argv; --argc; sscanf(*argv,"%d",&theirport); ++argv; --argc; sscanf(*argv,"%d",&ourport); break; case 'D': ++argv; --argc; strcpy(device,*argv); break; case 'A': aoption=1; case '0': class0=1; case 'h': dohex=1; case 'N': omittermios=1; case 'B': strcpy(baud,(*argv)+2); if(!strcmp(baud,"2400")) baudrate=B2400; if(!strcmp(baud,"4800")) baudrate=B4800; if(!strcmp(baud,"9600")) baudrate=B9600; if(!strcmp(baud,"19200")) baudrate=B19200; if(!strcmp(baud,"38400")) baudrate=B38400; if(!strcmp(baud,"57600")) baudrate=B57600; if(!strcmp(baud,"115200")) baudrate=B115200; if(!strcmp(baud,"230400")) baudrate=B230400; if(baudrate==B0) xerror("unsupported baudrate"); break; case 'I': ++interactive; break; case 'H': usage(); break; default : xerror("unknown option,; use option -H for information"); } } if(argc > 1) xerror("wrong command line; use option -H for information"); if(argc==1) { if(argv[0][0]!='+') xerror("only phone numbers in international format with + accepted"); strncpy(number,argv[0]+1,MAXNUMBER); } int f,fout; if(usesockets) { } else { f=open(device,O_RDWR+O_SYNC+O_NOCTTY+O_EXCL,0); if(f<0) {perror("cannot open connection to device"); exit(10); } } struct termios devicetermios, ttytermios; if(usesockets) { int sockout; struct sockaddr_in server, from; struct hostent *hp; sockout= socket(AF_INET, SOCK_DGRAM, 0); if (sockout < 0) perror("socket() failed"); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; hp = gethostbyname(remotehost); if(hp==0) perror("Unknown host"); bcopy((char *)hp->h_addr, (char *)&server.sin_addr, hp->h_length); server.sin_port = htons(theirport); int r=connect(sockout,(struct sockaddr *)&server,sizeof(server)); if(r) perror("connect() failed"); fout=sockout; int sockin; struct sockaddr_in serverin; sockin=socket(AF_INET, SOCK_DGRAM, 0); if (sockin < 0) perror("socket() 2 failed"); int length = sizeof(serverin); bzero(&serverin,length); serverin.sin_family=AF_INET; serverin.sin_addr.s_addr=INADDR_ANY; serverin.sin_port=htons(ourport); if(bind(sockin,(struct sockaddr *)&serverin,length)<0) perror("bind() failed"); f=sockin; phone_fd=f; phone_fd_out=fout; filehandle= -1; } else phone_fd = phone_fd_out = filehandle= fout= f; if(!omittermios) { if(tcgetattr(f,&devicetermios)) perror("cannot tcgetattr 1"); memcpy(&devicetermios0,&devicetermios,sizeof(struct termios)); cfsetispeed(&devicetermios,baudrate); cfsetospeed(&devicetermios,baudrate); devicetermios.c_iflag |= IGNBRK; devicetermios.c_iflag &= ~BRKINT; devicetermios.c_iflag &= ~ICRNL; devicetermios.c_iflag &= ~IMAXBEL; devicetermios.c_iflag &= ~IXON; devicetermios.c_iflag &= ~IXOFF; devicetermios.c_oflag &= ~OPOST; devicetermios.c_oflag &= ~ONLCR; //devicetermios.c_oflag |= OCRNL; devicetermios.c_cflag |= CLOCAL; devicetermios.c_cflag |= HUPCL; devicetermios.c_cflag &= ~CRTSCTS; devicetermios.c_lflag &= ~ISIG; devicetermios.c_lflag &= ~ICANON; devicetermios.c_lflag &= ~IEXTEN; devicetermios.c_lflag &= ~ECHO; devicetermios.c_lflag &= ~ECHOE; devicetermios.c_lflag &= ~ECHOK; devicetermios.c_lflag &= ~ECHOCTL; devicetermios.c_lflag &= ~ECHOKE; if(tcsetattr(f,TCSANOW,&devicetermios)) perror("cannot tcsetattr 1"); } if(number[0]) { char text[MAXSMS]="",line[MAXSMS]; phone_reset(); while(fgets(line,MAXSMS-1,stdin)) { if(strlen(line)=2) ttytermios.c_lflag &= ~ISIG; else ttytermios.c_lflag |= ISIG; ttytermios.c_lflag &= ~ECHO; ttytermios.c_lflag &= ~ECHOE; ttytermios.c_lflag &= ~ECHOK; ttytermios.c_lflag &= ~ECHOKE; ttytermios.c_lflag &= ~ECHOCTL; if(tcsetattr(fileno(stdin),TCSANOW,&ttytermios)) perror("cannot tcsetattr 2"); atexit(termioscleanup); signal(SIGINT,handler); rwloop(f,fout,dohex); } xerror("not implemented"); close(f); return 0; }