Sometimes one needs to send a few SMS. Doesn't matter if it's because you have a homelab and want to send system alerts, or you built your own alarm system and want notifications, or whether you are a programmer that wants to validate phone numbers. SMS is still the ultimate push notification system.
But for most of the scenarios mentioned above you might not rely on external sms gateways or APIs because they should also work if your internet goes out. Especially if you're building an alarm system because it should also be able to notify you when the power goes out.
The obvious solution is of course
Host your own SMS gateway!
But how does one build such a system with minimal investments? Let's find out!
Install gammu-smsd, get the API code from github and you'll be able to send and receive SMS via API.
Table of contents
- Step 1: Preparing the Pi
- Step 2: Setting up Gammu
- Step 3: Making an API for sending and receiving SMS
- FAQ
What we need
1. A Raspberry Pi
It doesn't matter which one you can even use the original from 2007.
As for the operating system: you can use Raspian or Alpine Linux (or actually any other system for that matter we just need a few easily available packages). I'll be using Alpine because it runs from a ramdisk so it won't corrupt the SD card on power loss but I'll explain all steps also for the raspberry pi OS
2. A USB 3g/4g Dongle
I'll be using the Huawei E303. Not all USB dongles will work but most Huawei from the series are pretty cheap and easily available. If you already have a 3g dongle you can Google the model combined with "raspberry pi" to be sure.
Obviously you'll also need a SIM card with some kind of plan capable of sending SMS messages.
Step 1: Preparing the Pi
I'll assume that you already have the pi running and can run commands on it whether via ssh or the desktop interface.
We'll need the following packages:
On Alpine: apk add gammu gammu-smsd php php-json usb-modeswitch usbutils git
On Raspian: apt install gammu gammu-smsd php php-json usb-modeswitch git
Most of the USB dongles are by default in storage mode and we need to switch it into modem mode. To check if this is true for your dongle plug it into a desktop computer and if the system mounts the dongle as usb storage (usually with an installer for the software and drivers) you know you'll need to modeswitch it.
Modeswitch is a bit different for every model (again: Google might help with yours) but for me this command works like charm:
usb_modeswitch -W -v 12d1 -p 14fe -K -P 14ac -M "55534243000000000000000000000011060000000000000000000000000000"
lsusb
returns Bus 001 Device 005: ID 12d1:1c05 HUAWEI HUAWEI Mobile
. 12d1 is the vendor code for Huawei and the string after that (1c05 in my case) is the product ID. We're telling modeswitch
Check out the arch wiki or the Ubuntuusers wikis. They should get you through modeswitching yours.
You know it worked when you your system created /dev/ttyUSB0
pi:~# ls -al /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0 Dec 3 12:10 /dev/ttyUSB0
That's it! The pi is prepared for the heart of the project.
Step 2: Setting up Gammu
Wait, what is Gammu? In the words of the developers
Gammu command line utility provides access to wide range of phone features.
- Gammu devs https://wammu.eu/gammu/
They have already done all the heavy lifting with parsing messages and sending it out to the carrier. We just need to tell gammu how it can talk to the dongle we set up in step 1 and it will handle the rest.
To see if your dongle is recognized run gammu idenitfy
. The output should look something like this
vpnpi:~# gammu identify
Device : /dev/ttyUSB0
Manufacturer : Huawei
Model : E303 (E303)
Firmware : 21.157.01.00.199
IMEI : 860000000000619
SIM IMSI : 230000000000006
Great, now let's create a config file with the following content and store it in /etc/gammurc
[gammu]
device = /dev/ttyUSB0
name = Bob
connection = at
logfile = /var/log/gammu.log
[smsd]
service = files
logfile = syslog
#PIN = 1234
# Increase for debugging information
debuglevel = 0
# Paths where messages are stored
inboxpath = /var/spool/gammu/inbox/
outboxpath = /var/spool/gammu/outbox/
sentsmspath = /var/spool/gammu/sent/
errorsmspath = /var/spool/gammu/error/
The name can be freely chosen, I chose bob because that's the name of my carrier.
If your SIM requires a pin you can set it under the [smsd] part.
Quick SMS test
To test if everything so far worked and you are able to send SMS just enter echo "some message" | gammu --sendsms TEXT 0664xxxxxxx
Obviously you'll have to replace the number with your phone number.
vpnpi:~# echo "Hello from your Pi" | gammu --sendsms TEXT 0664xxxxxxx
If you want break, press Ctrl+C…
Sending SMS 1/1…waiting for network answer..OK, message reference=22
It worked! I got the message on my phone.
You could stop here and automate things over the command line but I wanted to send and receive messages via an API that can be called from my whole home network so I'll add two more steps
Step 3: Making an API for sending and receiving SMS
We have been using the gammu command to send SMS messages but receiving them is not as straight forward and hard to automate. At least it would be, had the creators of gammu not also written gammu-smsd
gammu-smsd
Again the developers explain it best
Gammu SMS Daemon is a program that periodically scans GSM modem for received messages, stores them in defined storage and also sends messages enqueued in this storage. It is perfect tool for managing big amounts of received or sent messages and automatically process them.
- Gammu developers https://wammu.eu/smsd/
So it's basically a daemon that waits for new incoming or outgoing messages.
Since our gammu config from before already includes everything for gammu-smsd all that's left is to create the folders where gammu is going store the data.
mkdir -p /var/spool/gammu/inbox/
mkdir -p /var/spool/gammu/outbox/
mkdir -p /var/spool/gammu/sent/
mkdir -p /var/spool/gammu/error/
Sending Messages
Now we just need to start gammu-smsd in daemon mode (meaning it's going to run in the background) and point it to the config file we created earlier
gammu-smsd -d -c /etc/gammurc
Since we're now have the gammu-smsd daemon talking to the dongle we can no longer use the gammu command from before to send SMS messages. We still can send them though using the command gammu-smsd-inject
which is designed to work with the smsd daemon and just injects the messages into a local queue where it's then sent by the daemon.
The full example to send SMS messages now would be
gammu-smsd-inject TEXT 0664xxxxxxx -unicode -text "hello world from the daemon!"
And again we recieve it. This time it might take a few seconds longer because the daemon seems to wait a specific time before checking the queue for outgoing messages.
pi:~# gammu-smsd-inject TEXT 0664xxxxxxx -unicode -text "hello world from the daemon!"
gammu-smsd-inject[2964]: Warning: No PIN code in /etc/gammu-smsdrc file
gammu-smsd-inject[2964]: Created outbox message OUTC20211203_193330_00_0664xxxxxxx_sms0.smsbackup
Written message with ID /var/spool/gammu/outbox/OUTC20211203_193330_00_0664xxxxxxx_sms0.smsbackup
Receiving messages
Okay now it's time to send some message back and see if it registers on the device. Just answer on one of the messages you got before and if everything worked, it should appear in /var/spool/gammu/inbox/ as a file.
Wait a few seconds and then check the folder for contents
pi:~# ls /var/spool/gammu/inbox/
IN20211203_194458_00_+43664xxxxxxx_00.txt
pi:~# cat /var/spool/gammu/inbox/IN20211203_194458_00_+43664xxxxxxx_00.txt
Hello also from the outside world!
So each message is contained in its own file. You can guess that the file name IN20211203_194458_00_+43664xxxxxxx_00.txt
includes the date, time, phone number of the sender and a part number (for sms longer than 140 characters as they will be split up).
The API
We want a simple way to send and receive sms via API without installing hundreds of plugins and packages. I've written a two very slick php scripts that will do just that.
Get it at https://github.com/geek-at/gammu-php
Make sure gammu-smsd is already running as the script won't work without it.
Then from the directory where the two php files (send.php
and get.php
) are stored, run php -S 0.0.0.0:8080
which will serve the two files to anyone on the network.
Sending SMS with the API
Is really straight forward. Just call http://ip.of.your.pi/send.php?phone=0664xxxxxxx&text=Testmessage
Which will return a JSON object indicating if it failed (status:error), or succeeded (status:ok)
{
"status": "ok",
"log": "2021-12-04 15:43:39\ngammu-smsd-inject TEXT 0664xxxxxxx -unicode -text 'Testmessage'\ngammu-smsd-inject[2669]: Warning: No PIN code in /etc/gammu-smsdrc file\ngammu-smsd-inject[2669]: Created outbox message OUTC20211204_164340_00_0664xxxxxxx_sms0.smsbackup\nWritten message with ID /var/spool/gammu/outbox/OUTC20211204_164340_00_0664xxxxxxx_sms0.smsbackup\n\n\n"
}
Receiving SMS with the API
Is also very simple. Just call http://ip.of.your.pi/get.php
And it will return you all messages also in a JSON object
curl -s http://ip.of.your.pi/get.php | jq .
[
{
"id": "f0a7789a657bb34eddd17c8e64609c48",
"timestamp": 1638636342,
"year": "2021",
"month": "12",
"day": "04",
"time": "16:45",
"test": "04.12.2021 16:45:42",
"sender": "+43664xxxxxxx",
"message": "Hello bob!"
},
{
"id": "c358d0a4ca868c1d7d2eedab181eddd6",
"timestamp": 1638636414,
"year": "2021",
"month": "12",
"day": "04",
"time": "16:46",
"test": "04.12.2021 16:46:54",
"sender": "+43664xxxxxxx",
"message": "Hello "
}
]
Great now you can integrate sending and receiving SMS messages into your projects. Let me know if you want me to feature something you have built on top of this project.
FAQ
Q: Why the PHP scripts and not gammu-python?
A: I wanted the smallest possible footprint. I tried a few python implementations and none were as straight forward and easy to use without compiling hundreds of sub-packages. Also my pi was running out of ram compiling some of then and it was just faster to write a small PHP script than optimize the python packages.
Q: What if a user sends a long SMS message?
A: The API can handle parted messages even if there is another sms in the queue between it. So it should work out of the box as the API stitches it back together.
Q: What about MMS?
A: MMS or Multimedia Messages are supported by gammu but they are stored in a binary format I have yet to find documentation on how to make something usable out of it for download. Any info in it is greatly appreciated.
Comment using SSH! Info