Send and Receive SMS Messages Using Raspberry Pi and Python

Introduction

As I described already in another article – Running the Raspberry Pi on Solar Power it is pretty easy to run the Raspberry Pi on solar energy. This fact combined with the small form factor of the Pi makes it extremely portable platform. In one of my projects I will leave the Raspberry Pi working in a distant rural area with no wired or wifi connection. The only possibility to connect to it is through the cell phone network.

So I came up with the idea to control the Pi with short messages or SMSs.

Get a Raspberry Pi and Accessories

You can obtain many Raspberry Pi accessories including 3g modem from banggood.com or aliexpress.com

Connecting the 3g Modem

In order to connect the Pi with the cell network we would first need a 3g modem. Fortunately nowadays they are very cheap and easy to get hold of. The carriers even give them for free. So I had the ZTE MF190 from a Bulgarian carrier and attached it to the Raspberry Pi. Then came the first challenge – 3g modems usually have two modes of operation – a usb storage device and a 3g modem with the storage mode being the default one. This is very convenient for Windows users because when you plug the modem it opens as a usb stick where all the necessary drivers are, you install them and then it is automatically switched to a modem mode. Of course it is not that easy under Linux. What you have to do is to switch it manually to a modem mode and then use the likes of wvdial or pppd to establish a connection. The good news is that there are plenty of articles how to do it and it is not that hard, so I will not talk about it here :). A good reference are these pages:

Sending SMS

Once you have the 3g dongle connected as a modem three new devices will appear under /dev. In my case these are /dev/ttyUSB0, ttyUSB1 and ttyUSB2, with additional symbolic link /dev/gsmmodem pointing to ttyUSB2. What this means is that while you establish connection using the /dev/gsmmodem the others are free and can be opened and used to send AT commands to the modem.

But what are AT commands? The AT (meaning ‘attention’) commands are simple instructions send to the modem over a serial link telling it what to do – dial a number, hang up, send message, etc. For more information: Wikipedia. In reality it is not so straightforward to get a list of those commands since for 3g dongles there is no official documentation (at least for those I have). Fortunately there are internal leaked documents on the internet so I was able to find the ZTE command set by with Google.

So let’s try to send some commands. For testing purposes I use the cu program –  cu – Call up another system. To open the modem type the following: “cu -l /dev/ttyUSB1”. After that you should see an empty screen. Now what you type is directly send to the modem. Type AT and hit ENTER. You should see OK in response:

pi@raspberrypi ~ $ cu -l /dev/ttyUSB1
Connected.
AT
OK

Now we are ready to send a short message. There are two ways to do it – either in text mode either in PDU mode. For now we will stick to text mode because it is easier to work with. So let’s go in it:

AT+CMGF=1
OK

Now to send a SMS we should send the following command, notice that at then end of the message we hit ctrl + z:

AT+CMGS=”<phone number>”r
> here we type the message text<ctrl+z>

+CMGS: 152
OK

The phone number can be the full one with the country code or the short one starting with zero. Also r means the ENTER key. If everything is going fine you will see an “OK” in response.
How do we do it in python? Let’s see an example:

#!/usr/bin/python
import serial
<span class="pl-k">from</span> curses <span class="pl-k">import</span> <span class="pl-c1">ascii
</span>import time
 
modem <span class="pl-k">=</span> serial.Serial(<span class="pl-s"><span class="pl-pds">'</span>/dev/ttyUSB1<span class="pl-pds">'</span></span>, <span class="pl-c1">460800</span>, <span class="pl-smi">timeout</span><span class="pl-k">=</span><span class="pl-c1">1</span>)
 
modem.write("AT+CMGF=1")
print modem.readline()
print modem.readline()
 
modem.write('AT+CMGS="%s"r' % "012345679")
modem.write("message text")
modem.write(<span class="pl-c1">ascii</span>.ctrl(<span class="pl-s"><span class="pl-pds">'</span>z<span class="pl-pds">'</span></span>))
time.sleep(2)
 
print modem.readlines()

If everything is alright you will see several lines in response with an OK at the end.

Receiving SMS

Now when we are able to send messages let’s see how to read incoming ones. Looking at the AT command list for the ZTE MF190 we see the +CMGL – List Messages. In text mode the command syntax is the following:

AT+CMGL=”<stat>r”

Where stat is any of the following: “REC UNREAD”, “REC READ”, “STO UNSENT”, “STO SENT”, “ALL” – unread, read, unsent, send and all messages. Example:

AT+CMGL=”ALL”r

+CMGL: 12,”REC UNREAD”,”+359882505000″,,”15/06/14,23:27:17+12″
CMD:connect
+CMGL: 13,”REC UNREAD”,”+359882505000″,,”15/06/15,11:15:15+12″
CMD:status
+CMGL: 14,”REC UNREAD”,”+359882505000″,,”15/06/15,11:15:57+12″
CMD:status

OK

Using the +CMGL command its easy to get incoming SMS messages but there is one drawback – we have to query the modem every once in a while in order to get updates. It would be much better if the modem notifies us (pushes) directly once there is a message. Again looking at the command specification we can see: +CMTI which stands for “New Message Indications”. If there is an incoming SMS the modem pushes a line with the following form:

<CR><LF>+CMTI: <mem>,<index><CR><LF>

