You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:40:17