在物聯網、警報通知、驗證碼等場景,透過 GSM/3G/4G 短信貓(Modem) 經串口(COM/USB 轉串口)發送短信是經典方案。SMSLib v3 是 Java 領域最成熟的短信庫,支援 AT 指令封裝、PDU/文本模式、多 Gateway 管理、異步事件通知。本範例完整展示 SerialModemGateway 配置、發送中文短信(UCS2 編碼)、接收短信、來電通知三大核心功能。
public class SmsModem {
private SerialModemGateway gateway;
private Service srv; // SMSLib 核心服務
private OutboundNotification outboundNotification;
private InboundNotification inboundNotification;
private CallNotification callNotification;
// ... 單例模式 getInstant()
}
public SmsModem() {
try {
// 1. 讀取配置(ResourceBundle 讀取 sms.properties)
ResourceBundle rb = ResourceBundle.getBundle("sms");
String portName = "COM10"; // 預設
int baudRate = 9600;
if (rb != null) {
portName = rb.getString("smsport");
baudRate = Integer.parseInt(rb.getString("smsbolv"));
}
// 2. 建立 SerialModemGateway
// 參數:gatewayId、portName、baudRate、製造商、型號
gateway = new SerialModemGateway("modem." + portName, portName, baudRate,
"wavecom", "17254"); // 製造商/型號用於 AT+CGMI/AT+CGMM 識別
} catch (Exception e) {
LOG.error("Gateway 初始化失敗", e);
}
}
private void ensureServiceStarted() throws Exception {
if (srv == null) {
srv = new Service();
srv.S.SERIAL_POLLING = true; // 啟用串口輪詢
gateway.setInbound(true); // 啟用接收
gateway.setOutbound(true); // 啟用發送
gateway.setSimPin("0000"); // SIM PIN 碼(若無鎖可空)
// 3. 註冊三大回調
gateway.setOutboundNotification(outboundNotification); // 發送狀態報告
gateway.setInboundNotification(inboundNotification); // 收到短信
gateway.setCallNotification(callNotification); // 來電
srv.addGateway(gateway);
srv.startService(); // 關鍵:開啟串口、啟動輪詢執行緒
}
}
public void doIt(String phone, String content) throws Exception {
ensureServiceStarted();
// 印出 Modem 資訊(診斷用)
LOG.info("Manufacturer: " + gateway.getManufacturer());
LOG.info("Model: " + gateway.getModel());
LOG.info("Signal: " + gateway.getSignalLevel() + "%");
// 4. 建立發送訊息
OutboundMessage msg = new OutboundMessage(phone, content);
msg.setEncoding(Message.MessageEncodings.ENCUCS2); // 關鍵!中文必須 UCS2 (UTF-16BE)
// 若不設定,預設 GSM 7-bit,中文會亂碼或截斷
// 5. 同步發送
srv.sendMessage(msg);
}
public class OutboundNotification implements IOutboundMessageNotification {
public void process(String gatewayId, OutboundMessage msg) {
LOG.info("發送狀態回報 - Gateway: " + gatewayId);
LOG.info("狀態: " + msg.getStatus() + " | 參考號: " + msg.getRefNo());
}
}
public class InboundNotification implements IInboundMessageNotification {
public void process(String gatewayId, Message.MessageTypes msgType, InboundMessage msg) {
if (msgType == Message.MessageTypes.INBOUND) {
LOG.info("收到新短信 - 來自: " + msg.getOriginator());
LOG.info("內容: " + msg.getText());
LOG.info("時間: " + msg.getDate());
// 可選:自動刪除已讀短信釋放 SIM 空間
// srv.deleteMessage(msg);
} else if (msgType == Message.MessageTypes.STATUSREPORT) {
LOG.info("收到狀態報告: " + msg.getStatus());
}
}
}
public class CallNotification implements ICallNotification {
public void process(String gatewayId, String callerId) {
LOG.info("來電 - Gateway: " + gatewayId + " | 主叫: " + callerId);
// 可觸發業務邏輯:彈窗、錄音、掛斷等
}
}
public String readMessage() {
StringBuilder sb = new StringBuilder();
try {
ensureServiceStarted();
List<InboundMessage> msgList = new ArrayList<>();
srv.readMessages(msgList, InboundMessage.MessageClasses.ALL); // ALL/UNREAD/READ/SENT
int i = 1;
for (InboundMessage msg : msgList) {
sb.append("第").append(i).append("條;發件人:")
.append(msg.getOriginator()).append(";內容:")
.append(msg.getText()).append("\n");
i++;
}
} catch (Exception e) {
sb.append("讀取失敗: ").append(e.getMessage());
}
return sb.toString();
}
# 串口號(Windows: COMx, Linux: /dev/ttyUSBx)
smsport=COM10
# 波特率(常見:9600, 115200, 460800)
smsbolv=9600
| 步驟 | AT 指令 | 預期回應 |
|---|---|---|
| 基本通訊 | AT | OK |
| 查詢製造商 | AT+CGMI | Wavecom / Huawei / SIMCom 等 |
| 查詢型號 | AT+CGMM | 具體型號 |
| 查詢訊號 | AT+CSQ | +CSQ: 15,99 (0-31, 越大越好) |
| 查詢 SIM 狀態 | AT+CPIN? | +CPIN: READY |
| 設定 PDU 模式 | AT+CMGF=0 | OK |
| 設定短信中心號碼 | AT+CSCA="+861380xxxxxxx" | OK |
| 發送測試 | AT+CMGS=... (PDU 封裝) | +CMGS: <mr> |
msg.setEncoding(ENCUCS2),且確認 Modem 支援 UCS2(絕大多數現代模組支援)。sudo usermod -a -G dialout $USER 或 chmod 666 /dev/ttyUSB0。AT+IPR=9600 固定。gateway.setSimPin("1234") 或 AT+CPIN="1234" 解鎖。AT+CSCA,否則發送失敗無錯誤碼。org.smslib:smslib:3.5.0。Service.addGateway() 多 Gateway 輪詢。jSerialComm / PureJavaComm 直接發送 AT 指令,輕量無依賴,適合簡單場景。SmsManager 發送。SMSLib 提供企業級的 Modem 短信抽象層:Service 管理多 Gateway、SerialModemGateway 封裝 AT 指令、三大回調實現異步事件驅動。核心要點:**UCS2 編碼發送中文、正確配置串口/波特率/短信中心、啟用輪詢接收**。對於新專案,優先評估雲短信 API;若必須自建硬體,SMSLib 仍是 Java 生態最佳選擇。