Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultiMobile MT9234MU modem support and minor improvement to hang-up detection #170

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions callattendant/app.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ DEBUG = False
# TESTING: If True function tests are executed in lieu of normal operation
TESTING = False

# REC_VM_MAX_DURATION: How long to record voicemail / message in seconds Default 120
REC_VM_MAX_DURATION = 120

# SERIAL_BAUD_RATE: # Serial Baud Rate 57600 for USR and CONEXANT, 115200 MT9234MU. 115200 could work for USR and CONEXANT based modem too.
# MT9234MU seems to requrie 115200 baud rate to produce good audio quality.

SERIAL_BAUD_RATE = 57600

# DATABASE: Sqlite database for incoming call log, whitelist and blacklist
# This should not be changed/overrriden except during development/testing
#DATABASE = "callattendant.db"
Expand Down
11 changes: 10 additions & 1 deletion callattendant/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
# and screened callers through to the home phone.
#
default_config = {
"VERSION": '1.1.1',
"VERSION": '1.1.2',

"ENV": 'production',
"DEBUG": False,
"TESTING": False,

"REC_VM_MAX_DURATION": 120,
"SERIAL_BAUD_RATE": 57600,

"DATABASE": "callattendant.db",
"SCREENING_MODE": ("whitelist", "blacklist"),

Expand Down Expand Up @@ -173,6 +176,12 @@ def validate(self):
if not isinstance(self["TESTING"], bool):
print("* TESTING should be bool: {}".format(type(self["TESTING"])))
success = False
if not isinstance(self["REC_VM_MAX_DURATION"], int):
print("* REC_VM_MAX_DURATION should be an integer: {}".format(type(self["REC_VM_MAX_DURATION"])))
success = False
if not isinstance(self["SERIAL_BAUD_RATE"], int):
print("* SERIAL_BAUD_RATE should be an integer: {}".format(type(self["SERIAL_BAUD_RATE"])))
success = False
if not isinstance(self["BLOCK_ENABLED"], bool):
print("* BLOCK_ENABLED should be a bool: {}".format(type(self["BLOCK_ENABLED"])))
success = False
Expand Down
38 changes: 33 additions & 5 deletions callattendant/hardware/modem.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@
ETX_CODE = chr(3) # End Transmission (ETX) code
CR_CODE = chr(13) # Carraige return
LF_CODE = chr(10) # Line feed
ESCAPE_CODE = chr(27) # Escape key for use with MT9234MU commands with multi-page response

# Supported modem product codes returned by ATI0
USR_5637_PRODUCT_CODE = b'5601'
CONEXANT_PROODUCT_CODE = b'56000'
CONEXANT_PRODUCT_CODE = b'56000'
MT9234MU_PRODUCT_CODE = b'MT9234MU'

# Modem AT commands:
# See http://support.usr.com/support/5637/5637-ug/ref_data.html
Expand Down Expand Up @@ -83,6 +85,7 @@
GO_OFF_HOOK = "ATH1"
GO_ON_HOOK = "ATH0"
TERMINATE_CALL = "ATH"
DISABLE_SPEAKER = "ATM0"

# Modem DLE shielded codes - DCE to DTE modem data
DCE_ANSWER_TONE = (chr(16) + chr(97)).encode() # <DLE>-a
Expand Down Expand Up @@ -114,6 +117,10 @@
# Record Voice Mail variables
REC_VM_MAX_DURATION = 120 # Time in Seconds - TODO: make REC_VM_MAX_DURATION a config setting.

# Serial Baud Rate 57600 for USR and CONEXANT, 115200 MT9234MU. 115200 could work for USR and CONEXANT based modem too. Hasn't been tested
# MT9234MU has stretched out audio at lower baud rate. Indication of underrun buffer?
SERIAL_BAUD_RATE = 57600

TEST_DATA = [
b"RING", b"DATE=0801", b"TIME=1801", b"NMBR=8055554567", b"NAME=Test1 - Permitted", b"RING", b"RING", b"RING", b"RING",
b"RING", b"DATE=0802", b"TIME=1802", b"NMBR=5551234567", b"NAME=Test2 - Spammer",
Expand All @@ -137,10 +144,19 @@ def __init__(self, config):
application configuration dict
"""
print("Initializing Modem")

global SERIAL_BAUD_RATE, REC_VM_MAX_DURATION

self.config = config
self.is_open = False
self.model = None # Model is set to USR, CONEXANT or UNKNOWN by _detect_modem

REC_VM_MAX_DURATION = REC_VM_MAX_DURATION if self.config['REC_VM_MAX_DURATION'] == None else self.config['REC_VM_MAX_DURATION']
SERIAL_BAUD_RATE = SERIAL_BAUD_RATE if self.config['SERIAL_BAUD_RATE'] == None else self.config['SERIAL_BAUD_RATE']

print("Serial baud rate: {}".format(SERIAL_BAUD_RATE))
print("Record duraction: {} seconds".format(REC_VM_MAX_DURATION))

# Thread synchronization objects
self._stop_event = threading.Event()
self._lock = threading.RLock()
Expand Down Expand Up @@ -474,6 +490,9 @@ def record_audio(self, audio_file_name, detect_silence=True):
if (DCE_BUSY_TONE in audio_data):
print(">> Busy Tone... Stop recording.")
break
if (DCE_DIAL_TONE in audio_data):
print(">> Dial tone... Stop recording.")
break

# Test for silence
if detect_silence:
Expand Down Expand Up @@ -517,7 +536,7 @@ def record_audio(self, audio_file_name, detect_silence=True):
# Send End of Recieve Data state by passing "<DLE>!"
# USR-5637 note: The command returns <DLE><ETX>, but the DLE is stripped
# from the response during the test, so we only test for the ETX.
response = "OK" if self.model == "CONEXANT" else ETX_CODE
response = "OK" if self.model == "CONEXANT" else "OK" if self.model == "MT9234MU" else ETX_CODE
if not self._send(DTE_END_VOICE_DATA_RX, response):
print("* Error: Unable to signal end of data receive state")

Expand Down Expand Up @@ -745,7 +764,7 @@ def _init_serial_port(self, com_port):
The OS com port
"""
self._serial.port = com_port
self._serial.baudrate = 57600 # bps
self._serial.baudrate = SERIAL_BAUD_RATE # bps
self._serial.bytesize = serial.EIGHTBITS # number of bits per bytes
self._serial.parity = serial.PARITY_NONE # set parity check: no parity
self._serial.stopbits = serial.STOPBITS_ONE # number of stop bits
Expand All @@ -765,7 +784,8 @@ def _detect_modem(self):
global SET_VOICE_COMPRESSION, DISABLE_SILENCE_DETECTION, \
ENABLE_SILENCE_DETECTION_5_SECS, ENABLE_SILENCE_DETECTION_10_SECS, \
DTE_RAISE_VOLUME, DTE_LOWER_VOLUME, DTE_END_VOICE_DATA_TX, \
DTE_END_VOICE_DATA_RX, DTE_CLEAR_TRANSMIT_BUFFER
DTE_END_VOICE_DATA_RX, DTE_CLEAR_TRANSMIT_BUFFER, \
GET_MODEM_SETTINGS, ENTER_VOICE_MODE

# Test if connected to a modem using basic AT command.
if not self._send("AT"):
Expand All @@ -779,7 +799,7 @@ def _detect_modem(self):
print("******* US Robotics Model 5637 detected **********")
self.model = "USR"

elif CONEXANT_PROODUCT_CODE in result:
elif CONEXANT_PRODUCT_CODE in result:
print("******* Conextant-based modem detected **********")
self.model = "CONEXANT"
# Define the settings for the Zoom3905 where they differ from the USR5637
Expand All @@ -794,6 +814,12 @@ def _detect_modem(self):
DTE_END_VOICE_DATA_TX = (chr(16) + chr(16) + chr(16) + chr(3)) # <DLE><DLE><DLE><ETX>
DTE_CLEAR_TRANSMIT_BUFFER = (chr(16) + chr(16) + chr(16) + chr(24)) # <DLE><DLE><DLE><CAN>

elif MT9234MU_PRODUCT_CODE in result:
print("******* MT9234MU modem detected **********")
self.model = "MT9234MU"
GET_MODEM_SETTINGS = GET_MODEM_SETTINGS + '\r' + ESCAPE_CODE
ENTER_VOICE_MODE = ENTER_VOICE_MODE + '\r' + ESCAPE_CODE

else:
print("******* Unknown modem detected **********")
# We'll try to use the modem with the predefined USR AT commands if it supports VOICE mode.
Expand Down Expand Up @@ -824,6 +850,8 @@ def _init_modem(self):
print("Error: Failed to disable local echo mode")
if not self._send(ENABLE_FORMATTED_CID):
print("Error: Failed to enable formatted caller report.")
if not self._send(DISABLE_SPEAKER):
print("Error: Failed to disable internal speaker.")

# Save these settings to a profile
if not self._send("AT&W0"):
Expand Down