apple_pie
apple_pie

Reputation: 339

sending sms in hebrew

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

Answers (3)

Shaybc
Shaybc

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:

  • replace test_phone_number with your phone number
  • run the script using: py .\gsm_modem.py to test
  • you will receive 3 sms, and one phone call that will hangup in 10 seconds
  • it will automatically find the serial port your usb modem is at

in 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

adamk
adamk

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

Victor Welling
Victor Welling

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

Related Questions