MC7340 on a Digi Transport wr21

Hello everyone,

I’m currently working on integrating a Sierra Wireless MC7340 module into a Digi WR21 router and am facing some configuration and provisioning challenges with the GPS, the modem works but i cannot get the GPS to work. I would appreciate any guidance or advice from the community to help resolve these issues.

Background: I have upgraded the cellular module in our Digi WR21 to a Sierra Wireless MC7340. The existing provisioning script, originally designed for a different module, uses a variety of AT commands and custom settings, and I need to ensure these are compatible or suitably updated for the MC7340.

' provision.sb
'
' This sends a series of configuration commands to the GPRS modem.
' In this case, the configuration commands are those used for provisioning
' the CMotech CDMA modems.
' Usage:
'     bas provision.sb <IMSI> <NAI> <NAIPW>
'
' The script automatically works out which ASY port from the modemcc 0 asy_add setting
' It also deactivates the PPP instance just in case it's in use.
'
' Version2: for ICE/NMT PRI, added MDN (based on MIN), home SID/NID list etc
' Version2: also ICE/NMT PRI, added programming of ACCOLC via DM port
' version for NMT (Sweden)
' home sid/nid now programmed via DM port due to possible bug in CMotech AT command
' MCC and MNC are now correcly encoded following revelation from CMotech that these
' values are written unencoded to NVRAM (unlike the MIN which is correctly encoded).
' Version 4 for Norway, Sweden and Denmark
' Version 5 allows a directory number to be specified. If the a_key parameter is less
' than 20 digits, it is assumed to be a directory number and overrides builtin setting

' include "pppfcs.sb"

' the following line makes all string comparisons case insensitive
'
OPTION COMPARE sbCaseInsensitive

' set up the error handler
'
on error goto ErrorLabel

const nl = "\r\n"

' set to 1 to allow dprint messages
global_debug = 0

' Enter an error message in the event log and exit
' Also prints message to console output
'
function errexit(msg)
	print "ERROR: " & msg & nl
	junk = system("setevent \"" & msg & "\"" & " 6")
	print "ERROR: provisioning failed" & nl
	stop
end function

' Enter an error message in the event log
' Also prints message to console output
'
function eventmsg(msg)
	print msg, nl
	junk = system("setevent \"" & msg & "\"" & " 0")
end function

function dprint(msg)
	if global_debug <> 0 then print msg & nl
end function


' From http://en.wikipedia.org/wiki/Computation_of_CRC
' // Least significant bit first (little-endian)
' // x^16+x^12+x^5+1 = 1000 0100 0000 1000 (1) = 0x8408

' Add FCS to GLOBAL array buf of length GLOBAL msglen
'
function addfcs
	local remainder, i, j
     remainder = 0xffff
     for i = 1 to msglen
         remainder = remainder xor buf[i]
         for j = 1 to 8
             if (remainder and 0x0001) <> 0 then
                 remainder = (remainder \ 2) xor 0x8408
             else
                 remainder = remainder \ 2
             endif
         next j
     next i
'	print "\r\nFCS = 0x", format("%04X", remainder), "\r\n"
	remainder = (NOT remainder) AND 0xffff
'	print "\r\nInverted FCS = 0x", format("%04X", remainder), "\r\n"
	buf[msglen+1] = remainder AND 255
	buf[msglen+2] = remainder\256
	msglen += 2
end function

' calculate the FCS for a string buffer
'
function calculate_fcs(string_data_buf, data_len)
	local remainder, i, j
     remainder = 0xffff
     for i = 1 to data_len
         remainder = remainder xor asc(mid(string_data_buf,i,1))
         for j = 1 to 8
             if (remainder and 0x0001) <> 0 then
                 remainder = (remainder \ 2) xor 0x8408
             else
                 remainder = remainder \ 2
             endif
         next j
     next i
	remainder = (NOT remainder) AND 0xffff
	calculate_fcs = remainder
end function

function decode_escapes(string_data_buf)
	local ostr, n, i, c

	ostr = ""
	n = len(string_data_buf)
	for i = 1 to n-1
		c = asc(mid(string_data_buf,i,1))
		if c = 0x7D then
			i += 1
			c = asc(mid(string_data_buf,i,1))
			c = c XOR 0x20
		endif
		ostr = ostr & chr(c)
	next i
	' add the last character
	ostr = ostr & right(string_data_buf, 1)
	decode_escapes = ostr
