Setting up SMS Sending and Receiving using a USB GSM Dongle by ZTE

The MF190 GSM Modem by ZTE

The MF190 GSM Modem by ZTE

Send & Receive SMS for IoT Devices

There are various scenarios when you may have an IoT device in the field where you do not need a 4G LTE or 5G connectivity or where you have poor connectivity such as in forests or mountains. These scenarios include situations where your IoT device may only need to send data in occasional spurts, and may never need to receive any commands, or receive a very small set of pre-designed commands to control the device.

For such scenarios, where you do not want your IoT device to be connected to the internet all the time using a 4G or 5G network, use up too much battery power and be relatively secure from remote attacks over the internet, a simple GSM modem can solve your problem. For example, we have built a device in the past where a USB modem connected to a transport vehicle would send an SMS with the latitude and longitude of its location every 5 minutes. There were other scenarios where an IoT device we prototyped would be placed in a forest and would send the temperature and humidity levels over SMS every hour.

A perpetually connected device that uses a data plan can become prohibitively expensive if you want to just send short messages. This post describes how to write simple programs to use an off the shelf modem, that can be purchased for less than $10, and using a SIM card that provides Unlimited text messaging to send and receive SMS messages.

You may also want to build your own SMS over the internet service like that of Twilio from scratch and this script can help you start.

The ZTE MF190 GSM Modem

This GSM Modem is a 3-G modem as shown in the below image. Even though networks in the USA and other countries may be removing support for 3G networks, all of these devices support SMS sending and receiving, which is why they continue to be useful. The ZTE MF190 modem is supported on Linux out of the box, and appears as a serial port device when connected to a computer or Raspberry Pi or Beaglebone Black via their USB A ports or using a USB hub.

The modem can be purchased on Amazon or eBay and you will also need to buy a pay-as-you-go SIM card from the various providers in your area. For our use case we purchased LycaMobile for $20.

The GSM Modem cover can be opened to insert a microSD card and a full size SIM card. You can see the IMEI number on the device here.

The GSM Modem cover can be opened to insert a microSD card and a full size SIM card. You can see the IMEI number on the device here.

You need a 3-in-1 size SIM card for this project. The full size SIM is needed for the USB modem.

You need a 3-in-1 size SIM card for this project. The full size SIM is needed for the USB modem.

The USB modem now connected to the USB port of a laptop. The SIM card has been inserted and the LED on the right will be red on start and green when connected to the GSM network.

The USB modem now connected to the USB port of a laptop. The SIM card has been inserted and the LED on the right will be red on start and green when connected to the GSM network.

Disable Unnecessary Modem Properties

The USB GSM modem, MF190, comes with properties like appearing as a CD-ROM drive on Linux or as a mass storage device or both. In our case, on Kali Linux, this device appeared as 3 different devices: a CD-ROM device /dev/sr1, a mass storage device /dev/sdc and a USB modem /dev/ttyUSB2. These device names can be extracted from the dmesg output as below as soon as you plugin the USB device to your computer.

