SMS-соединение с телефоном из C # и получение ответа - PullRequest
7 голосов
/ 12 июня 2010

Я написал код для отправки SMS с помощью моего телефона GSM, который подключен к компьютеру через COM-порт.Код ниже.

Проблема в том, что я вижу, что он находится в папке «Исходящие» телефона, и кажется, что он действительно отправлен, но когда я связываюсь с получателем, они говорят, что я не получил сообщение.

Я тестирую телефон, создаю и отправляю сообщение, используя только телефон, и он работает отлично.Однако, когда я делаю это с моим кодом, он, кажется, отправлен, и я получаю все правильные ответы 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;
        }
    }

Ответы [ 4 ]

2 голосов
/ 15 июня 2010

Это мой код, который работает (тестируется) с сотовым модемом 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();
}

Чтобы отправить SMS:

        _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; } }
    }
}

Я реализовал это, чтобы включить кондиционер с помощью SMS (да, я живу в жаркой стране).Оно работает.Пожалуйста, не стесняйтесь использовать любой из этого кода.

Наслаждайтесь.

0 голосов
/ 17 июня 2010

Работа с GSM модемом для отправки SMS никогда не была хорошей идеей как таковая.Вот контрольный список, чтобы убедиться, что ваше SMS отправлено правильно.

  • Вы отправляете SMS в текстовом режиме.Попробуйте отправить его в режиме PDU .
  • Попробуйте эту библиотеку http://phonemanager.codeplex.com/
  • Измените ли вы номер SMSC перед отправкой SMS, если это такубедитесь, что SMSC верен.
  • Также убедитесь, что на вашей SIM-карте достаточно кредитов , чтобы вы могли отправлять исходящие SMS.
  • Проверьте состояние сети особенно, когда вы отправляете SMS из своей программы.Это может быть проблемой.

Я рекомендую вам ознакомиться с Lite-версией шлюза NowSMS , который поставляется бесплатно и позволяет работать с GSM-модемом очень непрерывно,NowSMS предоставит вам api-абстракцию через GSM-модемное соединение, поэтому вам просто нужно будет вызвать Http Api шлюза NowSMS, об остальном позаботится шлюз NowSMS.

Прежде всего, если ваш конечныйпользователи будут оставаться подключенными к Интернету во время использования вашего приложения или, если ваше приложение работает в Интернете и размещено в Интернете, я настоятельно рекомендую покончить с опцией GSM-модема и выбрать для надежных поставщиков услуг Http-шлюза SMS .

0 голосов
/ 15 июня 2010

GSMComm - это библиотека C #, которая позволит вам легко взаимодействовать с мобильным телефоном GSM в текстовом или PDU-режиме.(На сайте также есть куча утилит для тестирования)

0 голосов
/ 14 июня 2010

Microsoft предоставляет довольно приличный пример VB.Net в статье базы знаний " Как получить доступ к последовательным и параллельным портам с помощью Visual Basic .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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...