Reputation: 339
I'm using sms1.cardboardfish.com to sens smses through the web. I have these datacoding schemes to work with: 0: Flash 1: Normal 2: Binary 4: UCS2 5: Flash UCS2 6: Flash GSM 7: Normal GSM and I want to send it in hebrew. right now I'm sending it in 7: Normal GSM and it comes out scrambled.. Ideas anyone?
Upvotes: 1
Views: 1303
Reputation: 3147
i have created a python script as a modem driver to take care of all my needs (send sms in ascii or utf8 (including hebrew), place a call ..),
Prerequisite:
from cmd run the command:
pip install pyserial
save the following code in a file gsm_modem.py
import serial.tools.list_ports
import time
import os
from utils import log
import threading
# read more here:
# https://www.smssolutions.net/tutorials/gsm/sendsmsat/
# https://www.smssolutions.net/tutorials/gsm/receivesmsat/
# constants to represent the GSM modem encoding modes (GSM, USC2)
GSM_MODE = 1 # GSM mode
UNICODE_MODE = 2 # UCS2 mode
# class to represent the GSM modem
class GSMModem:
# current encoding mode
current_mode = GSM_MODE
def __init__(self):
# default port name, will be updated by find_modem_port functionבךד
self.modem_port_name = 'COM9'
# modem serial port object, used to communicate with the modem
self.serial_port = None
# flag to indicate if the modem is initialized
self.is_modem_initialized = False
def find_modem_port(self):
ports = serial.tools.list_ports.comports()
for port, desc, hwid in sorted(ports):
log("[found port] {}: {} [{}]".format(port, desc, hwid))
if 'modem' in desc.lower():
self.modem_port_name = port
log(f"Found modem at port: {self.modem_port_name}")
return
raise Exception("No modem found.")
def connect_to_modem(self):
# Open serial connection to the modem
log(f"Connecting to modem on port: {self.modem_port_name}")
self.serial_port = serial.Serial(self.modem_port_name, 9600, timeout=2)
# Wait for the modem to initialize
time.sleep(2)
if not self.serial_port.is_open:
raise Exception(f"Failed to connect to modem, serial port: {self.modem_port_name} failed to open.")
log("Successfully connected to modem")
def at_command(self, command, expected_response=b'OK', wait_before_read=0.2):
# send AT command to the modem
log(f"AT Command: {command}")
# if command is instance of string, encode it to bytes
if isinstance(command, str):
self.serial_port.write(command.encode('utf-8') + b'\r\n')
else:
self.serial_port.write(command)
# allow the command to be precesed before reading the response
time.sleep(wait_before_read)
# Read response from modem, until timeout is reached or the requested bytes are read
response = self.serial_port.read_all()
log(f"AT Response: {response.decode('utf-8')}")
# Check if modem returned the expected response
if expected_response not in response:
raise Exception("Error: Modem did not respond with the expected response, we expected: " + expected_response.decode('utf-8') + " but got: " + response.decode('utf-8') + " instead.")
return response
# this function will be called only once, when the first action is performed on the modem
# it will initialize the modem and open the serial port
# it will send some AT commands to the modem to initialize it
# it will also set the is_modem_initialized flag to True
def initialize_modem(self):
if self.is_modem_initialized:
return
self.find_modem_port() # Find the modem port
self.connect_to_modem() # Open serial connection to the modem
self.at_command('at', expected_response=b'') # Send some dummy message
self.at_command(bytes([3]), wait_before_read=2, expected_response=b'') # Send Ctrl+C character to cancel any send sms process
self.at_command(bytes([26]), wait_before_read=2, expected_response=b'') # Send Ctrl+Z character to finish any sms process
self.at_command('AT+CSCS="GSM"') # Set the modem to use GSM encoding
self.at_command('ATE0') # Disable local echo
self.at_command('AT') # Send AT command to test modem is ok
self.at_command('ATH') # Hang up any existing connections
self.at_command('ATZ') # Reset modem to default settings
# Set the GSM modem to send SMS in text mode (and not PDU mode)
self.at_command('AT+CMGF=1')
# set the modem to read sms messages from the SIM card
self.at_command('AT+CPMS="SM","SM","SM"')
# Set modem initialization flag
self.is_modem_initialized = True
# this function will send an SMS message to the specified phone number
# it will first initialize the modem if it's not initialized yet
# then it will send the SMS message using the AT commands
def send_sms(self, phone_number, message):
# check if the message contains a utf-8 character
try:
# try to encode the message to ascii, if it fails, it means the message contains a utf-8 character
message.encode('ascii')
if self.current_mode == UNICODE_MODE:
# if the current mode is unicode, we need to switch to GSM mode (7-bit Ascii encoding)
self.at_command('AT+CSCS="GSM"', expected_response=b'OK')
# set the modem to use ASCII encoding for the SMS message
self.at_command('AT+CSMP=,,0,0', expected_response=b'OK')
self.current_mode = GSM_MODE
except UnicodeEncodeError:
if self.current_mode == GSM_MODE:
# if the current mode is GSM, we need to switch to UCS2 mode (UTF-16BE hex encoding)
self.at_command('AT+CSCS="UCS2"', expected_response=b'OK')
# set the modem to use UTF-8 encoding for the SMS message
self.at_command('AT+CSMP=17,167,0,8', expected_response=b'OK')
self.current_mode = UNICODE_MODE
# if the message contains a utf-8 character, we need to encode it to utf-16
phone_number = phone_number.encode('utf-16-be').hex().upper()
message = message.encode('utf-16-be').hex().upper()
# Set the phone number to send the SMS to
self.at_command(f'AT+CMGS="{phone_number}"', expected_response=b'>')
# set the sms message to send as bytes to prevent at_commannd function from adding \r\n to the message
self.at_command(message.encode('utf-8'), expected_response=b'')
# send the sms message by sending Ctrl+Z character
self.at_command(bytes([26]), expected_response=b'', wait_before_read=3) # Send Ctrl+Z character
def hangup(self):
# Hang up any existing connections
self.at_command('ATH', wait_before_read=1)
def call_phone(self, phone_number):
# make a call to the recipient phone number
self.at_command('ATD{};'.format(phone_number), expected_response=b'OK', wait_before_read=1)
def check_modem_status(self):
# Check modem status
self.at_command('ATI') # returns the product identification information
self.at_command('ATI3') # returns the product firmware version
self.at_command('AT+CREG?', b'+CREG: 0,1', wait_before_read=1) # gives information about the registration status and access technology of the serving cell
self.at_command('AT+CSQ', b'+CSQ:', wait_before_read=1) # Check signal quality
self.at_command('AT+CGATT?', b'+CGATT: 1', wait_before_read=1) # Check if the device is attached to the packet domain service
self.at_command('AT+CGREG?', b'+CGREG: 0,1', wait_before_read=1) #
self.at_command('AT+CGDCONT?', b'+CGDCONT:', wait_before_read=1) # check the modem PDP context parameter type
self.at_command('AT+COPS?', b'+COPS:', wait_before_read=1) # check the current mode, registered operator, and radio access technology
self.at_command('AT+CGACT?', b'+CGACT:', wait_before_read=1) # check the status of the PDP context (Activated or Deactivated)
self.at_command('AT+CGPADDR=1', b'+CGPADDR:', wait_before_read=1) # returns the IP address(es) of the PDP context(s)
def close_serial_port(self):
if self.serial_port is not None:
self.serial_port.close()
self.serial_port = None
log("Serial port closed")
# my modem uses an app to communicate with the modem, so it needs to be up and running before sending AT commands
def start_modem_app(self):
# start the modem app
log("Starting modem app...")
os.system('start "" "C:\\Program Files\\4G LTE Modem\\App.exe"')
# sleep for 5 seconds and allow the app to start
time.sleep(5)
# my modem uses an app to communicate with the modem, so i need to stop it first if it is running
def stop_modem_app(self):
# kill the modem app process
log("Killing modem app process...")
os.system('taskkill /f /im App.exe')
# sleep for 5 seconds
time.sleep(5)
def restart_modem_app(self):
self.stop_modem_app()
self.start_modem_app()
####### test the GSMModem class #######
test_phone_number = "+972555111111" # replace with your phone number
modem = GSMModem()
# modem.restart_modem_app()
modem.initialize_modem()
start_time = time.time()
modem.send_sms(test_phone_number, "test 1")
print(time.time()-start_time)
start_time = time.time()
modem.send_sms(test_phone_number, "test 2")
print(time.time()-start_time)
start_time = time.time()
modem.send_sms(test_phone_number, "test 3")
print(time.time()-start_time)
# place a call to the phone number
modem.call_phone(test_phone_number)
# launch a thread that will hangup the call in 10 seconds
threading.Timer(10, modem.hangup).start()
Notes:
test_phone_number
with your phone numberpy .\gsm_modem.py
to testin order to use it as driver:
remark or delete all the test lines (from line 195 until the end of the file)
from this line: ####### test the GSMModem class #######
untill the end of the file
then you can call it from code like this:
modem = GSMModem()
modem.initialize_modem()
modem.send_sms(test_phone_number, "test 1")
modem.call_phone(test_phone_number)
modem.hangup()
Upvotes: 0
Reputation: 46794
Send it in UCS2, which is normal UTF-16 encoding.
I think this should do the trick:
>>> a=u"שלום"
>>> a
u'\u05e9\u05dc\u05d5\u05dd'
>>> a.encode("utf_16_be").encode("hex")
'05e905dc05d505dd'
Upvotes: 4
Reputation: 1897
Note that when using a multi-byte character set (such as UCS2) the maximum number of characters per message will be significantly reduced. The well known 160 character limit is based on a 7 bit character set, with a 16 bit character set you'll be limited to 70 characters.
Upvotes: 2