Complete guide: Choosing an industrial controller
To send an SMS from a Raspberry Pi with a SIM7600E-H 4G HAT, connect the HAT by USB, open its AT port (usually /dev/ttyUSB2) at 115200 bit/s and send AT+CMGF=1 (text mode), AT+CMGS="+number", the text and Ctrl+Z. To read the received messages, send AT+CMGL="ALL" in text mode.
The test of this post used a Raspberry Pi 4, the board inside the TouchBerry Pi 10.1" panel PC, and a SIM7600E-H 4G HAT (LTE Cat-4, 4G, 3G and 2G, with GNSS) to send an SMS to a mobile phone and read the answer. At the end you have a Python script that sends an alarm by SMS.
GSM, GPRS, LTE and SMS in short
| Term | What it is |
|---|---|
| GSM | Global System for Mobile Communications, the 2G standard for voice and SMS |
| GPRS | Packet data over GSM, for slow internet access |
| LTE Cat-4 | 4G category of the SIM7600E-H: up to 150 Mbit/s down and 50 Mbit/s up, depending on the network |
| SMS | Short text message. The modem handles it with the AT commands of 3GPP TS 27.005, in text mode or PDU mode |
An SMS needs only network coverage and a SIM with SMS service, not a data plan. That is why it is still used for alarms in remote sites.
Connect the SIM7600E-H 4G HAT to the Raspberry Pi
Insert the SIM card in the HAT, connect the LTE antenna, and connect the USB port of the HAT to a USB port of the Raspberry Pi. Set the jumpers of the HAT as its user manual says for USB use.

