SMS, соединяющееся с телефоном от C# и получающее ответ

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

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

Я тестирую телефон, и я создаю и отправляю сообщение с помощью только телефон, и это работает отлично. Однако, когда я делаю это со своим кодом, это, КАЖЕТСЯ, было отправлено, и я получаю все корректное ПРИ ответах КОМАНДЫ с телефона, но сообщение на самом деле НЕ отправляется.

Вот код:

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;
        }
    }
7
задан Deduplicator 23 February 2015 в 19:22
поделиться

4 ответа

Это мой код, который работает (протестирован) с сотовым модемом Telia U9:

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 (да, я живу в жаркой стране). Оно работает. Не стесняйтесь использовать любой из этого кода.

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

2
ответ дан 7 December 2019 в 20:33
поделиться

Microsoft предоставляет довольно приличный пример VB.Net в статье KB "How to access serial and parallel ports by using 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)
0
ответ дан 7 December 2019 в 20:33
поделиться

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

0
ответ дан 7 December 2019 в 20:33
поделиться

Работа с 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-модема и opt -in для надежных поставщиков услуг шлюза Http SMS .

-1
ответ дан 7 December 2019 в 20:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: