【问题标题】:SMS connecting to a phone from C# and getting a response从 C# 连接到手机并获得响应的短信
【发布时间】:2010-06-11 23:10:32
【问题描述】:

我编写了代码以使用通过 COM 端口连接到计算机的 GSM 手机发送短信。代码如下。

问题是我确实看到它在手机的发件箱中,它实际上似乎已经发送,但是当我联系收件人时,他们说我没有收到邮件。

我测试了手机,我只使用手机创建和发送消息,它运行良好。但是,当我使用我的代码执行此操作时,它似乎已发送,并且我从手机获得了所有正确的 AT COMMAND 响应,但实际上并未发送消息。

代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO.Ports;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        SerialPort serialPort1;
        int m_iTxtMsgState = 0;
        const int NUM_MESSAGE_STATES = 4;
        const string RESERVED_COM_1 = "COM1";
        const string RESERVED_COM_4 = "COM4";

        public Form1()
        {
            InitializeComponent();

            this.Closing += new CancelEventHandler(Form1_Closing);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            serialPort1 = new SerialPort(GetUSBComPort());

            if (serialPort1.IsOpen)
            {
                serialPort1.Close();
            }

            serialPort1.Open();

            //ThreadStart myThreadDelegate = new ThreadStart(ReceiveAndOutput);
            //Thread myThread = new Thread(myThreadDelegate);
            //myThread.Start();

            this.serialPort1.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
        }

        private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            serialPort1.Close();
        }

        private void SendLine(string sLine)
        {
            serialPort1.Write(sLine);
            sLine = sLine.Replace("\u001A", "");
            consoleOut.Text += sLine;
        }

        public void DoWork()
        {
            ProcessMessageState();
        }

        public void ProcessMessageState()
        {
            switch (m_iTxtMsgState)
            {
                case 0:
                    m_iTxtMsgState = 1;
                    SendLine("AT\r\n");  //NOTE: SendLine must be the last thing called in all of these!
                break;

                case 1:
                    m_iTxtMsgState = 2;
                    SendLine("AT+CMGF=1\r\n");

                break;

                case 2:
                    m_iTxtMsgState = 3;
                    SendLine("AT+CMGW=" + Convert.ToChar(34) + "+9737387467" + Convert.ToChar(34) + "\r\n");
                break;

                case 3:
                    m_iTxtMsgState = 4;
                    SendLine("A simple demo of SMS text messaging." + Convert.ToChar(26));
                break;

                case 4:
                    m_iTxtMsgState = 5;

                break;

                case 5:
                    m_iTxtMsgState = NUM_MESSAGE_STATES;
                break;
            }
        }

        private string GetStoredSMSID()
        {
            return null;
        }

        /* //I don't think this part does anything
        private void serialPort1_DataReceived_1(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string response = serialPort1.ReadLine();
            this.BeginInvoke(new MethodInvoker(() => textBox1.AppendText(response + "\r\n")));
        }
        */
        void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                Thread.Sleep(500);

                char[] msg;
                msg = new char[613];
                int iNumToRead = serialPort1.BytesToRead;

                serialPort1.Read(msg, 0, iNumToRead);

                string response = new string(msg);

                this.BeginInvoke(new MethodInvoker(() => textBox1.AppendText(response + "\r\n")));
                serialPort1.DiscardInBuffer();

                if (m_iTxtMsgState == 4)
                {
                    int pos_cmgw = response.IndexOf("+CMGW:");
                    string cmgw_num = response.Substring(pos_cmgw + 7, 4);
                    SendLine("AT+CMSS=" + cmgw_num + "\r\n");
                    //stop listening to messages received
                }

                if (m_iTxtMsgState < NUM_MESSAGE_STATES)
                {
                    ProcessMessageState();
                }
            }
            catch
            { }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            m_iTxtMsgState = 0;
            DoWork();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            string[] sPorts = SerialPort.GetPortNames();
            foreach (string port in sPorts)
            {
                consoleOut.Text += port + "\r\n";
            }
        }

        private string GetUSBComPort()
        {
            string[] sPorts = SerialPort.GetPortNames();
            foreach (string port in sPorts)
            {
                if (port != RESERVED_COM_1
                    && port != RESERVED_COM_4)
                {
                    return port;
                }
            }

            return null;
        }
    }