$ sudo dmesg | tail -n 1000
[1800781.986806] usb 1-2.4.2: new high-speed USB device number 7 using ehci-pci                                                                                                                      
[1800782.197336] usb 1-2.4.2: New USB device found, idVendor=19d2, idProduct=2000, bcdDevice= 0.00                                                                                                   
[1800782.197343] usb 1-2.4.2: New USB device strings: Mfr=3, Product=2, SerialNumber=4                                                                                                               
[1800782.197347] usb 1-2.4.2: Product: ZTE WCDMA Technologies MSM                                                                                                                                    
[1800782.197350] usb 1-2.4.2: Manufacturer: ZTE,Incorporated                                                                                                                                         
[1800782.197352] usb 1-2.4.2: SerialNumber: MF1900ZTED010000                                                                                                                                         
[1800782.199979] usb-storage 1-2.4.2:1.0: USB Mass Storage device detected                                                                                                                           
[1800782.201943] scsi host3: usb-storage 1-2.4.2:1.0                                                                                                                                                 
[1800783.221344] scsi 3:0:0:0: CD-ROM            ZTE      USB SCSI CD-ROM  2.31 PQ: 0 ANSI: 2                                                                                                        
[1800783.225970] sr 3:0:0:0: [sr1] scsi-1 drive                                                                                                                                                      
[1800783.243508] sr 3:0:0:0: Attached scsi CD-ROM sr1                                                                                                                                                
[1800783.243741] sr 3:0:0:0: Attached scsi generic sg4 type 5                                                                                                                                        
[1800784.752096] usb 1-2.4.2: USB disconnect, device number 7                                                                                                                                        
[1800785.078898] usb 1-2.4.2: new high-speed USB device number 8 using ehci-pci                                                                                                                      
[1800785.290082] usb 1-2.4.2: New USB device found, idVendor=19d2, idProduct=0117, bcdDevice= 0.00                                                                                                   
[1800785.290090] usb 1-2.4.2: New USB device strings: Mfr=3, Product=2, SerialNumber=4                                                                                                               
[1800785.290093] usb 1-2.4.2: Product: ZTE WCDMA Technologies MSM                                                                                                                                    
[1800785.290096] usb 1-2.4.2: Manufacturer: ZTE,Incorporated                                                                                                                                         
[1800785.290099] usb 1-2.4.2: SerialNumber: MF1900ZTED010000                                                                                                                                         
[1800785.293883] usb-storage 1-2.4.2:1.3: USB Mass Storage device detected                                                                                                                           
[1800785.294205] scsi host3: usb-storage 1-2.4.2:1.3                                                                                                                                                 
[1800785.974549] usbcore: registered new interface driver option                                                                                                                                     
[1800785.976340] usbserial: USB Serial support registered for GSM modem (1-port)                                                                                                                     
[1800785.977650] option 1-2.4.2:1.0: GSM modem (1-port) converter detected                                                                                                                           
[1800785.977975] usb 1-2.4.2: GSM modem (1-port) converter now attached to ttyUSB0                                                                                                                   
[1800785.978392] option 1-2.4.2:1.1: GSM modem (1-port) converter detected                                                                                                                           
[1800785.978596] usb 1-2.4.2: GSM modem (1-port) converter now attached to ttyUSB1                                                                                                                   
[1800785.979220] option 1-2.4.2:1.2: GSM modem (1-port) converter detected                                                                                                                           
[1800785.979462] usb 1-2.4.2: GSM modem (1-port) converter now attached to ttyUSB2                                                                                                                   
[1800786.324071] scsi 3:0:0:0: CD-ROM            ZTE      USB SCSI CD-ROM  2.31 PQ: 0 ANSI: 2                                                                                                        
[1800786.325395] scsi 3:0:0:1: Direct-Access     ZTE      MMC Storage      2.31 PQ: 0 ANSI: 2                                                                                                        
[1800786.331560] sr 3:0:0:0: [sr1] scsi-1 drive                                                                                                                                                      
[1800786.359076] sr 3:0:0:0: Attached scsi CD-ROM sr1                                                                                                                                                
[1800786.359210] sr 3:0:0:0: Attached scsi generic sg4 type 5                                                                                                                                        
[1800786.359645] sd 3:0:0:1: Attached scsi generic sg5 type 0                                                                                                                                        
[1800786.373392] sd 3:0:0:1: [sdc] 7716864 512-byte logical blocks: (3.95 GB/3.68 GiB)                                                                                                               
[1800786.375021] sd 3:0:0:1: [sdc] Write Protect is off                                                                                                                                              
[1800786.375026] sd 3:0:0:1: [sdc] Mode Sense: 0f 0e 00 00                                                                                                                                           
[1800786.375768] sd 3:0:0:1: [sdc] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA                                                                                             
[1800786.416178]  sdc: sdc1                                                                                                                                                                          
[1800786.435203] sd 3:0:0:1: [sdc] Attached SCSI removable disk

You can also use lsusb to check that the device has been loaded.

$ lsusb | grep -i zte
Bus 001 Device 008: ID 19d2:0117 ZTE WCDMA Technologies MSM MF667

The device seems to show MF667 but the labeling says MF190. Either way, they support the same commands for sending and receiving SMS.