end function

' Return the value of Sarian parameter in rsp
' strCmd needs to be of form "<entity name> <instance no> <parameter name> ?"
' e.g. "ppp 0 ans ?"

function getParam(strCmd,rsp)
	local res, tmp, tmpPos, x, pos2
	getParam = false
	res = execute(strCmd, 0, tmp)
	if res = true then
		tmpPos = InStr(tmp, nl & "OK" & nl)
		if tmpPos > 0 then
        	For x = tmpPos To 1 Step -1
	    		pos2 = InStr(tmp, nl, x - 3)
		        If pos2 > 0 and pos2 <> tmpPos then
		        	x = 1
		        endif
		    next x
		    if pos2 > 0 and pos2 <> tmpPos then
		    	rsp = mid(tmp, pos2 + 2, tmpPos - pos2 - 2)
		        getParam = true
			endif
	    endif    
	endif
end function

' find the first PPP instance configured for GPRS
' returns TRUE if we found one, and put the instance number into num
' otherwise returns FALSE
'
function get_gprs_ppp_instance(num)
	local mdmstr, i

	get_gprs_ppp_instance = false
	for i = 0 to 4
		if getParam("ppp " & i & " use_modem ?", mdmstr) = TRUE then
'			print "PPP ", i, " use_modem = ", mdmstr, nl
			if mdmstr = "1" or mdmstr = "4" or mdmstr = "5" then
				get_gprs_ppp_instance = true
				num = i
				exit function
			endif
		else
			print "Failed to get PPP ", i, " use_modem", nl
		endif
	next i
end function

' ******** SERIAL IO FUNCTIONS ********
function init_mc75io
' open the serial port used for the MC75
' two handles, one for input and one for output
        mc75in = 0
        open mc75asyport for input as mc75in
'		if error() <> 0 then
'			print "error opening asy port", nl
'		endif
        mc75out = 0
        open mc75asyport for output as mc75out
        rxbuf = ""
        junk = setevent(0)
        junk = setevent(mc75in)

end function

' gets a character from mc75in
' timeout is fixed at 10 secs for now
' Return TRUE if successful, otherwise FALSE
' If TRUE, then character is retuned in the rsp parameter
'
function getchar(rsp)
        local tstart, junk

        getchar = FALSE
        tstart = ticks
        while ticks-tstart < 1000
                if len(rxbuf) <> 0 then
                        rsp = left(rxbuf, 1)
                        rxbuf = mid(rxbuf, 2)
                        getchar = TRUE
                        exit function
                endif
                if waitevents(1000) = mc75in then
                        junk = sockinfo(mc75in, sinfo)
'                        print "Bytes available = ", sinfo[2], nl
                        if sinfo[2] <> 0 then
                                rxbuf = input(sinfo[2], mc75in)
                        endif
                endif
        wend
end function

' Get a line from mc75in
' Return TRUE if successful, otherwise FALSE
' If TRUE, then line is retuned in the rsp parameter
'
function getline(rsp)
        local junk, sinfo, tbuf, tchr

        getline = FALSE
        tbuf = ""
        while TRUE
                if getchar(tchr) <> TRUE then exit function

' see if we received a LF (our end of line)
                if asc(tchr) = 0x0a then
                        rsp = tbuf
                        getline = TRUE
                        exit function
                endif

' ignore CR, append all other chars
                if asc(tchr) <> 0x0d then
                        tbuf = tbuf & tchr
                endif
        wend
end function

function getOKline
        local i, myline

        getOKline = FALSE
        for i = 1 to 10
                if getline(myline) <> TRUE then exit function
                if myline = "OK" then
                        getOKline = TRUE
                        exit function
                endif
        next i
end function

function getRespline(rspline)
        local i, myline

        getRespline = FALSE
        for i = 1 to 10
                if getline(myline) <> TRUE then exit function
                if (instr(myline, "OK") <> undef) or (instr(myline, "ERROR") <> undef) then
                        getRespline = TRUE
						rspline = myline
                        exit function
                endif
        next i
