Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SMS connecting to a phone from C# and getting a response

Tags:

c#

excel

vba

sms

I wrote code to send an SMS using my GSM phone which is attached to the computer through COM port. The code is below.

The problem is I do see that it is in the outbox of the phone and it actually appears to have been sent, but when I contact the recipient they say that I have not received the message.

I test the phone, and I create and send a message using only the phone and it works perfectly. However, when I do this with my code, it APPEARS to have been sent, and I am getting all the correct AT COMMAND responses from the phone, but the message is actually NOT sent.

Here is the code:

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;
        }
    }
like image 518
Alex Gordon Avatar asked Jun 11 '10 23:06

Alex Gordon


People also ask

Can you send a text to a mobile phone from a computer?

Yes, there are many ways to send a text message from the computer including through email, iMessage, Android Messages, using a wireless carrier's website, using a free SMS website, online texting platforms, Google Voice, and from a streamlined VoIP solution like OpenPhone.

How do I connect messages to another phone?

Open the "Messages" tab. Create a backup of your messages. Disconnect the phone, and connect the new Android device. Choose which messages to transfer from the backup to the phone.


1 Answers

This is my code that works (tested) with U9 Telia cellular modem:

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

Usage example in a winforms application:

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();
}

To send an SMS:

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

On timer event, once every second:

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

This is registered as Winforms timer handler, so that modem will not have a thread.

Some utility classes used:

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

I implemented this in order to turn on air conditioner with SMS (yeah, I live in a hot country). It works. Please feel free to use any of this code.

Enjoy.

like image 156
Pavel Radzivilovsky Avatar answered Nov 14 '22 23:11

Pavel Radzivilovsky