├── .gitignore ├── images ├── sim-800l-rpi3.jpg └── sim-800l-rpi31.jpg ├── LICENSE ├── README.md └── sim800l.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc -------------------------------------------------------------------------------- /images/sim-800l-rpi3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakhax/raspberry-pi-sim800l-gsm-module/HEAD/images/sim-800l-rpi3.jpg -------------------------------------------------------------------------------- /images/sim-800l-rpi31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakhax/raspberry-pi-sim800l-gsm-module/HEAD/images/sim-800l-rpi31.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 jack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi sim800l gsm module 2 | - connecting the raspberry pi to sim8ool gsm module to send, receive sms, delete sms etc. 3 | 4 | >> SIM900/SIM800 are 2G only modems, make sure your provider supports 2G as it is already being phased out in a lot of areas around the world, else a 3G/4G modem like the SIM7100 / SIM5300 is warranted. 5 | 6 | ## Requirements 7 | - Raspberry pi 3 with Raspbian OS installed. 8 | - Sim800L GSM module 9 | - external power supply for the Sim800L (5v 1A worked for our case, use the correct one for your module) 10 | - A bunch of jumper wires. 11 | 12 | ## References 13 | - https://github.com/vshymanskyy/TinyGSM 14 | - https://lastminuteengineers.com/sim800l-gsm-module-arduino-tutorial/ 15 | - [AT Datasheet](https://www.elecrow.com/wiki/images/2/20/SIM800_Series_AT_Command_Manual_V1.09.pdf) 16 | 17 | ## setup 18 | ### Hardware connection 19 | 20 | 21 | 22 | ### Disable serial console 23 | We will start by disabling serial console to enable communication between the pi and sim800l via serial0 . 24 | 25 | Open the terminal on your pi and run `sudo raspi-config` 26 | Select Interfaces → Serial 27 | Select No to the 1st prompt and Yes for the 2nd. 28 | 29 | 30 | ## Usage examples 31 | 32 | ```python 33 | from sim800l import SIM800L 34 | sim800l=SIM800L('/dev/serial0') 35 | ``` 36 | #### send sms 37 | ```python 38 | sms="Hello there" 39 | #sim800l.send_sms(dest.no,sms) 40 | sim800l.send_sms('2547xxxxxxxx',sms) 41 | ``` 42 | #### read sms 43 | ```python 44 | #sim800l.read_sms(id) 45 | sim800l.read_sms(id) 46 | ``` 47 | 48 | #### callback action 49 | ```python 50 | def print_delete(): 51 | #assuming the sim has no sms initially 52 | sms=sim800l.read_sms(1) 53 | print(sms) 54 | sim800l.delete_sms(1) 55 | 56 | sim800l.callback_msg(print_delete) 57 | 58 | while True: 59 | sim800l.check_incoming() 60 | ``` 61 | -------------------------------------------------------------------------------- /sim800l.py: -------------------------------------------------------------------------------- 1 | 2 | import os,time,sys 3 | import serial 4 | 5 | def convert_to_string(buf): 6 | try: 7 | tt = buf.decode('utf-8').strip() 8 | return tt 9 | except UnicodeError: 10 | tmp = bytearray(buf) 11 | for i in range(len(tmp)): 12 | if tmp[i]>127: 13 | tmp[i] = ord('#') 14 | return bytes(tmp).decode('utf-8').strip() 15 | 16 | class SIM800L: 17 | def __init__(self,ser): 18 | try: 19 | self.ser=serial.Serial("/dev/serial0", baudrate=9600, timeout=1) 20 | except Exception as e: 21 | sys.exit("Error: {}".format(e)) 22 | self.incoming_action = None 23 | self.no_carrier_action = None 24 | self.clip_action = None 25 | self._clip = None 26 | self.msg_action = None 27 | self._msgid = 0 28 | self.savbuf = None 29 | 30 | def setup(self): 31 | self.command('ATE0\n') # command echo off 32 | self.command('AT+CLIP=1\n') # caller line identification 33 | self.command('AT+CMGF=1\n') # plain text SMS 34 | self.command('AT+CLTS=1\n') # enable get local timestamp mode 35 | self.command('AT+CSCLK=0\n') # disable automatic sleep 36 | 37 | def callback_incoming(self,action): 38 | self.incoming_action = action 39 | 40 | def callback_no_carrier(self,action): 41 | self.no_carrier_action = action 42 | 43 | def get_clip(self): 44 | return self._clip 45 | 46 | def callback_msg(self,action): 47 | self.msg_action = action 48 | 49 | def get_msgid(self): 50 | return self._msgid 51 | 52 | def command(self, cmdstr, lines=1, waitfor=500, msgtext=None): 53 | while self.ser.in_waiting: 54 | self.ser.readline() 55 | self.ser.write(cmdstr.encode()) 56 | if msgtext: 57 | self.ser.write(msgtext.encode()) 58 | if waitfor>1000: 59 | time.sleep((waitfor-1000)/1000) 60 | buf=self.ser.readline() #discard linefeed etc 61 | #print(buf) 62 | buf=self.ser.readline() 63 | if not buf: 64 | return None 65 | result = convert_to_string(buf) 66 | if lines>1: 67 | self.savbuf = '' 68 | for i in range(lines-1): 69 | buf=self.ser.readline() 70 | if not buf: 71 | return result 72 | buf = convert_to_string(buf) 73 | if not buf == '' and not buf == 'OK': 74 | self.savbuf += buf+'\n' 75 | return result 76 | 77 | def send_sms(self,destno,msgtext): 78 | result = self.command('AT+CMGS="{}"\n'.format(destno),99,5000,msgtext+'\x1A') 79 | if result and result=='>' and self.savbuf: 80 | params = self.savbuf.split(':') 81 | if params[0]=='+CUSD' or params[0] == '+CMGS': 82 | return 'OK' 83 | return 'ERROR' 84 | 85 | def read_sms(self,id): 86 | result = self.command('AT+CMGR={}\n'.format(id),99) 87 | if result: 88 | params=result.split(',') 89 | if not params[0] == '': 90 | params2 = params[0].split(':') 91 | if params2[0]=='+CMGR': 92 | number = params[1].replace('"',' ').strip() 93 | date = params[3].replace('"',' ').strip() 94 | time = params[4].replace('"',' ').strip() 95 | return [number,date,time,self.savbuf] 96 | return None 97 | 98 | def delete_sms(self,id): 99 | self.command('AT+CMGD={}\n'.format(id),1) 100 | 101 | def check_incoming(self): 102 | if self.ser.in_waiting: 103 | buf=self.ser.readline() 104 | # print(buf) 105 | buf = convert_to_string(buf) 106 | params=buf.split(',') 107 | 108 | if params[0][0:5] == "+CMTI": 109 | self._msgid = int(params[1]) 110 | if self.msg_action: 111 | self.msg_action() 112 | 113 | elif params[0] == "NO CARRIER": 114 | self.no_carrier_action() 115 | 116 | elif params[0] == "RING" or params[0][0:5] == "+CLIP": 117 | #@todo handle 118 | pass 119 | 120 | def read_and_delete_all(self): 121 | try: 122 | return self.read_sms(1) 123 | finally: 124 | self.command('AT+CMGDA="DEL ALL"\n',1) 125 | --------------------------------------------------------------------------------