end function

' send the DM command containing the given parameters
' Note that buf and msglen are GLOBAL (used by addfcs)
' The packet is sent to the GLOBAL file handle infoout
'
function send_dm(params, paramlen)

	local i, ostr

	for i = 1 to paramlen
		buf[i] = params[i]
	next i
	msglen = paramlen
	addfcs

' copy to the string ostr, adding framing and escape characters
'
	ostr = ""
	for i = 1 to msglen
		if buf[i] = 0x7e or buf[i] = 0x7d then
			ostr = ostr & chr(0x7d)
			ostr = ostr & chr(buf[i] XOR 0x20)
		else
			ostr = ostr & chr(buf[i])
		endif
	next i
	ostr = ostr & chr(0x7e)

'	dump_packet(ostr, "Sending")
	print #infoout, ostr

end function

'
' Receive a DM response and check the FCS
' The packet is read from the GLOBAL file handle infoin
'
function recv_dm(pkt)
	local i, junk, complete, infoinbuf, pktbuf, infostat, unescstr, fcsval, actualfcs

	recv_dm = FALSE
	pktbuf = ""
	i = 5
	complete = FALSE
	while i > 0 AND complete = FALSE
		sleep(1)
		junk = sockinfo(infoin, infostat)
		dprint "bytes received = " & infostat[2]
		if infostat[2] <> 0 then
			infoinbuf = input(infostat[2], infoin)
			pktbuf = pktbuf & infoinbuf
			if asc(right(infoinbuf, 1)) = 0x7e then
				complete = TRUE
				unescstr = decode_escapes(pktbuf)

				fcsval = calculate_fcs(unescstr, len(unescstr)-3)
				dprint "Calculated FCS = 0x" & format("%04X", fcsval)
				actualfcs = asc(mid(unescstr, len(unescstr)-2, 1)) + asc(mid(unescstr, len(unescstr)-1, 1))*256
				dprint "Actual FCS = 0x" & format("%04X", actualfcs)
			endif
		endif
		i -= 1
	wend
	if complete = FALSE then
		dprint "No response"
		exit function
	endif
	if fcsval <> actualfcs then
		dprint "FCS error"
		exit function
	endif

	recv_dm = TRUE
	pkt = unescstr

end function

' Write the ACCOLC value. We have to do it with an NV write because CMotech's
' command NVW ACCOLC=<nam>,<value> doesn't work.
'
function write_accolc(accolc)
	local i, dmbuf, pkt

	write_accolc = FALSE

	eventmsg "write_accolc(" & accolc & ")"

	dmbuf[1] = 0x27
	dmbuf[2] = 0x25
	dmbuf[3] = 0x00
	for i = 1 to 130
		dmbuf[i+3] = 0
	next i
	dmbuf[5] = accolc
	dmbuf[6] = accolc
	send_dm(dmbuf, 133)

	if recv_dm(pkt) = FALSE then exit function

	write_accolc = TRUE

end function

' Write the HOME_SID_NID value. We have to do it with an NV write to item 0x103 because CMotech's
' command NVW HOME_SID_NID writes incorrect values
'
function write_home_sid_nid
	local i, dmbuf, pkt

	write_home_sid_nid = FALSE

	' NV write command
	dmbuf[1] = 0x27
	' NV Item
	dmbuf[2] = 0x03
	dmbuf[3] = 0x01
	for i = 1 to 130
		dmbuf[i+3] = 0
	next i

	for i = 1 to home_sid_nid_len
		' low byte
		dmbuf[i*2+3] = home_sid_nid_list[i] AND 255
		' high byte
		dmbuf[i*2+4] = home_sid_nid_list[i] \ 256
	next i

	send_dm(dmbuf, 133)

	if recv_dm(pkt) = FALSE then exit function

	write_home_sid_nid = TRUE

end function

' write NV item 176 (0x00B0) containing the MCC (2 bytes)
'
function write_mcc(mccval)
	local i, dmbuf, pkt
	write_mcc = FALSE
	eventmsg "write_mcc(" & mccval & ")"
	dmbuf[1] = 0x27
	dmbuf[2] = 0xB0
	dmbuf[3] = 0x00
	for i = 1 to 130
		dmbuf[i+3] = 0
	next i
	dmbuf[5] = mccval AND 255
	dmbuf[6] = mccval \ 256
	send_dm(dmbuf, 133)
	if recv_dm(pkt) = FALSE then exit function
	write_mcc = TRUE
