一、硬件设备准备
1.移远EC20CEFAG-512-SGNS mini pice版
2.usb转mini pcie 板 这个板上有sim卡卡槽 一般是Mini SIM(小卡最大的,比它小的还有MicroSim nano Sim)
3.IPEX 接口天线,一般选ipex转sma接sma天线
4.一台x86电脑 小主机也行 我这里用惠普T620瘦客户机 不建议树莓派因为需要额外处理供电问题。
EC20 mini pcie板正反图

usb转接板正反图

IPEX转sma天线图

二、EC20 网络与音频配置
查看串口
ls /dev/ttyUSB* ttyUSB0 ttyUSB1 PCM语音,GPS信号 ttyUSB2 AT控制命令 ttyUSB3
AT指令看版本号
yum install minicom minicom -D /dev/ttyUSB2
# 输入ATI看一下EC20的版本号:
ATI Quectel EC20F Revision: EC20CEFAGR06A15M4G
Ctrl+A X 退出
注意EC2的固件版本影响UAC功能的开启
已知EC20CEFAGR06A06M4G 不支持UAC
可升级到EC20CEFAGR06A15M4G
ATI Quectel EC20F Revision: EC20CEFAGR06A06M4G
建议先升级到A08,再升级到A15
ATI Quectel EC20F Revision: EC20CEFAGR06A08M4G
升级到A15
ATI Quectel EC20F Revision: EC20CEFAGR06A15M4G
下载
https://www.xxshell.com/3271.html
升级参考
https://programmersought.com/article/55674488086/
AT指令重置模块 可选
#重置模块 at+qprtpara=3 #重启 AT+CFUN=1,1
AT指令检查sim卡注册状态
AT+COPS? +COPS: 0,0,"CHN-UNICOM",7 AT+QNWINFO +QNWINFO: "FDD LTE","46001","LTE BAND 3",1650 AT+QENG="servingcell" +QENG: "servingcell","CONNECT","LTE","FDD",460,01
#AT指令开启VOLTE
#打开ims AT+QCFG="ims",1 #查看dongle内的mbn文件 AT+QMBNCFG="List" +QMBNCFG: "List",0,0,0,"ROW_Generic_3GPP",0x05010814,201704141 +QMBNCFG: "List",1,1,1,"OpenMkt-Commercial-CU",0x05011510,201704141 +QMBNCFG: "List",2,0,0,"OpenMkt-Commercial-CT",0x0501131C,201704141 +QMBNCFG: "List",3,0,0,"Volte_OpenMkt-Commercial-CMCC",0x05012011,201706021 +QMBNCFG: "List",4,0,0,"OpenMkt-Commercial-CN",0x05809201,201706091 # 尽管这里列出了移动联通电信的VoLTE配置文件,但使用默认的自动选择CU/CT/CMCC并不能注册VoLTE,在摸索很久之后,笔者发现需要强制选择ROW_Generic_3GPP才能成功注册VoLTE。 #关闭自动选择mbn文件 AT+QMBNCFG="AutoSel",0 #反激活当前的mbn at+qmbncfg="deactivate" #强制选择3gpp AT+QMBNCFG="select","ROW_Generic_3GPP" #重启 AT+CFUN=1,1 可以再确认一下mbn的选择状态,如果ROW_Generic_3GPP的第二位和第三位都是1的话,说明dongle目前选择了这个配置 AT+QMBNCFG="List" +QMBNCFG: "List",0,1,1,"ROW_Generic_3GPP",0x05010814,201704141 +QMBNCFG: "List",1,0,0,"OpenMkt-Commercial-CU",0x05011510,201704141 +QMBNCFG: "List",2,0,0,"OpenMkt-Commercial-CT",0x0501131C,201704141 +QMBNCFG: "List",3,0,0,"Volte_OpenMkt-Commercial-CMCC",0x05012011,201706021 +QMBNCFG: "List",4,0,0,"OpenMkt-Commercial-CN",0x05809201,201706091
#重启完后检查ims的状态
AT+QCFG="ims" +QCFG: "ims",1,1
#如果返回的是 +QCFG: “ims”,1,1 即为激活,如果是+QCFG: “ims”,1,0 说明没有激活
#拨号测试
ATD13800138000; OK
#这里的号码换成自己号码测试,如果拨号成功,则语音正常
AT指令激活UAC数字音频 (可选) 建议debian11+issabel
#查看配置 AT+QCFG="USBCFG" 返回 +QCFG: "usbcfg",0x2C7C,0x0125,1,1,1,1,1,0 注意,这里如果只有6个数字,必须7个数字才支持 可能硬件或者固件不支持,EC20CEFAGR06A06M4G这个低版本不支持,固件升级到A15支持了 含义:倒数第二个参数是启用 adb,倒数第一个参数是启用 UAC。 激活指令 AT+QCFG="USBCFG",0x2C7C,0x0125,1,1,1,1,1,0,1 返回 OK
验证
aplay -L
返回(debian11)
hw:CARD=Android,DEV=0 Android, USB Audio Direct hardware device without any conversions plughw:CARD=Android,DEV=0 Android, USB Audio Hardware device with all software conversions
arecord -L
返回
hw:CARD=Android,DEV=0 Android, USB Audio Direct hardware device without any conversions plughw:CARD=Android,DEV=0 Android, USB Audio Hardware device with all software conversions
要求 linux内核版本>=3.11 (优化安卓 UAC 设备兼容(解决非标准端点 / 采样率问题)),debian系 (在centos系同内核下看不到hw:开头的设备),目前最容易兼容的应该是freepbx17,采用的debian为基础镜像,但是软件包是直连github debian官网源、freepbx官网源进行安装,国内环境直连安装耗时预估一个星期。但16是centos系的可以离线安装,明显是退步了。
本文安装命令以freepbx16为主,其他为辅
freepbx16 全自动安装Fully Automatic Installation后的帐号密码
参考PBX Platforms – FreePBX15-16 installation – PBX Platforms – Sangoma Documentation
freepbx17的默认密码随机生成 默认用户名sangoma,随机生成时可Ctrl+Alt+F2进入第二控制台修改。
三、配置Asterisk的移远通道驱动
移远通道驱动项目地址:https://github.com/IchthysMaranatha/asterisk-chan-quectel
早期华为3G模块通道驱动项目地址:https://github.com/bg111/asterisk-chan-dongle
通道驱动要求asterisk 16, 实测也能支持18 ,兼容freepbx的最低要求没问题
这里用Debian 11 安装asterisk 16
asterisk安装命令
先安装编译相关的依赖
yum命令
yum install -y adb alsa-utils # 安装Autotools核心工具(解决aclocal/autoconf/automake缺失) yum install -y autoconf automake libtool # 同时安装Asterisk编译的基础依赖(避免后续编译asterisk-chan-quectel时报其他错) yum install -y gcc gcc-c++ make git pkgconfig # 这里asterisk-devel 可能是asterisk16-devel 这里建议查一下rpm -qa|grep asterisk 看带不带数字 yum install -y asterisk-devel 或者yum install -y asterisk16-devel 或者yum install -y asterisk18-devel yum install -y sqlite sqlite-devel yum install -y alsa-lib-devel
debian系命令
apt update apt install asterisk asterisk-dev adb git autoconf automake libsqlite3-dev build-essential libasound2-dev alsa-utils
下载和编译通道驱动
git clone https://github.com/IchthysMaranatha/asterisk-chan-quectel cd asterisk-chan-quectel # rocky 8 需要这3个变量 export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 export LANGUAGE=en_US.UTF-8 ./bootstrap ./configure --with-astversion=16 make make install
四、配置Asterisk 拨号计划(Dialplan)
参考https://github.com/IchthysMaranatha/asterisk-chan-quectel/discussions/13#
拷贝quectel.conf到目录/etc/asterisk并将最后4行注释掉
cp uac/quectel.conf /etc/asterisk
vi /etc/asterisk/quectel.conf
[quectel0] ;audio=/dev/ttyUSB1 ; tty port for Audio, set as ttyUSB4 for Simcom if no other dev present data=/dev/ttyUSB2 ; tty port for AT commands;no default value quec_uac=1 ; Uncomment line if using UAC mode alsadev=hw:CARD=Android,DEV=0 ; Uncomment if using UAC, set device name or index as reqd
[quectel0] audio=/dev/ttyUSB1 ; tty port for Audio, set as ttyUSB4 for Simcom if no other dev present data=/dev/ttyUSB2 ; tty port for AT commands; no default value ;quec_uac=1 ; Uncomment line if using UAC mode ;alsadev=hw:CARD=Android,DEV=0 ; Uncomment if using UAC, set device name or index as reqd
UAC音频的激活需要EC20固件和Linux内核版本支持,可参考移远EC20对UAC音频设备识别分析-CSDN博客
systemctl restart asterisk
fwconsole restart
asterisk -rvvv quectel show devices
正常返回的结果
freepbx*CLI> quectel show devices ID Group State RSSI Mode Submode Provider Name Model Firmware IMEI IMSI Number quectel0 0 Free 25 0 0 CHN-UNICOM EC20F EC20CEFAGR06A06M4 867184038634600 460010574507217 +8613800138000
-- [quectel0] Trying to connect on /dev/ttyUSB2...
[2026-01-12 13:57:54] ERROR[2429]: chan_quectel.c:277 lock_create: open('/var/lock/LCK..ttyUSB2') failed: Permission denied
[2026-01-12 13:57:54] WARNING[2429]: chan_quectel.c:362 opentty: unable to open /dev/ttyUSB2: Permission denied
vi /etc/udev/rules.d/99-quectel.rules
# 移远EC20串口权限配置
SUBSYSTEM=="tty", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", GROUP="asterisk", MODE="0660"
# 兜底配置(兼容所有ttyUSB串口)
KERNEL=="ttyUSB[0-9]*", GROUP="asterisk", MODE="0660"
udevadm control --reload-rules && udevadm trigger usermod -aG lock asterisk systemctl restart asterisk #freepbx命令 fwconsole restart
rocky8 增加ACL解锁
还有权限问题
[2026-01-17 17:19:31] ERROR[2022]: chan_quectel.c:277 lock_create: open('/var/lock/LCK..ttyUSB2') failed: Permission denied
修改如下
# 1. 修改之前创建的 asterisk-lock-permission.service 服务文件 cat> /etc/systemd/system/asterisk-lock-permission.service <<EOF [Unit] Description=Set /run/lock permission for asterisk user Before=asterisk.service [Service] Type=oneshot # 给 /run/lock 添加组写权限 ExecStart=/bin/chmod g+w /run/lock # 给 asterisk 用户添加 /run/lock 的 ACL 写入权限 ExecStart=/bin/setfacl -m u:asterisk:rwx /run/lock [Install] WantedBy=multi-user.target EOF # 2. 重新加载 systemd 配置并重启服务 systemctl daemon-reload systemctl restart asterisk-lock-permission.service # 3. 确认服务生效 systemctl status asterisk-lock-permission.service #开机自启 systemctl enable asterisk-lock-permission.service
vi /etc/asterisk/extensions_custom.conf
[incoming-mobile]
; 接收短信:增加空值判断+兼容写法,避免解码失败
exten => sms,1,Verbose(2,Incoming SMS from ${CALLERID(num)})
exten => sms,n,Set(SMS_CONTENT=${BASE64_DECODE(${SMS_BASE64})})
; 仅当短信内容非空时写入日志
exten => sms,n,ExecIf($["${SMS_CONTENT}" != ""]?System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${QUECTELNAME} - ${CALLERID(num)}: ${SMS_CONTENT}' >> /var/log/asterisk/sms.txt))
; TG Bot未读短信(如需启用,取消注释并确保目录存在)
;exten => sms,n,ExecIf($["${SMS_CONTENT}" != ""]?System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${QUECTELNAME} - ${CALLERID(num)}\n${SMS_CONTENT}' >> /var/log/asterisk/unread_sms/${STRFTIME(${EPOCH},,%Y%m%d%H%M%S)}-${CALLERID(num)}.txt))
exten => sms,n,Hangup()
; 接收USSD:同样增加空值判断
exten => ussd,1,Verbose(2,Incoming USSD)
exten => ussd,n,Set(USSD_CONTENT=${BASE64_DECODE(${USSD_BASE64})})
exten => ussd,n,ExecIf($["${USSD_CONTENT}" != ""]?System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${QUECTELNAME}: ${USSD_CONTENT}' >> /var/log/asterisk/ussd.txt))
exten => ussd,n,Hangup()
; 来电转接:优化主叫名称显示,转发到FreePBX的from-trunk上下文
exten => _.,1,Set(CALLERID(name)=Mobile-${CALLERID(num)}) ; 增加前缀,便于区分来电来源
exten => _.,n,Verbose(2,Incoming call from ${CALLERID(num)}, forwarding to from-trunk)
exten => _.,n,Goto(from-trunk,${EXTEN},1)
exten => _.,n,Hangup()
[Outbound-1001]
; 呼出电话:增加容错+日志,失败时提示
exten => _.,1,Verbose(2,Outbound call to ${EXTEN} via quectel0)
; 拨号:增加超时(60秒)、失败重试1次
exten => _.,n,Dial(Quectel/quectel0/${EXTEN},60,r)
; 拨号失败时记录日志
exten => _.,n,ExecIf($["${DIALSTATUS}" != "ANSWER"]?System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - Outbound call to ${EXTEN} failed, status: ${DIALSTATUS}' >> /var/log/asterisk/quectel_outbound.log))
; 失败时播放提示音(可选,需确保提示音文件存在)
;exten => _.,n,Playback(cannot-complete-as-dialed)
exten => _.,n,Hangup()
# 给asterisk用户授权日志目录 chown -R asterisk:asterisk /var/log/asterisk chmod 755 /var/log/asterisk # 若启用tg bot的未读短信目录,需提前创建并授权 mkdir -p /var/log/asterisk/unread_sms chown asterisk:asterisk /var/log/asterisk/unread_sms
重载配置
asterisk dialplan reload # 重新加载拨号计划
语音和短信测试
asterisk 交互命令下的测试
先输入asterisk 进入交互模式
呼出测试:执行channel originate Local/10010@Outbound-1001 extension null@default 查看是否能呼叫 10010; 短信测试:执行quectel sms quectel0 10010 "101" 给 EC20 的号码发送短信,查看cat /var/log/asterisk/sms.txt是否有记录; 呼出测试:执行channel originate Local/10086@Outbound-1001 extension null@default 查看是否能呼叫 10086; 短信测试:执行quectel sms quectel0 10086 "CXYE" 给 EC20 的号码发送短信,查看cat /var/log/asterisk/sms.txt是否有记录;
外部命令测试
Asterisk CLI 收发短信测试
asterisk -rx 'quectel sms quectel0 10010 "101"' asterisk -rx 'quectel sms quectel0 10086 "CXYE"' tail -f /var/log/asterisk/sms.txt 2022-10-01 00:00:00 - quectel0 - 10010: 短信内容
# # 发起呼叫并等待接听(超时60秒) asterisk -rx'channel originate Local/10010@Outbound-1001 extension null@default' # 挂断通话 ? # 先查看通道 asterisk -rx'core show channels'
主要就是调用 Asterisk CLI 和监听/var/log/asterisk/sms.txt实现
五、FreePBX相关配置
配置trunk、分机、出入局路由,注意submit后需要点击右上角Apply Config 生效,否则会带来很多不必要的排查
一、 配置 FreePBX Trunks(对接 chan-quectel 驱动)
1. 登录 FreePBX 后台,进入 Connectivity → Trunks → Add Trunk → Add Custom Trunk。
2. Trunk Name:填写自定义名称,例如 Quectel_EC20_Trunk。
3. 切换到customer setting 其中 Custom Dial String:填写 Quectel/quectel0/$OUTNUM$(quectel0 需与 /etc/asterisk/quectel.conf 中配置的通道名一致)。
如果是华为板,则填dongle/dongle1/$OUTNUM$。
4. 其他参数保持默认,点击 Submit → Apply Config 保存应用配置。
1. 进入 Applications → Extensions → Add Extension → Add SIP Extension。
2. User Extension:填写分机号,例如 200;
4. Secret:设置分机密码(需符合复杂度要求,避免弱密码)。
5. 点击 Submit → Apply Config 保存,分机配置完成后可在 SIP 话机上注册测试。
1. 进入 Connectivity → Outbound Routes → Add Outbound Route。
2. Route Name:填写自定义名称,例如 Quectel_Outbound_Route。
4. Trunk Sequence for Matched Routes:选择第一步配置的 Quectel_EC20_Trunk。
4. 切换到Dial Patterns选项卡填写 Dial Patterns That Will Use This Route的 match pattern
◦ 添加号码匹配规则,例如 匹配 _XXXXXXXXXXX(11 位手机号)、_XXXXXXX(7 位固话),按需覆盖外呼号码格式。
ZX. 意思是拨出去的号码最少有3个数字那么长,且第一位不是0,那手机11位,起始是1就满足了。
5. 点击 Submit → Apply Config 保存。
1. 进入 Connectivity → Inbound Routes → Add Inbound Route。
2. Description:填写自定义名称,例如 Quectel_Inbound_Route。
4. Set Destination:选择 Extensions → [目标分机号](例如 200),实现来电振铃分机。
5. 点击 Submit → Apply Config 保存。
六、SIP客户端测试
1.windows软电话拨号测试
这里用zoiper
下载 Download Zoiper 5, a free VoIP softphone :: Zoiper
zoiper配置 免费版只支持一个帐号
username/login: 200@192.168.11.25 password:xxxx 你设置的分机密码 domain/sip server/registar/sip proxy:192.168.11.25
之后zoiper会检测4中类型
SIP TLS: PRO SIP TCP: Not found SIP UDP: Found IAX UDP: Not found
可以看到 SIP UDP成功了
2.ios客户端测试
sip客户端建议用 portsip:PortSIP UC App – App Store
port sip配置
用户名:200 密码:xxxx 你设置的分机密码 域名:192.168.11.25
默认设置情况下即可在锁屏界面接起电话
IAX2协议客户端建议用 skylar :Skylar App – App Store
skylar配置
Profile Name:220 Domain:192.168.11.21 Protocol:IAX Extension:200 Password:你的分机密码
IAX映射到公网比较简单,但是实测下来UDP极易被联通运营商qos,20秒一重置,断断续续的,移动电信网络无此问题
如果考虑全运营商,优先考虑openvxx,或者魔改frp映射SIP
短信转发测试
vi /etc/asterisk/extensions_custom.conf
在写入sms log后添加
; 调用 Python 2 脚本,参数顺序不变
exten => sms,n,System(/usr/local/bin/sms2wecom.py "${CALLERID(num)}" "${SMS_BASE64}" "${QUECTELNAME}" "${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)}")
编写脚本
vi /usr/local/bin/sms2wecom.py
内容如下
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import sys
import json
import urllib2
import base64
# 替换为你的企业微信机器人 WebHook 地址
WECOM_WEBHOOK = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的机器人key"
def send_wecom_msg(sender, content_base64, device, send_time):
try:
# 解码 Base64 内容(Python 2 先解码为字节流,再转 UTF-8 字符串)
content_bytes = base64.b64decode(content_base64)
content = content_bytes.decode('utf-8')
except Exception as e:
# 解码失败时记录错误,使用原编码内容
content = "Base64 解码失败: %s" % str(e)
# 构建消息体,使用解码后的内容
msg_data = {
"msgtype": "text",
"text": {
"content": u"【Quectel EC20 短信通知】\n"
u"时间:%s\n"
u"设备:%s\n"
u"发件人:%s\n"
u"内容:%s" % (send_time, device, sender, content)
}
}
# 处理中文编码(Python 2 关键)
json_data = json.dumps(msg_data, ensure_ascii=False).encode('utf-8')
headers = {"Content-Type": "application/json; charset=utf-8"}
try:
req = urllib2.Request(WECOM_WEBHOOK, data=json_data, headers=headers)
resp = urllib2.urlopen(req, timeout=10)
return resp.read()
except Exception as e:
# 记录错误日志到 /var/log/asterisk/sms_wecom_error.log
with open("/var/log/asterisk/sms_wecom_error.log", "a") as f:
log_content = "%s - Error: %s\n" % (send_time, str(e))
f.write(log_content.encode('utf-8')) # Python 2 写入需转字节
return None
if __name__ == "__main__":
# 接收拨号计划传递的 4 个参数
if len(sys.argv) != 5:
sys.exit(1)
sender = sys.argv[1].strip()
content_base64 = sys.argv[2].strip()
device = sys.argv[3].strip()
send_time = sys.argv[4].strip()
# 发送消息
send_wecom_msg(sender, content_base64, device, send_time)
权限处理
chmod +x /usr/local/bin/sms2wecom.py chown asterisk:asterisk /var/log/asterisk/sms_wecom_error.log 2>/dev/null
重载拨号计划
asterisk -rx "dialplan reload"
发一条查余额短信测试收信推送功能
asterisk -rx 'quectel sms quectel0 10086 "CXYE"'
分机调优
Max Contacts 2 Timer Expiration Period 3600 Minimum Expiration 60
响铃调优
设置响铃组
Applications—>Ring groups—>Add Ring Group
Group Description 填299
其他默认, submit —> apply config
修改之前的入局路由从200分机改成响铃组299
内线拨号优化
为了实现3位数内部分机互拨,修改出局路由,将原来的表达式改成XXX.
常见问题排查
分机注册失败排查
asterisk -rvvv # 查看所有 PJSIP 终端(应显示分机 200) pjsip show endpoints # 查看分机 200 详情 pjsip show endpoint 200 # 查看 aor 和 auth 配置 pjsip show aor 200 pjsip show auth auth200
可以尝试删除分机重建,常见的错误是没点右上角Apply Config(更新配置)。目前发现存在reboot命令重启丢失分机配置的问题,列表在但是注册不了,用排查命令也看不到信息。这种情况目前无解,只能删除分机重新添加。
freebpx 网页密码忘记重置
查看用户名密码 cat /etc/freepbx.conf <?php // This file was generated at 2026-01-08T23:44:10+00:00 $amp_conf["AMPDBUSER"] = "freepbxuser"; $amp_conf["AMPDBPASS"] = "l/jw8/LOYneC"; $amp_conf["AMPDBHOST"] = "localhost"; $amp_conf["AMPDBNAME"] = "asterisk"; $amp_conf["AMPDBENGINE"] = "mysql"; $amp_conf["datasource"] = ""; require_once "/var/www/html/admin/bootstrap.php"; mysql -u freepbxuser -p asterisk 输入 l/jw8/LOYneC 改成新密码NewPass123! UPDATE ampusers SET password_sha1 = '5c4f2778eafa38751fbd21fd4c1ef13ed9cfcff6' WHERE username = 'admin'; FLUSH PRIVILEGES; exit;
fail2ban封禁解封
# 查看所有 jail 状态 fail2ban-client status Status |- Number of jail:7 `- Jail list:apache-tcpwrapper, recidive, ssh-iptables, apache-badbots, pbx-gui, asterisk-iptables, vsftpd-iptables # 查看特定 jail(如 pbx-gui)的封禁 IP fail2ban-client status pbx-gui # 查看拨号失败的封禁 IP fail2ban-client status asterisk-iptables # 检查单个 IP 是否被封禁 fail2ban-client banned 192.168.1.100 解封单个ip # 格式:fail2ban-client set <JAIL> unbanip <IP> fail2ban-client set pbx-gui unbanip 192.168.1.100 fail2ban-client set asterisk-iptables unbanip 192.168.11.11