where <mem> can be “ME” – message storage, “SM” – SIM storage or “SR” status report storage and then the <index> is the message index in that storage. Having this information we can send +CMGR “Read Message” command. it has the following form:

+CMGR=<index>r

When executed it sends back the message content together with some header information:

AT+CMGR=14
+CMGR: “REC READ”,”+359882505000″,,”15/06/15,11:15:57+12″
CMD:status

OK

If we receive too many messages the memory eventually will fill up, so how do we free space? Easy – with the +CMGD command:

+CMGD=<index>[,<delf lag>]

By using the index we can delete the message we want and by using the <delflag> we can delete all read messages for example.

AT+CMGD=1,2
OK

Here I used the delflag = 2 which means: “Delete all read messages from preferred message storage and sent mobile originated messages, leaving unread messages and unsent mobile originated messages untouched”.

For more information regarding the ZTE at commands look for the fucking manual on the internet :).

Putting It All Together

Now that we can send and receive messages let’s make a server program in python which will receive commands via SMS, decode them and then execute actions on the Pi. For now I have defined actions such as “connect” – to establish internet connection on the Pi, “disconnect”, “reboot” – to reboot the Raspberry Pi, “status” – which returns connection status, free space on the memory card and in the future will return local temperature. This list can easily be extended. It would probably have been much easier to use a simple web server to control the Pi but unfortunately you don’t get a real ip address on the 3g network. So we should stick to SMS.

To see the full program, checkout my github repository – smscenter.

Conclusion

It is easy to turn the Raspberry Pi into a remotely managed “probe” even without internet connection. Possible applications are garden automation, weather stations, surveillance camera etc.

Links

21 thoughts on “Send and Receive SMS Messages Using Raspberry Pi and Python

  1. My sim900A gsm module is not responding to AT commands.It should print OK but not printing anything.

    I have been facing this problem since long time.

    Please help me ASAP.

    Thanks.

    1. you should first see that it is connected then you have to put the AT and press enter and then it will respond

  2. Can we use a number which is stored in a variable in AT+CMGS command ?
    Please help.

  3. Hi, Can we connect many USB 3G with SIM cards into the raspberry pi? We can use a hub usb to divide an USB port to 4 sub USB and plug in 4 USB 3G with SIM cards. Can we use in this way? Did you try? Tks

  4. Please, I have bought and installed an ZTE MF190, as yours.

    lsusb gives me:

    Bus 001 Device 005: ID 19d2:0117 ZTE WCDMA Technologies MSM
    Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
    Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

    pi@raspberrypi ~ $ cu -l /dev/ttyUSB1
    Connected.
    AT
    OK

    exactly as you describe.

    But,

    AT+CMGF=1
    ERROR

    The red light is on the dongle.

    Am I missing a step?

  5. Hi, can you correct the sending sms code example? There are extra HTML formatting characters and its a bit confusing. If you open this blog post there you can see the sample code as follows:

    #!/usr/bin/python
    import serial
    from curses import ascii
    import time

    modem = serial.Serial(‘/dev/ttyUSB1’, 460800, timeout=1)

    modem.write(“AT+CMGF=1”)
    print modem.readline()
    print modem.readline()

    modem.write(‘AT+CMGS=”%s”r’ % “012345679”)
    modem.write(“message text”)
    modem.write(ascii.ctrl(‘z’))
    time.sleep(2)

    print modem.readlines()

    Thanks in advance.

    BR,

    //Cs

  6. Aahhhhh, posted the comment and transformed to the correct one 🙂 But the sample code window is still not fine…should be the same I pasted above.

  7. Hi All, I just finisced to configure a project on a RPI3 using the infirmations contained here. Few notes. to make the python send message I had to modify the formatting on the line modem.write(‘AT+CMGS=”+393478692663″\r’).
    I had also issues on running the python file, this mainly was due to the permissions, solved by using the chmod +x statement and formatting in the gammu-smsd as: modem.write(‘AT+CMGS=”+393478692663″\r’)

  8. I using smslib

    and error

    run:
    SMSLib: A Java API library for sending and receiving SMS via a GSM modem or other supported gateways.
    This software is distributed under the terms of the Apache v2.0 License.
    Web Site: http://smslib.org
    Version: 3.5.1
    log4j:WARN No appenders could be found for logger (smslib).
    log4j:WARN Please initialize the log4j system properly.
    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
    Exception in thread “Thread-3” java.lang.ExceptionInInitializerError
    at org.smslib.modem.SerialModemDriver.connectPort(SerialModemDriver.java:69)
    at org.smslib.modem.AModemDriver.connect(AModemDriver.java:114)
    at org.smslib.modem.ModemGateway.startGateway(ModemGateway.java:189)
    at org.smslib.Service$1Starter.run(Service.java:276)
    Caused by: java.lang.RuntimeException: CommPortIdentifier class not found
    at org.smslib.helper.CommPortIdentifier.(CommPortIdentifier.java:76)
    … 4 more

  9. This is all very informative. But I would have liked to see it more developed.Now all AT-command have to be typed in, it would be nicer if I would have clicked somewhere and that the program produces the correct AT-codes. With the AT-commands much more is possible, such as the signal strength and error messages.

  10. Hello gyz your code help me alot all of the communication is done between gsm800l and rpi
    now i want to receive an sms and do conditional work on that need help
    also thanks in advance

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.