end function

' write NV item 177 (0x00B1) containing the MNC (1 byte)
'
function write_mnc(mncval)
	local i, dmbuf, pkt
	write_mnc = FALSE
	eventmsg "write_mnc(" & mncval & ")"
	dmbuf[1] = 0x27
	dmbuf[2] = 0xB1
	dmbuf[3] = 0x00
	for i = 1 to 130
		dmbuf[i+3] = 0
	next i
	dmbuf[5] = mncval
	send_dm(dmbuf, 133)
	if recv_dm(pkt) = FALSE then exit function
	write_mnc = TRUE
end function

function restorecfg
	local res
	res = execute("modemcc 0 asy_add " & asystr, 0, resstr)
	res = execute("modemcc 0 info_asy_add " & infoasystr, 0, resstr)
end function

' ******** MAIN PROGRAM STARTS HERE ********
errcnt = 0

split command() by " " to imsi, nai, naipw, a_key

print "\r\n"

if isundef(imsi) or isundef(nai) or isundef(naipw) then
	errexit "Incorrect usage: provision.sb <IMSI> <NAI> <NAIPW>"
	exit sub
endif

dirnum = ""
akey = ""

if isdefined(a_key) then
	dprint "A_KEY supplied"
	if len(a_key) > 20 or len(a_key) < 10 then errexit "Incorrect A_KEY length: should be 10-20 digits"
	for i = 1 to len(a_key)
		chval = asc(mid(a_key,i,1))
		if chval < 0x30 or chval > 0x39 then errexit "A_KEY contains non-digit character(s)"
	next i
	dprint "A_KEY OK(" & a_key & ")"
	if len(a_key) = 20 then
		akey = a_key
	else
		dirnum = a_key
	endif
else
	dprint "A_KEY will not be programmed"
endif

if len(imsi) <> 15 then errexit "Incorrect IMSI length: should be 15 digits"

for i = 1 to 15
	chval = asc(mid(imsi,i,1))
	if chval < 0x30 or chval > 0x39 then errexit "IMSI contains non-digit character(s)"
next i


' the 3 digit MCC and 2 digit MNC need to be converted before writing using the AT command
'
mcc = mid(imsi, 1, 3)
mcc1 = val(mid(mcc,1,1))
mcc2 = val(mid(mcc,2,1))
mcc3 = val(mid(mcc,3,1))
if mcc1 = 0 then mcc1 = 10
if mcc2 = 0 then mcc2 = 10
if mcc3 = 0 then mcc3 = 10
mcc24 = mcc1*100 + mcc2*10 + mcc3 - 111

mnc = mid(imsi, 4, 2)
mnc1 = val(mid(mnc,1,1))
mnc2 = val(mid(mnc,2,1))
if mnc1 = 0 then mnc1 = 10
if mnc2 = 0 then mnc2 = 10
mnc16 = mnc1*10 + mnc2 - 11

' Initial default settings for Sweden
'
NbConfigStrings = 9
ConfigStrings[1] = "at$$spc=000000"
' ConfigStrings[2] = "at$$nvw mcc=0," & mcc24
' ConfigStrings[3] = "at$$nvw mnc=0," & mnc16
ConfigStrings[2] = "at$$nvw min=0," & mid(imsi, 6, 10)
ConfigStrings[3] = "at$$nvw dir_number=0,46" & mid(imsi, 6, 10)
ConfigStrings[4] = "at$$nvw home_reg=0,1"
ConfigStrings[5] = "at$$nvw for_sid=0,1"
ConfigStrings[6] = "at$$nvw for_nid=0,1"
ConfigStrings[7] = "at$$nvw sys_pref=0,3"
ConfigStrings[8] = "at$$hdr_nai=" & nai
ConfigStrings[9] = "at$$hdr_password=" & naipw
' ConfigStrings[12] = "at$$nvw a_key=0," & a_key

