为什么会有这篇文章
刚开始一直在用SmsFordarder在安卓手机上进行短信转发,长期占用安卓手机,老化的电池也不放心,咸鱼发现有便宜可用的支持短信的4g模块,顺带研究一下相关的内容,就自己用golang写了相关服务。
我自己的硬件配置
主板 | orange pi5 max | 很不错的板子,我在上面跑frigate、jellyfin 能解码,能跑图像识别。 |
cpu | rk3588 | x86和arm64都可以 |
系统 | ubuntu server22.04 | 因为还在跑redroid,需要5.10内核,所有没用24.04系统 |
4g模块 | 移远的EC200T-CN | 咸鱼10块钱,cat4速率,但是我不需要用它上网,便宜,有短信,有通话接口就行。 |
usb转4g板子 | 咸鱼搜 mini pcie转4g模块 | 20块钱 |
天线 | 2根 | IPEX一代接口 |
sim卡信息 | 中国联通 | 没其他卡,没测试移动和电信 |
软件配置
- docker就行了。
- 相关代码:
- 我用golang写的一份读取短信和来电的服务gammu-web
- 配合golang的通知服务ForwardSMS
准备转发配置文件forward.yaml
# 如果有all这个配置,就是默认所有短信都会转发给这个企业微信机器人,建议发送给管理员,或者直接删除关闭
all:
rule: all
type: all
notify: wechat
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxx
# 从上到下依次为 项目名称、规则(使用关键字匹配)
测试:
rule: 测试DDD
type: keyword
notify: wechat
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxx
# 正则匹配规则
工单号:
rule: "\\d{8}" # 匹配8位数字
type: regex
notify: gotify
url: https://gotify.example.com
token: your_app_token
# 转发给ios用它。
bark:
notify: bark
type: all
rule: all
url: "https://api.day.app/xxxx"
# 转发给安卓用它。
gtify:
notify: gotify
type: all
rule: all
url: "https://gotify.example.com"
token: "your_app_token"
# emm邮箱转发
email:
notify: email
type: all
rule: all
smtp_host: "smtp.qq.com"
smtp_port: "587"
username: "[email protected]"
password: "授权码"
from: "[email protected]"
to: "[email protected]"
配置docker-compose.yaml (支持x86和arm64)
version: "3"
services:
# https://github.com/scjtqs2/ForwardSMS
forwardsms:
image: ghcr.io/scjtqs2/forwardsms/forwardsms:latest
environment:
- FORWARD_SECRET=your_shared_secret_here
- TZ=Asia/Shanghai
- HTTP_PORT=8080
volumes:
# 挂载上面配置的推送文件
- ./forward.yaml:/data/config/forward.yaml
restart: always
expose:
- 8080
# https://github.com/scjtqs2/gammu-web
gammu-web:
image: ghcr.io/scjtqs2/gammu-web:latest
environment:
- FORWARD_ENABLED=1
# 写死
- FORWARD_URL=http://forwardsms:8080/api/v1/sms/receive
# 和forwardsms里面的要一致。
- FORWARD_SECRET=your_shared_secret_here
- TZ=Asia/Shanghai
# 转发通知携带的标识。
- PHONE_ID=SMS1_186xxxxxxxx
- ATCONNECTION=at9600
# ec200t-cn 默认的管理接口就是/dev/ttyUSB1(能同时收发短信,还能读取来电显示数据用来达到转发来电通知)。根据实际情况自行调整。
- USB_PORT=/dev/ttyUSB1
- API_PORT=21234
# web网页访问用到。
- API_TOKEN=your_shared_token_here
- GAMMU_DEBUG=false
- DEBUG=0
- CALL_FORWARD_ENABLED=true
- CALL_FORWARD_URL=http://forwardsms:8080/api/v1/call/receive
volumes:
- ./data:/data
# 加了这个,就不用挨个去映射 /dev/ttyUSBx了
privileged: true
restart: always
ports:
- 21234:21234
FAQ
1. gammu-web自带一个web页面,可以在上面管理你的短信,登陆的时候,记得勾选“记住”,遇到无线跳转的话手动清理当前页面cookie。
2. 如果遇到端口被占用的情况,需要手动关闭默认的调制解调器。并通过netplan关闭usb 4g网卡。
# 停止所有可能的调制解调器管理服务
sudo systemctl stop ModemManager
sudo systemctl disable ModemManager
sudo pkill -f ModemManager
3. 重启模块命令:echo -e "AT+CFUN=1,1\r" > /dev/ttyUSB2
4. 调试建议使用apt install minicom
,然后用
minicom -D /dev/ttyUSB2
(如果你的 /dev/ttyUSB2已经被gammu-web占用了,用/dev/ttyUSB1也可以查询部分数据)连接。
退出的话,按下ctrl+a
,放开键盘再按x
,点击确定。
使用ATI
来查看模块信息。
然后输入 AT+COPS?
和 AT+QNWINFO
查询是否已经注册到运营商网络,大概会返回如下信息
5. 如果不喜欢模块自带的cdc网络连接,我这有脚本关掉它。disable_cdc.sh
:
#!/bin/bash
# 自动禁用CDC Ethernet接口脚本
# 文件名: disable_cdc.sh
set -e
echo "=== 正在扫描CDC Ethernet接口 ==="
# 检查cdc_ether驱动目录是否存在
if [ ! -d "/sys/bus/usb/drivers/cdc_ether" ]; then
echo "❌ cdc_ether驱动目录不存在,可能没有CDC设备连接"
exit 1
fi
# 获取所有绑定到cdc_ether的设备
devices=(ls /sys/bus/usb/drivers/cdc_ether/ | grep -E '^[0-9]+-[0-9]+:[0-9]+\.[0-9]+' 2>/dev/null || true)
if [ -z "devices" ]; then
echo "ℹ️ 没有找到绑定到cdc_ether的设备"
exit 0
fi
echo "找到以下CDC Ethernet设备:"
echo "devices"
# 禁用每个设备
disabled_count=0
for device in devices; do
echo "🔄 正在禁用设备:device"
if echo "device" | sudo tee /sys/bus/usb/drivers/cdc_ether/unbind>/dev/null 2>&1; then
echo "✅ 成功禁用:device"
((disabled_count++))
else
echo "❌ 禁用失败: device"
fi
done
echo "=== 完成 ==="
echo "共禁用了disabled_count 个CDC Ethernet接口"
# 验证结果
remaining_devices=(ls /sys/bus/usb/drivers/cdc_ether/ | grep -E '^[0-9]+-[0-9]+:[0-9]+\.[0-9]+' 2>/dev/null | wc -l)
if [ "remaining_devices" -eq 0 ]; then
echo "🎉 所有CDC Ethernet接口已成功禁用"
else
echo "⚠️ 仍有remaining_devices 个接口未禁用"
fi
6. 对应的恢复脚本enable_cdc.sh
:
#!/bin/bash
set -euo pipefail
DRIVER_DIR="/sys/bus/usb/drivers/cdc_ether"
echo "== 恢复 CDC Ethernet (cdc_ether) =="
# 确保驱动目录存在(若模块没加载,尝试 modprobe)
if [ ! -d "DRIVER_DIR" ]; then
echo "cdc_ether 驱动目录不存在,尝试加载模块..."
sudo modprobe cdc_ether || true
sleep 1
if [ ! -d "DRIVER_DIR" ]; then
echo "❌ cdc_ether 驱动仍不可用,退出。"
exit 1
fi
fi
bound=0
for iface in /sys/bus/usb/devices/*:*; do
[ -d "iface" ] || continue
name=(basename "iface")
# 读取接口类(支持 '2'、'02'、'0x02')
cls=(cat "iface/bInterfaceClass" 2>/dev/null || true)
case "cls" in
2|02|0x02) ;;
*) continue ;; # 不是通信/网络类,跳过
esac
if [ -e "iface/driver" ]; then
echo "ℹ️ 接口name 已绑定驱动,跳过。"
continue
fi
echo "🔧 尝试绑定接口 name 到 cdc_ether..."
if sudo sh -c "echo -n 'name' > DRIVER_DIR/bind"; then
echo "✅ 绑定成功:name"
bound=((bound+1))
# 等待 sysfs 创建 net 子目录再尝试 up
sleep 1
netdir="iface/net"
if [ -d "netdir" ]; then
for ifn in "netdir"/*; do
ifn=(basename "ifn")
echo "⤴️ 设置网络接口 ifn up"
sudo ip link set "ifn" up || true
done
fi
else
echo "❌ 绑定失败: name"
fi
done
if [ "bound" -eq 0 ]; then
echo "⚠️ 未找到可绑定的 CDC 接口(或绑定失败)。请检查 lsusb/dmesg。"
else
echo "🎉 共绑定 $bound 个接口。"
fi