Over USB the HAT creates several /dev/ttyUSB ports, and one of them takes AT commands. In our test it was /dev/ttyUSB2. The serial port option of raspi-config is only needed if you connect the HAT through the UART pins of the GPIO header instead of USB.
Open the AT port and check the modem
Install screen, list the ports and open the AT port:
sudo apt update
sudo apt install screen
ls /dev/ttyUSB*
screen /dev/ttyUSB2 115200
Type each command and press Enter. The modem answers OK or ERROR. To leave screen, press Ctrl+A and then K.
| Command | Expected answer | What it checks |
|---|---|---|
AT | OK | The port and the modem answer |
AT+CPIN? | +CPIN: READY | The SIM is present and has no PIN pending |
AT+CSQ | +CSQ: 20,99 | Signal level, from 0 to 31; 99 is unknown |
AT+CREG? | +CREG: 0,1 or 0,5 | Registered on the home network (1) or roaming (5) |
AT+CFUN=1 | OK | Full functionality of the modem |
Send an SMS with AT commands
Set text mode, give the number in international format and write the text after the > prompt. End the text with Ctrl+Z, not with Enter. The modem answers with +CMGS: and a reference number when the network accepts the message.
AT+CMGF=1
OK
AT+CMGS="+34600000000"
> Type your text here
+CMGS: 12
OK
Read the received SMS
AT+CMGL lists the messages stored in the SIM or in the modem. In text mode it takes a filter: "REC UNREAD", "REC READ", "STO UNSENT", "STO SENT" or "ALL".
AT+CMGF=1
OK
AT+CMGL="ALL"
+CMGL: 0,"REC READ","+346XXXXXXX6","","21/05/21,12:32:09+08"
HIGH Temperature
OK
Each message gives its index, its status, the sender, and the date and time. The last part of the time, +08, is the time zone in quarters of an hour: +08 is UTC+2.
AT+CMGR=0reads only the message with index 0.AT+CMGD=0deletes it. Delete the messages that you have processed, because the SIM memory is small.AT+CNMI=2,1makes the modem announce each new message with+CMTI: "SM",<index>, so a program does not need to poll.
Send an SMS alarm from a Python script
In an automation, a program sends the SMS when an alarm starts. This script uses pyserial, checks the SIM and the network, and sends one message:
import time
import serial # pyserial
PORT = "/dev/ttyUSB2" # AT port of the SIM7600 over USB, check with: ls /dev/ttyUSB*
NUMBER = "+34600000000" # destination in international format
TEXT = "PLC alarm: high temperature in line 2"
def read_until(ser, tokens, timeout):
"""Read the modem answer until one of the tokens arrives or the time runs out."""
end = time.monotonic() + timeout
answer = b""
while time.monotonic() < end:
answer += ser.read(ser.in_waiting or 1)
if any(token in answer for token in tokens):
break
return answer.decode(errors="replace").strip()
def command(ser, cmd, wait_for=b"OK", timeout=5):
ser.write(cmd.encode() + b"\r")
return read_until(ser, (wait_for, b"ERROR"), timeout)
with serial.Serial(PORT, 115200, timeout=0.2) as ser:
print(command(ser, "AT"))
print(command(ser, "AT+CPIN?")) # +CPIN: READY means the SIM is unlocked
print(command(ser, "AT+CREG?")) # +CREG: 0,1 (home) or 0,5 (roaming) means registered
print(command(ser, "AT+CMGF=1")) # SMS text mode
print(command(ser, f'AT+CMGS="{NUMBER}"', wait_for=b">"))
ser.write(TEXT.encode() + b"\x1a") # Ctrl+Z (0x1A) ends the text and sends it
print(read_until(ser, (b"OK", b"ERROR"), 60)) # +CMGS: <reference> and OK
sudo apt install python3-serial
python3 send_sms.py
Output against our test modem:
OK
+CPIN: READY
OK
+CREG: 0,1
OK
OK
>
+CMGS: 12
OK
What we tested. The script ran with Python 3.13 and pyserial 3.5 on Linux, against a simulated modem on a virtual serial link that answers the commands of this post as in 3GPP TS 27.005 text mode. The simulated modem received the commands and the text in the right order. We did not send a real SMS for this update: on a real modem, the command echo also appears in the output.
If the SMS does not go out
| What you see | Usual cause | What to check |
|---|---|---|
No /dev/ttyUSB port | The HAT is not detected by USB | USB cable, jumpers, power of the HAT, lsusb |
No answer to AT | Wrong port or wrong speed | Try the other ttyUSB ports at 115200 |
+CPIN: SIM PIN | The SIM asks for its PIN | AT+CPIN="1234" with your PIN, or remove the PIN with a phone |
+CREG: 0,2 or 0,3 | Searching or registration denied | Antenna, coverage, and that the SIM is active |
+CMS ERROR after Ctrl+Z | The network refused the message | Number format, SMS service of the SIM, service center with AT+CSCA? |
SMS and 4G in an industrial project
SMS is a good channel for a few alarms when there is no internet. For data, the usual way is MQTT or HTTPS over the 4G data connection. Industrial Shields controllers have cellular options of their own: the Raspberry PLC takes LTE Cat 4 or NB-IoT/LTE-M expansion boards, the TouchBerry Pi takes a 4G LTE expansion, and the ESP32 PLC can also connect over 4G. The SMS commands of 3GPP TS 27.005 are standard, so the sequence of this post is the same. The port name and the setup change with each module: check its user guide.
Alarms that reach the phone. In the hospital room alarm case study, a PLC turns on the local alarms and sends the call to the nurses on Telegram. In the railway carriage case study, an ESP32 PLC 57R sends its data over 4G LTE-M.
Read the hospital alarm case studyRead the railway case study
FAQ about SMS with a Raspberry Pi
Do I need a data plan to send SMS?
No. You need a SIM with SMS service and network coverage. A data plan is only needed for internet access.
Which ttyUSB port is the AT port?
In our test, /dev/ttyUSB2. If it does not answer, try the other ttyUSB ports with AT.
Why does the message not send when I press Enter?
Because in text mode the modem waits for Ctrl+Z (0x1A) to end the text. Enter only adds a new line to the message. Esc cancels it.
Can a received SMS start an action in the PLC?
Yes. A program reads the new messages with AT+CMGL or +CMTI, checks the sender and the text, and then acts. Accept commands only from known numbers.
Text mode or PDU mode?
Text mode is easier to read and to test. PDU mode is needed for special cases, such as long messages in several parts or alphabets outside the GSM alphabet.
More about cellular communication
- Node-RED tutorial: how to send AT commands with a Raspberry PLC
- How to work with the Raspberry Pi GPRS module
- How to use 4G with the ESP32 PLC
- Choosing an industrial controller
See the Raspberry PLC family and the TouchBerry Pi 10.1" panel PC.