ResetString = "at$$reset"
AKeyString = "at$$nvw a_key=0," & akey

home_sid_nid_list[1] = 24
home_sid_nid_list[2] = 2
home_sid_nid_list[3] = 13
home_sid_nid_list[4] = 1
home_sid_nid_list[5] = 35
home_sid_nid_list[6] = 3
home_sid_nid_list[7] = 46
home_sid_nid_list[8] = 4
home_sid_nid_len = 8

' country specific settings for Norway
'
if mcc = "242" then
	ConfigStrings[3] = "at$$nvw dir_number=0,47" & mid(imsi, 6, 10)
	home_sid_nid_list[1] = 13
	home_sid_nid_list[2] = 1
	home_sid_nid_list[3] = 24
	home_sid_nid_list[4] = 2
endif

' country specific settings for Denmark
'
if mcc = "238" then
	ConfigStrings[3] = "at$$nvw dir_number=0,45" & mid(imsi, 6, 10)
	home_sid_nid_list[1] = 35
	home_sid_nid_list[2] = 3
	home_sid_nid_list[5] = 24
	home_sid_nid_list[6] = 2
endif

' Network specific settings for Ediscom
'
if mcc = "262" and mnc = "19" then
	home_sid_nid_list[1] = 22110
	home_sid_nid_list[2] = 0
	home_sid_nid_list[3] = 0
	home_sid_nid_list[4] = 0
	home_sid_nid_list[5] = 0
	home_sid_nid_list[6] = 0
	home_sid_nid_list[7] = 0
	home_sid_nid_list[8] = 0
endif

' Override directory number if specified separately
'
if dirnum <> "" then ConfigStrings[3] = "at$$nvw dir_number=0," & dirnum

' Find out what ASY port is used by modemcc for GPRS
'
if getParam("modemcc 0 asy_add ?", asystr) = TRUE then
	dprint "modemcc 0 asy_add is: " & asystr
else
	errexit "Failed to get modemcc 0 asy_add"
endif

if asystr = "255" or asystr = "0" or asystr = "" then
	errexit "Modemcc asy_add incorrectly configured: try again after muxon"
endif

' Find out what INFO ASY port is used by modemcc for GPRS
'
if getParam("modemcc 0 info_asy_add ?", infoasystr) = TRUE then
	dprint "modemcc 0 info_asy_add is: " & infoasystr
else
	errexit "Failed to get modemcc 0 info_asy_add"
endif

if infoasystr = "0" or infoasystr = "" then
	errexit "Modemcc info_asy_add incorrectly configured: try again after muxon"
endif


' Find out which PPP instance is configured for GPRS
'
got_gprs_ppp = get_gprs_ppp_instance(gprs_ppp_instance)

if got_gprs_ppp then
	dprint "PPP " & gprs_ppp_instance & " is configured for GPRS"
	res = execute("ppp " & gprs_ppp_instance & " deact_rq", 0, resstr)
	sleep(1)
else
	dprint "No PPP instances configured for GPRS"
endif

res = execute("modemcc 0 asy_add 255", 0, resstr)
res = execute("modemcc 0 info_asy_add 0", 0, resstr)

' initialise the serial port
AsyPortName = "ASY" & asystr
mc75asyport = AsyPortName
dprint "AsyPortName = " & AsyPortName

init_mc75io()

eventmsg "Connecting to modem on " & AsyPortName & "..."

' First make sure we can talk to the device by ATing it
' until we get OK back
'
i = 0
GotOK = false
while i < 10 and GotOK = false
	i += 1
	dprint "Sending AT..."
    print #mc75out, "AT\r"
    if getOKline() = true then GotOK = true
wend

if GotOK = false then
	restorecfg()
	errexit "Cannot AT device on " & AsyPortName
endif

dprint "Connected to modem on " & AsyPortName

' First, do a reset in case logging has been enabled
' This will turn off excessive messages on the DM interface
'
print #mc75out, ResetString, "\r"
dprint "Sent " & ResetString
rslt = getRespline(rspline)
if rslt = true then
	dprint "Got response: " & rspline
else
	dprint "No response to reset command"
endif
sleep(2)