For this post, we will disable both the CD-ROM and mass storage feature. It may so happen that you may want to take advantage of the mass storage feature to save messages or to use the USB GSM modem to receive files over GSM and that is out of the scope of this post.

  • First eject the CD-ROM from the host machine

    $ sudo eject /dev/sr1
  • Then connect to the USB modem using a serial program like minicom and run the following commands to disable the CD-ROM features. The first command makes the device online and the second command disables the CD-ROM auto-run feature.

$ minicom -b 115200 /dev/ttyUSB2
AT+ZOPRT=5 
AT+ZCDRUN=8

Now the device is ready for sending and receiving messages and phone calls. For this post, we will not be making any phone calls.

Install Perl Modules

Once the device has been setup and configured, let us try to use it using a Perl script. The GSM device acts like a modem with a serial port so the dependency that you need to install using the cpanm tool is primarily Device::Gsm which will install other dependencies like Device::Modem and Device::SerialPort. Of course, you could also use a Python script to connect to the modem, but we love writing Perl.

$ cpanm Device::Gsm Device::Modem Device::SerialPort

Sending Messages

Sending messages can be done very easily using the Device::Gsm module as shown below in the perl script. The $modem, $text and $recipient variables must be edited as per your needs. The phone number must have no spaces, must include the country code and the + sign in front of it.

Once you create a Device::Gsm object, then you connect to the modem over the serial port /dev/ttyUSB2 with a baud rate of 115200. Once that connection works, you can then send a message using the send_sms function which is obvious in the below code.

use strict;
 use warnings;
 use Device::Gsm;
 ## setup the USB GSM modem                                                                                                                                                                       
 my $modem = '/dev/ttyUSB2'; # or other like /dev/modem                                                                                                                                           
 my $gsm = Device::Gsm->new(port => $modem, log => 'file,/tmp/gsm.log',                                                                                                                           
                loglevel => 'info');                                                                                                                                                                 
 $gsm->connect(115200) or die "Failed to connect to $modem";                                                                                                                                      
 ## my message                                                                                                                                                                                    
 my $text = "This is a test message";                                                                                                                                                             
 ## my phone number                                                                                                                                                                               
 my $recipient = '+12012012010'; # the full phone number you want to use                                                                                                                          
 ## send the message                                                                                                                                                                              
 my $sent = $gsm->send_sms(                                                                                                                                                                       
        content => substr($text, 0, 140),                                                                                                                                                            
        recipient => $recipient,                                                                                                                                                                     
  );                                                                                                                                                                                               
 die "Unable to send message to $recipient ", scalar localtime unless $sent;                                                                                                                      
 print "Sent message to $recipient at ", scalar localtime, "\n";

Receiving Messages

Receiving messages is similarly done. You must connect to the modem at a baud rate of 115200 and then invoke the messages() function with the storage option defined as ME which stands for the phone memory, which is where the messages are stored by default on this ZTE device.

If you have messages stored in memory, the for loop will get invoked and each message which is an instance of Device::Gsm::Sms can then be handled or printed or processed or stored in a database etc. In the code below, we delete the message if the status is UNREAD, but that is where the processing by the user can be done.