【问题讨论】:

  • 你说"i dont know which COM to connect to.",然后你说" in hyperterminal you see responses right away"所以,你知道如何连接,COM号是什么,或者你永远不会通过HT连接它。顺便说一句,您需要在处理代码之前关闭所有 COM (HT),因为 COM 端口已经打开,在关闭第一个连接之前您将无法再次打开它...

标签: c# excel vba sms


【解决方案1】:

这是我在 U9 Telia 蜂窝调制解调器上工作(测试)的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Configuration;

using UsbEject.Library;
using Utils;

namespace Hardware
{
    public class TeliaModem : IDisposable
    {
        public delegate void NewSmsHandler(InboundSMS sms);
        public event NewSmsHandler OnNewSMS;

        #region private data
        System.IO.Ports.SerialPort modemPort;

        Timeouter _lastModemKeepAlive;

        private delegate void DataReceivedDelegate();
        private DataReceivedDelegate dataReceivedDelegate;

        Queue<OutboundSMS> _outSmses = new Queue<OutboundSMS>();

        enum ModemState
        {
            Error = -1, NotInitialized, PowerUp, Initializing, Idle,
            SendingSMS, KeepAliveAwaitingResponse
        };

        Timeouter ModemStateTimeouter = new Timeouter(timeout_s: 10, autoReset: false);
        ModemState _modemState = ModemState.NotInitialized;
        ModemState CurrentModemState
        {
            get
            {
                return _modemState;
            }
            set
            {
                _modemState = value;
                ModemStateTimeouter.Reset();
                NihLog.Write(NihLog.Level.Debug, "State changed to: " + value.ToString());
            }
        }

        private System.Windows.Forms.Control _mainThreadOwnder;
        #endregion

        public TeliaModem(System.Windows.Forms.Control mainThreadOwnder)
        {
            _mainThreadOwnder = mainThreadOwnder;

            dataReceivedDelegate = new DataReceivedDelegate(OnDataReceived);
        }

        public void SendSMS(string phone, string text)
        {
            _outSmses.Enqueue(new OutboundSMS(phone, text));
            HeartBeat();
        }

        private void SendSmsNow()
        {
            OutboundSMS sms = _outSmses.Peek();
            sms.Attempt++;
            if (sms.Attempt > sms.MaxTries)
            {
                NihLog.Write(NihLog.Level.Error, "Failure to send after " + sms.MaxTries + " tries");
                _outSmses.Dequeue();
                return;
            }

            NihLog.Write(NihLog.Level.Info, "Sending SMS: " + sms.ToString());
            WriteToModem("AT+CMGS=\"" + sms.Destination + "\"\r");
            System.Threading.Thread.Sleep(500);
            WriteToModem(sms.Text);

            byte[] buffer = new byte[1];
            buffer[0] = 26; // ^Z
            modemPort.Write(buffer, offset:0, count:1);

            CurrentModemState = ModemState.SendingSMS;
        }

        public void Dispose()
        {
            UninitModem();
        }

        public void HeartBeat()
        {
            if (CurrentModemState == ModemState.NotInitialized)
            {
                TryInitModem();
                return;
            }

            if (IsTransitionalState(CurrentModemState) && ModemStateTimeouter.IsTimedOut())
            {
                NihLog.Write(NihLog.Level.Error, "Modem error. Timed out during " + CurrentModemState);
                CurrentModemState = ModemState.Error;
                return;
            }

            if (CurrentModemState == ModemState.Idle && _lastModemKeepAlive.IsTimedOut())
            {
                // Send keepalive
                WriteToModem("AT\r");
                CurrentModemState = ModemState.KeepAliveAwaitingResponse;
                return;
            }

            if (CurrentModemState == ModemState.Error)
            {
                NihLog.Write(NihLog.Level.Debug, "Reenumerating modem...");
                UninitModem();
                return;
            }

            if (_outSmses.Count != 0 && CurrentModemState == ModemState.Idle)
            {
                SendSmsNow();
                return;
            }
        }