' And make sure we can talk to the device by ATing it
' until we get OK back
'
i = 0
GotOK = false
while i < 10 and GotOK = false
	i += 1
	dprint "Sending AT..."
    print #mc75out, "AT\r"
    if getOKline() = true then GotOK = true
wend

if GotOK = false then
	restorecfg()
	errexit "Cannot AT device on " & AsyPortName
endif

dprint "Reconnected to modem on " & AsyPortName



for i = 1 to NbConfigStrings
	' skip MDN for unknown countries if not overridden
	if i = 3 and mcc <> "240" and mcc <> "238" and mcc <> "242" and dirnum = "" then goto continue
	print #mc75out, ConfigStrings[i], "\r"
	eventmsg "Sent " & ConfigStrings[i]
	rslt = getRespline(rspline)
	if rslt = true then
		if rspline <> "OK" then
			restorecfg()
			errexit "Got bad response: " & rspline
		endif
		dprint "Got response: " & rspline
	else
		restorecfg()
		errexit "No response to command"
	endif
continue:
next i

if akey <> "" then
	print #mc75out, AKeyString, "\r"
	eventmsg "Sent " & AKeyString
	rslt = getRespline(rspline)
	if rslt = true then
		if rspline <> "OK" then
			restorecfg()
			errexit "Got bad response: " & rspline
		endif
		dprint "Got response: " & rspline
	else
		restorecfg()
		errexit "No response to command"
	endif
endif

' now set up the info port for the DM operations
InfoAsyPortName = "ASY" & infoasystr
infoout = 0
infoin = 0
open InfoAsyPortName for output as infoout
open InfoAsyPortName for input as infoin

if write_accolc(val(right(imsi,1))) = FALSE then
	restorecfg()
	errexit "writing accolc failed"
endif

' only set home sid nid list for Sweden, Norway, and Denmark
if mcc = "240" or mcc = "242" or mcc = "238" or (mcc = "262" and mnc = "19") then
	i = 0
	while i < 5 and write_home_sid_nid() = FALSE
		sleep(1)
		i += 1
	wend
endif

if write_mcc(mcc24) = FALSE then
	restorecfg()
	errexit "writing MCC failed"
endif

if write_mnc(mnc16) = FALSE then
	restorecfg()
	errexit "writing MNC failed"
endif


close(infoin)
close(infoout)

' Finally, do a reset
'
print #mc75out, ResetString, "\r"
eventmsg "Sent " & ResetString
rslt = getRespline(rspline)
if rslt = true then
	eventmsg "Got response: " & rspline
else
	dprint "No response to reset command"
endif


' close the serial port and restore the modem connection
'
close(mc75out)
close(mc75in)
sleep(1)
restorecfg()


eventmsg "SUCCESS: provisioning complete"

stop

ErrorLabel:
'print "Got error code: ", error(), "(", error$, ")", nl
if error() = 22 and errcnt < 10 then
	dprint "Retrying connection ..."
	sleep(1)
	errcnt = errcnt + 1
	on error goto ErrorLabel
	resume
endif
restorecfg()
errexit error$

Specific Challenges:

  1. AT Command Compatibility: The script uses several AT commands that I need to confirm are supported by the MC7340 or identify alternatives. The script includes commands related to network registration, modem initialization, and setting various cellular parameters.
  2. Port Configuration: I need to determine the appropriate ASY port configuration for both modem communications and GPS functionality. Currently, it’s unclear if “asy 2” should be used for GPS with the MC7340 installed in the Digi WR21.
gps 0 asy_add 3
gps 0 gpson ON
gps 0 init_str "$GPS_START"
  1. Provisioning Process: The provisioning process needs to be reviewed to ensure it correctly sets up the MC7340 for operation, including setting network parameters, configuring profiles, and handling any carrier-specific requirements.

Questions:

  • Has anyone integrated an MC7340 with a Digi WR21? If so, how did you handle the AT command updates?
  • What considerations should be made for ASY port assignments on the MC7340, especially concerning GPS functionality?
  • Are there specific settings or commands in Sierra Wireless modules that I should adjust in the provisioning script to accommodate the MC7340?

Any scripts, configuration tips, or insights into handling Sierra modules in Digi routers would be highly appreciated. Thank you in advance for your help!