use strict;
    use warnings;
    use Device::Gsm;
    use Device::Gsm::Sms;
    ## setup the USB GSM modem                                                                                                                                                                       
    my $modem = '/dev/ttyUSB2'; # or other like /dev/modem                                                                                                                                           
    my $gsm = Device::Gsm->new(port => $modem, log => 'file,/tmp/gsm.log',                                                                                                                           
               loglevel => 'info');                                                                                                                                                                 
    $gsm->connect(115200) or die "Failed to connect to $modem";                        
    ## get the messages from the local storage 'ME'                                                                                                                                                  
    my @messages = $gsm->messages('ME');                                                                                                                                                             
    printf "You have %d messages\n", scalar(@messages);                                                                                                                                              
    my $idx = 0;                                                                                                                                                                                     
    foreach my $msg (@messages) {                                                                                                                                                                    
        next unless defined $msg;                                                                                                                                                                    
        print '-' x 60, "\n", "MESSAGE N. $idx\n";                                                                                                                                                   
        print 'Type   ',($msg->type() eq Device::Gsm::Sms::SMS_SUBMIT ? 'SUBMIT' : 'DELIVER'), "\n";                                                                                                 
        print 'Status ', $msg->status(), "\n";                                                                                                                                                       
        print 'From   ', $msg->sender(), "\n";                                                                                                                                                       
        print 'To     ', $msg->recipient(), "\n";                                                                                                                                                    
        print 'Time   ', $msg->time(), "\n";                                                                                                                                                         
        print 'Text   [', $msg->text(), "]\n";                                                                                                                                                       
        ## if the message is unread, delete it now since you may have read it or                                                                                                                     
        if ($msg->status() =~ /UNREAD/) {                                                                                                                                                            
            ### DO SOME PROCESSING HERE                                                                                                                                                              
            print "DELETING SMS FROM STORAGE!!\n";                                                                                                                                                   
            $msg->delete();                                                                                                                                                                          
        }                                                                                                                                                                                            
        $idx++;                                                                                                                                                                                      
    }

The received messages will look like the below:

