如何在C# .NET中为GSM调制解调器的AT通信配置串口
问题分析与解决方案
背景梳理
你遇到的问题很典型:用C#代码连接三星Galaxy S5的COM口能正常发SMS,但切换到GSM调制解调器后就失效了——不过终端工具能正常发送,且终端里启用了DTR和RTS,自己调整了串口配置还是没解决。核心问题应该出在串口参数匹配或AT指令交互的时序/细节上,毕竟终端能工作就说明硬件和AT指令本身是没问题的。
下面是具体的排查和调整建议:
1. 先让串口参数和终端完全对齐
终端里能正常工作,那代码里的串口配置必须和终端完全一致,尤其是你提到的DTR/RTS:
- 你之前只开了
DtrEnable = true,但终端里标记了RTS,所以一定要加上RtsEnable = true - 流控制模式也要和终端匹配,如果终端用的是硬件流控,把
Handshake改成Handshake.RequestToSend,而不是None
调整后的串口初始化代码:
_serialPort = new SerialPort("COM3", 9600) { Parity = Parity.None, DataBits = 8, StopBits = StopBits.One, Handshake = Handshake.None, // 终端用硬件流控的话改成Handshake.RequestToSend DtrEnable = true, RtsEnable = true, // 必须加上,和终端配置对齐 ReadTimeout = 2000, // 建议加上超时,避免无限等待 WriteTimeout = 2000 };
2. 替换固定Sleep,改成等待调制解调器响应
你的代码里用Thread.Sleep(200)来等待响应,但不同调制解调器的响应速度差异很大,固定延时很容易导致指令发送时机不对。正确的做法是发送指令后,等待调制解调器返回指定响应(比如OK、>)再执行下一步。
修改后的完整代码,增加了响应验证逻辑:
using System; using System.IO.Ports; using System.Text; using System.Threading; public class AtSmsSender { private SerialPort _serialPort; public void SmsSend(string phoneNr, string messageText) { string portName = "COM3"; int portBaudRate = 9600; _serialPort = new SerialPort(portName, portBaudRate) { Parity = Parity.None, DataBits = 8, StopBits = StopBits.One, Handshake = Handshake.None, DtrEnable = true, RtsEnable = true, ReadTimeout = 2000, WriteTimeout = 2000 }; try { _serialPort.Open(); // 清空缓冲区,避免旧数据干扰交互 _serialPort.DiscardInBuffer(); _serialPort.DiscardOutBuffer(); // 先发送AT指令确认连接正常 SendAtCommand("AT\r", "OK"); // 设置短信为文本模式 SendAtCommand("AT+CMGF=1\r", "OK"); // 发送目标号码,等待调制解调器返回">" SendAtCommand($"AT+CMGS=\"+{phoneNr}\"\r", ">"); // 发送短信内容+结束符(Ctrl+Z) _serialPort.Write(messageText + "\x1A"); // 等待发送结果 string finalResponse = ReadResponse(); if (finalResponse.Contains("OK")) { Console.WriteLine("短信发送成功!"); } else { Console.WriteLine($"发送失败,调制解调器响应:{finalResponse}"); } } catch (Exception ex) { Console.WriteLine($"执行出错:{ex.Message}"); } finally { if (_serialPort.IsOpen) _serialPort.Close(); } } // 封装发送AT指令并等待指定响应的方法 private void SendAtCommand(string command, string expectedResponse) { _serialPort.Write(command); string response = ReadResponse(); if (!response.Contains(expectedResponse)) { throw new InvalidOperationException($"指令执行失败:{command}\n响应内容:{response}"); } } // 读取调制解调器的完整响应 private string ReadResponse() { StringBuilder response = new StringBuilder(); // 循环读取直到没有数据 while (_serialPort.BytesToRead > 0) { response.Append(_serialPort.ReadExisting()); Thread.Sleep(100); // 给点时间让调制解调器发送完所有数据 } return response.ToString(); } }
3. 额外排查点
- 检查短信中心号码:有些调制解调器需要手动设置短信中心,发送
AT+CSCA?查看,不对的话用AT+CSCA="+你的运营商短信中心号码"设置 - 字符编码问题:如果发送含中文或特殊字符的短信,可能需要设置编码指令,比如
AT+CSMP=17,167,0,8(针对UCS2编码) - 调试日志:把发送的指令和收到的响应都打印出来,和终端里的交互流程对比,就能快速找到差异点
内容的提问来源于stack exchange,提问作者Daniel Žeimo
相关产品推荐
相关产品推荐