        private string pendingData;
        private void OnDataReceived()
        {
            // Called in the main thread

            string nowWhat = modemPort.ReadExisting();
            pendingData += nowWhat;
            string[] lines = pendingData.Split(new string[] { "\r\n" }, StringSplitOptions.None);
            if (lines.Length == 0)
            {
                pendingData = string.Empty;
                return;
            }
            else
            {
                pendingData = lines[lines.Length - 1];
            }

            // This happens in main thread.
            for (int i = 0; i < lines.Length - 1; i++)
            {
                string line = lines[i];

                if (line.Length >= 5 && line.Substring(0, 5) == "+CMT:")
                {
                    // s+= read one more line
                    if (i == lines.Length - 1) // no next line
                    {
                        pendingData = line + "\r\n" + pendingData; // unread the line
                        continue;
                    }
                    string line2 = lines[++i];
                    NihLog.Write(NihLog.Level.Debug, "RX " + line);
                    NihLog.Write(NihLog.Level.Debug, "RX " + line2);
                    InboundSMS sms = new InboundSMS();
                    sms.ParseCMT(line, line2);
                    if(OnNewSMS != null)
                        OnNewSMS(sms);
                }
                else   // Not a composite
                    NihLog.Write(NihLog.Level.Debug, "RX " + line);

                if (line == "OK")
                {
                    OnModemResponse(true);
                }
                else if (line == "ERROR")
                {
                    OnModemResponse(false);
                    NihLog.Write(NihLog.Level.Error, "Modem error");
                }
            }
        }

        private void ProcessSmsResult(bool ok)
        {
            if (!ok)
            {
                OutboundSMS sms = _outSmses.Peek();
                if (sms.Attempt < sms.MaxTries)
                {
                    NihLog.Write(NihLog.Level.Info, "Retrying sms...");
                    return;
                }

                NihLog.Write(NihLog.Level.Error, "Failed to send SMS: " + sms.ToString());
            }

            _outSmses.Dequeue();
        }

        private void OnModemResponse(bool ok)
        {
            if (CurrentModemState == ModemState.SendingSMS)
                ProcessSmsResult(ok);

            if (!ok)
            {
                NihLog.Write(NihLog.Level.Error, "Error during state " + CurrentModemState.ToString());
                CurrentModemState = ModemState.Error;
                return;
            }

            switch (CurrentModemState)
            {
                case ModemState.NotInitialized:
                    return;

                case ModemState.PowerUp:
                    WriteToModem("ATE0;+CMGF=1;+CSCS=\"IRA\";+CNMI=1,2\r");
                    CurrentModemState = ModemState.Initializing;
                    break;

                case ModemState.Initializing:
                case ModemState.SendingSMS:
                case ModemState.KeepAliveAwaitingResponse:
                    CurrentModemState = ModemState.Idle;
                    break;
            }
        }

        private void CloseU9TelitNativeApp()
        {
            bool doneSomething;

            do
            {
                doneSomething = false;

                Process[] processes = Process.GetProcessesByName("wirelesscard");
                foreach (Process p in processes)
                {
                    p.CloseMainWindow();
                    doneSomething = true;
                    NihLog.Write(NihLog.Level.Info, "Killed native U9 app");
                    System.Threading.Thread.Sleep(1000); // Will not wait if no native app is started
                }
            } while (doneSomething);
        }

        void WriteToModem(string s)
        {
            modemPort.Write(s);
            NihLog.Write(NihLog.Level.Debug, "TX " + s);
        }

        void UninitModem()
        {
            if (modemPort != null)
                modemPort.Dispose();
            modemPort = null;
            CurrentModemState = ModemState.NotInitialized;
        }

        private bool IsTransitionalState(ModemState ms)
        {
            return ms == ModemState.Initializing
                || ms == ModemState.SendingSMS
                || ms == ModemState.PowerUp
                || ms == ModemState.KeepAliveAwaitingResponse;
        }

        Timeouter _initFailureTimeout = 
            new Timeouter(timeout_s: 10, autoReset:false, timedOut:true);