------------------------------------------------------------                                                                                                                                         
MESSAGE N. 0                                                                                                                                                                                         
Type   DELIVER                                                                                                                                                                                       
Status REC UNREAD                                                                                                                                                                                    
From   LYCAMOBILE                                                                                                                                                                                    
To                                                                                                                                                                                                   
Text   [Dear customer ,Your $19 INTERNATIONAL PLAN has been renewed succesfully.it is good through 09/16/2021. Visit https://www.lycamobile.us/login for details.]                                   
------------------------------------------------------------                                                                                                                                         
MESSAGE N. 1                                                                                                                                                                                         
Type   DELIVER                                                                                                                                                                                       
Status REC UNREAD                                                                                                                                                                                    
From   LYCAMOBILE                                                                                                                                                                                    
To                                                                                                                                                                                                   
Text   [ Thank you for using Lycamobile.]                                                                                                                                                            
------------------------------------------------------------                                        
------------------------------------------------------------                                                                                                                                         
MESSAGE N. 2                                                                                                                                                                                         
Type   DELIVER                                                                                                                                                                                       
Status REC UNREAD                                                                                                                                                                                    
From   +12012012010                                                                                                                                                                                  
To                                                                                                                                                                                                   
Text   [Test]                                                                                                                                                                                        
------------------------------------------------------------                                                                                                                                         
MESSAGE N. 3                                                                                                                                                                                         
Type   DELIVER                                                                                                                                                                                       
Status REC UNREAD                                                                                                                                                                                    
From   +12012012010                                                                                                                                                                                  
To                                                                                                                                                                                                   
Text   [Got it]                                                                                                                                                                                      
------------------------------------------------------------

Sending & Receiving Together

The below script lets you send a message to a recipient and receive replies by checking every 5 seconds. The most important thing to note is that you have to reconnect to the GSM modem if you have sent a message and then want to receive new messages. This forces the modem to exit the sending state and enter a receiving state.

The rest of the code is similar to the above code blocks in the earlier sections. This script was tested on Kali Linux and it works as of this writing.

#!/usr/bin/env perl                                                                                                                                                                                  
use strict;                                                                                                                                                                                          
use warnings;                                                                                                                                                                                        
use sigtrap qw/handler signal_handler normal-signals/;                                                                                                                                               

use Device::Gsm;                                                                                                                                                                                     
use Device::Gsm::Sms;                                                                                                                                                                                
use Data::Dumper;                                                                                                                                                                                    

sub usage {                                                                                                                                                                                          
    die << "..."                                                                                                                                                                                     
Usage: $0 <phone number like +12125432109> <message>                                                                                                                                                 
Press Ctrl-C to exit program.                                                                                                                                                                        
...                                                                                                                                                                                                  
}                                                                                                                                                                                                    

sub send_sms {                                                                                                                                                                                       
    my ($gsm, $text, $recipient) = @_;                                                                                                                                                               
    return unless length($text // '');                                                                                                                                                               
    return unless $recipient;                                                                                                                                                                        
    my $sent = $gsm->send_sms(                                                                                                                                                                       
        content => substr($text, 0, 140),                                                                                                                                                            
        recipient => $recipient,                                                                                                                                                                     
    );                                                                                                                                                                                               
    die "Unable to send message to $recipient ", scalar localtime unless $sent;                                                                                                                      
    print "Sent message to $recipient at ", scalar localtime, "\n";                                                                                                                                  
}                                                                                                                                                                                                    

sub receive_messages {                                                                                                                                                                               
    my $gsm = shift;                                                                                                                                                                                 
    my @messages = $gsm->messages('ME');                                                                                                                                                             
    printf "You have %d messages\n", scalar(@messages);                                                                                                                                              
    my $idx = 0;                                                                                                                                                                                     
    foreach my $msg (@messages) {                                                                                                                                                                    
        next unless defined $msg;                                                                                                                                                                    
        print '-' x 60, "\n", "MESSAGE N. $idx\n";                                                                                                                                                   
        print 'Type   ',($msg->type() eq Device::Gsm::Sms::SMS_SUBMIT ? 'SUBMIT' : 'DELIVER'), "\n";                                                                                                 
        print 'Status ', $msg->status(), "\n";                                                                                                                                                       
        print 'From   ', $msg->sender(), "\n";                                                                                                                                                       
        print 'To     ', $msg->recipient(), "\n";                                                                                                                                                    
        print 'Time   ', $msg->time(), "\n";                                                                                                                                                         
        print 'Text   [', $msg->text(), "]\n";                                                                                                                                                       
        if ($msg->status() =~ /UNREAD/) {                                                                                                                                                            
            print "DELETING SMS FROM STORAGE!!\n";                                                                                                                                                   
            $msg->delete();                                                                                                                                                                          
        }                                                                                                                                                                                            
        $idx++;                                                                                                                                                                                      
    }                                                                                                                                                                                                
}       
sub signal_handler {                                                                                                                                                                                 
    die "Signal caught: $!";                                                                                                                                                                         
}                                                                                                                                                                                                    

usage() unless scalar(@ARGV);                                                                                                                                                                        
usage() unless defined $ARGV[0];#TODO: verify phone number format                                                                                                                                    
usage() unless length($ARGV[1] // '');                                                                                                                                                               
warn "Message to be sent is longer than 140 chars and will be truncated" if length($ARGV[1]) > 140;                                                                                                  
my $recipient = shift(@ARGV);                                                                                                                                                                        
my $message = shift(@ARGV);                                                                                                                                                                          
print "Using phone number $recipient for recipient\n";                                                                                                                                               
my $modem = '/dev/modem';                                                                                                                                                                            
$modem = '/dev/ttyACM0' if -e '/dev/ttyACM0';                                                                                                                                                        
$modem = '/dev/ttyUSB2' if -e '/dev/ttyUSB2';                                                                                                                                                        
my %log = (log => 'file,/tmp/gsm.log', loglevel => 'debug');                                                                                                                                         
my $gsm = Device::Gsm->new(port => $modem, %log);                                                                                                                                                    
$gsm->connect(115200) or die "Failed to connect to $modem";                                                                                                                                          
send_sms($gsm, $message, $recipient);                                                                                                                                                                
while (1) {                                                                                                                                                                                          
    $gsm->connect(115200);                                                                                                                                                                           
    if ($gsm->is_active()) {                                                                                                                                                                         
        receive_messages($gsm);                                                                                                                                                                      
        sleep 5;                                                                                                                                                                                     
    } else {                                                                                                                                                                                         
        warn "GSM modem is not active";                                                                                                                                                              
        last;                                                                                                                                                                                        
    }                                                                                                                                                                                                
}                                                                                                                                                                                                    
__END__                                                                                                                                                                                              
###                                                                                                                                                                                                  
###                                                                                                                                                                                                  
### COPYRIGHT: 2013-2021 Selective Intellect LLC. All Rights Reserved.

In a future post, we will show how to make a phone call and play a pre-recorded audio file using this GSM modem.