Introduction: Giving the Raspberry Pi a Serial Modem Using the HUAWEI E3531 USB Dongle (+ Send SMS)

About: Hi, I like making things for raspberry pi, and Arduino. Especially to do with machine vision and animals! But also for signals such as EEG and EMG. I'm a Neuroscience graduate. I'm interested in solar system …

Hi, here we will give the Raspberry Pi 3 a serial modem using the Huawei E3531 USB dongle. You can now send AT commands to the modem, and easily send text messages. We'll do this in Python code.

Step 1: Getting You SIM Ready, and Using the Default HiLink CDC-Ethernet Mode for the E3531

So mostly when you buy a SIM the carriers (this is for the UK) want you to activate it using a voice call! So it's easiest to do that with an unlocked phone. Then you'll know for sure that the SIM is working. Ok, so once that is done you can insert the SIM into the E3531 and plug it into a USB port on the Raspberry Pi 3!

Once you've plugged in the E3531 you should run the following command:


This will return a list of USB devices that are connected, along with their modes. You can see what I got back in the image above!

I got back for the USB device listed as Huawei Technology Co., the following mode:


Now the 12d1 being the vendor ID, and 14dc being the HiLink CDC-Ethernet model/mode ID. So this is supposed to be the default mode for the E3531. However, because these dongles do contain data, they may return as a mass storage device instead! That's common for the E303. But I never had that with the E3531.

Anyway, this HiLink CDC-Ethernet mode means the dongle is all ready to connect to the internet over 2G/3G and you can use PPP now if you want. It's probably easiest to make sure the dongle is working by going ahead with this first!

So you should open a browser, and access . Then you'll get the home html file giving your status. See the image above. So that should tell us that our SIM is working fine! And we are ready to switch our mode to serial modem now!

Step 2: Now Let's Switch to Serial Modem Mode!

Next, let's switch it to a serial modem so we can send AT commands and thus SMS. We'll be wanting to access it as tty/USBx now.

The first thing we need to take into account is the mode we want to switch to. We want to switch our E3531 mode from 12d1:14dc (which it is in now) to modem mode of 12d1:1001 . This should give us three virtual serial ports at tty/USB0 , tty/USB1 , and tty/USB3 !

Ok, so we need to use usb-modeswitch to do this. If you look around on the net, there are several approaches to using usb-modeswitch to do this. This is the one I found most reliable!

So download and install usb-modeswitch with the following command:

sudo apt-get install usb-modeswitch

Now let's make a file containing our switch mode instructions. The instructions are:


So, we create a file at the following location, using the following command. And add those three lines of code to it. Command is:

sudo nano /etc/usb_modeswitch.d/12d1:1f01

You can see this file in the image at the top! So after saving the file, you should reboot your Raspberry Pi.


The message content


is kinda tricky. It took me a while to get this right, and the Huawei website wasn't much help. Actually it didn't help at all. The message content depends on the firmware of your E3531, so honesty, the above might not work and you'll have to experiment/research to find a message that does in that case! Just by searching forums. Or else I can try to be of assistance if you contact me!

Step 3: Checking If We Now Have a Serial Modem!

So, after the reboot you should run the following command again:


And hopefully *fingers crossed* you will see the new ID as 12d1:1001 ; so the E3531 has switched to serial modem mode! If even describes it as a modem now! Although with strange model numbers! See above image.

Next, issue the following command:

ls /dev/tty*

which should list all the tty bindings. Hopefully we'll see our new virtual serial ports: ttyUSB0, ttyUSB1 and ttyUSB2! Great!

Step 4: Connect to Our New Virtual Serial Ports and Issue Some AT Commands!

Now we can go ahead and connect to the virtual serial ports and issue some AT commands! I used cu "call up another system" to do this.

So you can install cu with the following command:

sudo apt-get install cu

So after you've installed it, execute it with following arguments to connect to ttyUSB0 (if that doesn't work, try ttyUSB1 or ttyUSB2):

cu -l /dev/ttyUSB0

So you can see what we get back from cu in the above image. It doesn't echo, so it's a bit tricky! The first was 'AT' and I got back OK. Then I issued 'AT+CMGF=1' which tells the modem to act in SMS mode, and got back CMS ERROR 302, which is operation not allowed. Then I tried to send an SMS with 'AT+CMGS="+44phonenumberhere" ' which returned an error code of CMS ERROR 302 again! It all worked fine the next time, so I must have entered them wrongly! It's tricky when you don't get feedback for what you are typing :-(

Anyway, I should note at this point, that you may enter correct AT commands and get those errors due to the SIM card requiring a PIN or PIN2. If the SIM is blocked, the error would be due to SIM required PUK or PUK2 (personal unblocking codes).

You can send a PIN using AT+CPIN="0000" . Note that carriers will block the SIM after 3x incorrect PIN attempts, and you will need to use a PUK or PUK2 to unblock it. All the carriers have default PIN codes.

You can find a full list of error codes here:

Great! So it isn't much fun using cu to send AT commands is it! Next, let's do it with Python!

Step 5: Using the Python Code to Deliver AT Commands to Serial Modem in Order to Send SMS!

Now, let's do away with cu, since it is not much fun. And use Python code to send our AT commands, and thus send an SMS. Honestly, it is quite exciting to send your first SMS using the serial modem!!

So here's the code to do it:

I've got this in full on GitHub at

import serial
from curses import ascii # since we need ascii code from CTRL-Z import time
# here we are testing sending an SMS via virtual serial port ttyUSB0 that was created by a USB serial modem
import serial
from curses import ascii # since we need ascii code from CTRL-Z import time
# here we are testing sending an SMS via virtual serial port ttyUSB0 that was created by a USB serial modem
phonenumber = #enter phone number to send SMS to e.g. "+441234123123"
SMS = "here's your SMS!"
ser = serial.Serial('/dev/ttyUSB0', 460800, timeout=1)
# 460800 is baud rate, ttyUSB0 is virtual serial port we are sending to
# send AT to the ttyUSB0 virtual serial port
line = ser.readline()
# what did we get back from AT command? Should be OK
# send AT+CMGF=1 so setting up for SMS followed by CR 
line = ser.readline()
# what did we get back from that AT command?
ser.write('AT+CMGS="%s"\r\n' %phonenumber)
# send AT+CMGS then CR, then phonenumber variable
# send the SMS variable after we sent the CR
# send a CTRL-Z after the SMS variable using ascii library
# wait 10 seconds
print ser.readline()
print ser.readline()
print ser.readline()
print ser.readline()
# what did we get back after we tried AT_CMGS=phonenumber followed
# by  , then SMS variable, then  ascii code??

Step 6: All Done! Here's the SMS!

So, after running that, we are able to send an SMS! The phone numbers have to be entered in + then country code, then number format e.g +441234123123