        void TryInitModem()
        {
            // Try pistoning the modem with higher frequency. This does no harm (such as redundant logging)
            PrepareU9Modem();   // Will do nothing if modem is okay

            if (!_initFailureTimeout.IsTimedOut())
                return; // Don't try too frequently

            if (modemPort != null)
                return;

            const string modemPortName = "Basecom HS-USB NMEA 9000";
            const int speed = 115200;

            string portName = Hardware.Misc.SerialPortFromFriendlyName(modemPortName);
            if (portName == null)
            {
                NihLog.Write(NihLog.Level.Error, "Modem not found (yet). ");
                _initFailureTimeout.Reset();
                return;
            }

            NihLog.Write(NihLog.Level.Info, string.Format("Found modem port \"{0}\" at {1}", modemPortName, portName));
            modemPort = new System.IO.Ports.SerialPort(portName, speed);
            modemPort.ReadTimeout = 3000;
            modemPort.NewLine = "\r\n";

            modemPort.Open();

            modemPort.DiscardInBuffer();
            modemPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(delegate { _mainThreadOwnder.Invoke(dataReceivedDelegate); });  // called in different thread!

            _lastModemKeepAlive = new Timeouter(60, true);

            WriteToModem("AT+CFUN=1\r");
            CurrentModemState = ModemState.PowerUp;
        }

        void CheatU9Telit()
        {
            // U9 telit appears as USB CDrom on the bus, until disk eject is sent.
            // Then, it reappears as normal stuff.

            VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass();
            foreach (Volume device in volumeDeviceClass.Devices)
            {
                if (device.FriendlyName == "PCL HSUPA Modem USB Device")
                {
                    NihLog.Write(NihLog.Level.Info, "Trying to initialize: " + device.FriendlyName);
                    device.EjectCDRomNoUI();
                }
            }
        }

        void PrepareU9Modem()
        {
            CloseU9TelitNativeApp(); // Closes the autorun native app
            CheatU9Telit();
        }
    }

    public class OutboundSMS
    {
        public string Destination;
        public string Text;
        public int MaxTries;
        public int Attempt = 0;

        public OutboundSMS(string dest, string txt)
        {
            Destination = dest;
            Text = txt;
            MaxTries = 3;
        }

        override public string ToString()
        {
            if(Attempt > 1)
                return string.Format("\"{0}\" to {1}, attempt {2}", 
                    Text, Destination, Attempt);
            else
                return string.Format("\"{0}\" to {1}",
                    Text, Destination);
        }
    }

    public class InboundSMS
    {
        public string SourcePhone;
        public DateTime ReceiveTime;
        public string Text;

        public void ParseCMT(string line1, string line2)
        {
            string[] fields = line1.Split(new char[] { ',', ' ', '/', ':', '"' });
            if (fields.Length < 12)
                throw new ApplicationException("CMT message too short. Expected 2 fields");

            SourcePhone = fields[3];
            ReceiveTime = DateTime.Parse("20" + fields[7] + "-" + fields[8] + "-" + fields[9] + " " + fields[10] + ":" + fields[11] + ":" + fields[12].Substring(0, 2));   //test carefully
            Text = line2;
        }
    };
}

winforms 应用程序中的使用示例:

Hardware.TeliaModem _modem;

public Form1()
{
    InitializeComponent();

    _modem = new Hardware.TeliaModem(this);

    _modem.OnNewSMS += new Hardware.TeliaModem.NewSmsHandler(ProcessNewSMS);

        _timer.Interval = 1000; // milliseconds
        _timer.Tick += new EventHandler(OnTimerTick);
        _timer.Start();
}

发送短信:

        _modem.SendSMS(sms.SourcePhone, "Aircon is now " + BoolToString.ON_OFF(_aircon.On));

定时器事件,每秒一次:

private void OnTimerTick(object o, EventArgs e)
{
    _modem.HeartBeat();
}

这被注册为 Winforms 计时器处理程序,因此调制解调器将没有线程。

使用的一些实用程序类:

namespace Utils
{
    public class StopWatch
    {
        protected DateTime _resetTime;

        public StopWatch() { Reset(); }
        public void Reset() { _resetTime = DateTime.Now; }
        public double TotalSeconds { get { return (DateTime.Now - _resetTime).TotalSeconds; } }
        public TimeSpan Time { get { return DateTime.Now - _resetTime; } }
    };

    public class Timeouter : StopWatch
    {
        private bool _autoReset;
        private double _timeout_s;

        public Timeouter(double timeout_s, bool autoReset, bool timedOut = false)
        {
            _timeout_s = timeout_s;
            _autoReset = autoReset;

            if (timedOut)
                _resetTime -= TimeSpan.FromSeconds(_timeout_s + 1);  // This is surely timed out, as requested
        }

        public bool IsTimedOut()
        {
            if (_timeout_s == double.PositiveInfinity)
                return false;

            bool timedout = this.TotalSeconds >= _timeout_s;

            if (timedout && _autoReset)
                Reset();

            return timedout;
        }

        public void Reset(double timeout_s) { _timeout_s = timeout_s; Reset(); }
        public double TimeLeft { get { return _timeout_s - TotalSeconds; } }
    }
}

我实现这个是为了通过短信打开空调(是的,我住在一个炎热的国家)。有用。请随意使用任何此代码。

享受吧。

【讨论】:

  • 下载并编译,如果你愿意的话:dl.dropbox.com/u/616350/out/aircon%20r75.rar(完整的 VS2010 解决方案在 c# 中)
  • spasibo bolshoe,你认为这适用于我的手机吗?它通过 USB 连接
  • 我认为应该.. AT 命令应该是一样的。如果没有,我很想知道原因,并修复代码。
  • 嘿帕维尔,你似乎是 stackoverflow 上唯一能够回答我问题的人:stackoverflow.com/questions/3057301/…
  • da ya uverin shto tvoi otvet pravilnii,我只是在等待更多输入
【解决方案2】:

Microsoft 在知识库文章“How to access serial and parallel ports by using Visual Basic .NET”中提供了一个相当不错的 VB.Net 示例。我不确定您的手机返回什么,但是,如果您更改示例中的缓冲区循环,您至少可以打印出回复:

MSComm1.Output = "AT" & Chr(13)
Console.WriteLine("Send the attention command to the modem.")
Console.WriteLine("Wait for the data to come back to the serial port...")
' Make sure that the modem responds with "OK".
' Wait for the data to come back to the serial port.
Do
    Buffer = Buffer & MSComm1.Input
    Console.WriteLine("Found: {0}", Buffer)
Loop Until InStr(Buffer, "OK" & vbCrLf)

【讨论】:

    【解决方案3】:

    GSMComm 是一个 C# 库,可让您轻松地以文本或 PDU 模式与 GSM 手机进行交互。 (该网站还有很多测试工具)

    【讨论】:

      【解决方案4】:

      使用 GSM 调制解调器发送 SMS 从来都不是一个好主意。这是一个清单,可确保您的 SMS 正确发送。

      • 您正在以文本模式发送 SMS。尝试以PDU模式发送。
      • 试试这个库http://phonemanager.codeplex.com/
      • 您是否在发送 SMS 之前更改了 SMSC 号码,如果更改,请确保 SMSC 正确。
      • 还要确保您的SIM 卡有足够的信用额度,以便您发送出站短信。
      • 检查网络状态,尤其是当您从您的程序发送 SMS 时。这可能是个问题。

      我建议您查看免费提供的 Lite edition of NowSMS gateway,让您可以在大陆上使用 GSM 调制解调器。 NowSMS 将通过 GSM 调制解调器连接为您提供 api 抽象,因此您只需调用 NowSMS 网关的 Http Api,其余将由 NowSMS 网关处理。

      最重要的是,如果您的最终用户在使用您的应用程序时要保持互联网连接,或者如果您的应用程序是基于 Web 并通过互联网托管的,我强烈建议取消 GSM 调制解调器选项和 opt - 用于可靠的 Http SMS 网关服务提供商

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-07
        • 1970-01-01
        • 2014-11-29
        • 1970-01-01
        • 2016-02-25
        相关资源
        最近更新 更多