40

Tenía que hacer una aplicación web y en base a ciertas reglas de negocio enviarles SMS a los clientes avisándoles de su movimientos, en ese momento al investigar encontré varias paginas con apis o servicios donde haces un llamado enviándoles cierta información y ellos mandan el SMS, y te cobran por SMS.

Entonces me puse a pensar como podría hacer ese servicio yo misma, pensando en pagar un plan con SMS ilimitados

Cristina Carrasco
  • 5,152
  • 1
  • 15
  • 35
  • 2
    Arduino es la respuesta! Existe un modem GSM/GPRS para Arduino que permite enviar mensajes. Hay una montaña de información por internet. http://forum.arduino.cc/index.php?topic=183295.0 – UselesssCat Sep 28 '17 at 03:37

2 Answers2

50

Esta pregunta me la hice ya algunos meses atrás y encontré la solución quiero compartirla con ustedes.

Si se pueden enviar mensajes SMS con una aplicación propia, se necesita lo siguiente:

  1. Una computadora o servidor física (no rentado, ni un servicio)
  2. Un módem GSM o Banca Ancha Móvil (instalado con drivers)
  3. Un chip de alguna compañía de celular (de preferencia con SMS ilimitados)
  4. Una aplicación que pueda enviar GSM AT Command

De esta manera podemos hacer nuestro servidor de envió de SMS y pensando en enviar muchos mensajes, evidentemente nos conviene mas pagar una renta por SMS ilimitados que pagar una cantidad fija por cada mensaje.

Existen los Modems GSM, Banda Ancha Movil (BAM), como los que muestro acontinuación:

BAM

introducir la descripción de la imagen aquí

GSM

introducir la descripción de la imagen aquí introducir la descripción de la imagen aquí

Yo hice la prueba con esta BAM:

introducir la descripción de la imagen aquí

Huawei E153 Módem USB GSM/GPRS/EDGE 850/900/1800/1900 MHz

La conecte a la computadora con el chip (tarjeta SIM) de mi compañía de celular, instale los drivers correspondientes y probé con la app propia de la compañía Telcel en este caso.

Después encontré un software hecho en c#, les explico y les comparto la liga:

Descargar código de: SMS aplicación c#

La aplicación se conecta por un puerto COM al modem introducir la descripción de la imagen aquí

private void btnOK_Click(object sender, EventArgs e)
    {
        try
        {
            //Open communication port 
            this.port = objclsSMS.OpenPort(this.cboPortName.Text, Convert.ToInt32(this.cboBaudRate.Text), 
                Convert.ToInt32(this.cboDataBits.Text), Convert.ToInt32(this.txtReadTimeOut.Text), Convert.ToInt32(this.txtWriteTimeOut.Text));

            if (this.port != null)
            {
                this.gboPortSettings.Enabled = false;

                //MessageBox.Show("Modem is connected at PORT " + this.cboPortName.Text);
                this.statusBar1.Text = "Modem is connected at PORT " + this.cboPortName.Text;

                //Add tab pages
                this.tabSMSapplication.TabPages.Add(tbSendSMS);
                this.tabSMSapplication.TabPages.Add(tbReadSMS);
                this.tabSMSapplication.TabPages.Add(tbDeleteSMS);

                this.lblConnectionStatus.Text = "Connected at " + this.cboPortName.Text;
                this.btnDisconnect.Enabled = true;
            }

            else
            {
                //MessageBox.Show("Invalid port settings");
                this.statusBar1.Text = "Invalid port settings";
            }
        }
        catch (Exception ex)
        {
            ErrorLog(ex.Message);
        }

    }

Se intenta conectar:

//Open Port
    public SerialPort OpenPort(string p_strPortName, int p_uBaudRate, int p_uDataBits, int p_uReadTimeout, int p_uWriteTimeout)
    {
        receiveNow = new AutoResetEvent(false);
        SerialPort port = new SerialPort();

        try
        {           
            port.PortName = p_strPortName;                 //COM1
            port.BaudRate = p_uBaudRate;                   //9600
            port.DataBits = p_uDataBits;                   //8
            port.StopBits = StopBits.One;                  //1
            port.Parity = Parity.None;                     //None
            port.ReadTimeout = p_uReadTimeout;             //300
            port.WriteTimeout = p_uWriteTimeout;           //300
            port.Encoding = Encoding.GetEncoding("iso-8859-1");
            port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
            port.Open();
            port.DtrEnable = true;
            port.RtsEnable = true;
        }
        catch (Exception ex)
        {
            throw ex;
        }
        return port;
    }

Una vez conectado se pueden usar las funciones de las siguientes pestañas:

Enviar SMS

introducir la descripción de la imagen aquí

Muestro el código utilizado para enviar los SMS

btnSendSMS_Click

 private void btnSendSMS_Click(object sender, EventArgs e)
    {

        //.............................................. Send SMS ....................................................
        try
        {

            if (objclsSMS.sendMsg(this.port, this.txtSIM.Text, this.txtMessage.Text))
            {
                //MessageBox.Show("Message has sent successfully");
                this.statusBar1.Text = "Message has sent successfully";
            }
            else
            {
                //MessageBox.Show("Failed to send message");
                this.statusBar1.Text = "Failed to send message";
            }

        }
        catch (Exception ex)
        {
            ErrorLog(ex.Message);
        }
    }

sendMsg

public bool sendMsg(SerialPort port, string PhoneNo, string Message)
    {
        bool isSend = false;

        try
        {

            string recievedData = ExecCommand(port,"AT", 300, "No phone connected");
            recievedData = ExecCommand(port,"AT+CMGF=1", 300, "Failed to set message format.");
            String command = "AT+CMGS=\"" + PhoneNo + "\"";
            recievedData = ExecCommand(port,command, 300, "Failed to accept phoneNo");         
            command = Message + char.ConvertFromUtf32(26) + "\r";
            recievedData = ExecCommand(port,command, 3000, "Failed to send message"); //3 seconds
            if (recievedData.EndsWith("\r\nOK\r\n"))
            {
                isSend = true;
            }
            else if (recievedData.Contains("ERROR"))
            {
                isSend = false;
            }
            return isSend;
        }
        catch (Exception ex)
        {
            throw ex; 
        }

    }     

Como se muestra en esta línea:

String command = "AT+CMGS=\"" + PhoneNo + "\"";

el módem recibe AT COMMANDS o también conocidos como Conjunto de comandos Hayes

Leer SMS

introducir la descripción de la imagen aquí

btnReadSMS_Click

 private void btnReadSMS_Click(object sender, EventArgs e)
    {
        try
        {
            //count SMS 
            int uCountSMS = objclsSMS.CountSMSmessages(this.port);
            if (uCountSMS > 0)
            {

                #region Command
                string strCommand = "AT+CMGL=\"ALL\"";

                if (this.rbReadAll.Checked)
                {
                    strCommand = "AT+CMGL=\"ALL\"";
                }
                else if (this.rbReadUnRead.Checked)
                {
                    strCommand = "AT+CMGL=\"REC UNREAD\"";
                }
                else if (this.rbReadStoreSent.Checked)
                {
                    strCommand = "AT+CMGL=\"STO SENT\"";
                }
                else if (this.rbReadStoreUnSent.Checked)
                {
                    strCommand = "AT+CMGL=\"STO UNSENT\"";
                }
                #endregion

                // If SMS exist then read SMS
                #region Read SMS
                //.............................................. Read all SMS ....................................................
                objShortMessageCollection = objclsSMS.ReadSMS(this.port, strCommand);
                foreach (ShortMessage msg in objShortMessageCollection)
                {

                    ListViewItem item = new ListViewItem(new string[] { msg.Index, msg.Sent, msg.Sender, msg.Message });
                    item.Tag = msg;
                    lvwMessages.Items.Add(item);

                }
                #endregion

            }
            else
            {
                lvwMessages.Clear();
                //MessageBox.Show("There is no message in SIM");
                this.statusBar1.Text = "There is no message in SIM";

            }
        }
        catch (Exception ex)
        {
            ErrorLog(ex.Message);
        }
    }

ReadSMS

public ShortMessageCollection ReadSMS(SerialPort port, string p_strCommand)
    {

        // Set up the phone and read the messages
        ShortMessageCollection messages = null;
        try
        {

            #region Execute Command
            // Check connection
            ExecCommand(port,"AT", 300, "No phone connected");
            // Use message format "Text mode"
            ExecCommand(port,"AT+CMGF=1", 300, "Failed to set message format.");
            // Use character set "PCCP437"
            ExecCommand(port,"AT+CSCS=\"PCCP437\"", 300, "Failed to set character set.");
            // Select SIM storage
            ExecCommand(port,"AT+CPMS=\"SM\"", 300, "Failed to select message storage.");
            // Read the messages
            string input = ExecCommand(port, p_strCommand, 5000, "Failed to read the messages.");
            #endregion

            #region Parse messages
            messages = ParseMessages(input);
            #endregion

        }
        catch (Exception ex)
        {
            throw ex;
        }

        if (messages != null)
            return messages;
        else
            return null;

    }

Eliminar SMS

introducir la descripción de la imagen aquí

btnDeleteSMS_Click

 private void btnDeleteSMS_Click(object sender, EventArgs e)
    {
        try
        {
            //Count SMS 
            int uCountSMS = objclsSMS.CountSMSmessages(this.port);
            if (uCountSMS > 0)
            {
                DialogResult dr = MessageBox.Show("Are u sure u want to delete the SMS?", "Delete confirmation", MessageBoxButtons.YesNo);

                if (dr.ToString() == "Yes")
                {
                    #region Delete SMS

                    if (this.rbDeleteAllSMS.Checked)
                    {                           
                        //...............................................Delete all SMS ....................................................

                        #region Delete all SMS
                        string strCommand = "AT+CMGD=1,4";
                        if (objclsSMS.DeleteMsg(this.port, strCommand))
                        {
                            //MessageBox.Show("Messages has deleted successfuly ");
                            this.statusBar1.Text = "Messages has deleted successfuly";
                        }
                        else
                        {
                            //MessageBox.Show("Failed to delete messages ");
                            this.statusBar1.Text = "Failed to delete messages";
                        }
                        #endregion

                    }
                    else if (this.rbDeleteReadSMS.Checked)
                    {                          
                        //...............................................Delete Read SMS ....................................................

                        #region Delete Read SMS
                        string strCommand = "AT+CMGD=1,3";
                        if (objclsSMS.DeleteMsg(this.port, strCommand))
                        {
                            //MessageBox.Show("Messages has deleted successfuly");
                            this.statusBar1.Text = "Messages has deleted successfuly";
                        }
                        else
                        {
                            //MessageBox.Show("Failed to delete messages ");
                            this.statusBar1.Text = "Failed to delete messages";
                        }
                        #endregion

                    }

                    #endregion
                }
            }
        }
        catch (Exception ex)
        {
            ErrorLog(ex.Message);
        }

    }

DeleteMsg

    #region Delete SMS
    public bool DeleteMsg(SerialPort port , string p_strCommand)
    {
        bool isDeleted = false;
        try
        {

            #region Execute Command
            string recievedData = ExecCommand(port,"AT", 300, "No phone connected");
            recievedData = ExecCommand(port,"AT+CMGF=1", 300, "Failed to set message format.");
            String command = p_strCommand;
            recievedData = ExecCommand(port,command, 300, "Failed to delete message");
            #endregion

            if (recievedData.EndsWith("\r\nOK\r\n"))
            {
                isDeleted = true;
            }
            if (recievedData.Contains("ERROR"))
            {
                isDeleted = false;
            }
            return isDeleted;
        }
        catch (Exception ex)
        {
            throw ex; 
        }

    }  
    #endregion

Anexo un poco de información sobre los AT COMMANDS

Conjunto de comandos Hayes

El conjunto de comandos Hayes es un lenguaje desarrollado por la compañía Hayes Communications que prácticamente se convirtió en estándar abierto de comandos para configurar y parametrizar módems. Los caracteres «AT», que preceden a todos los comandos, significan «Atención», e hicieron que se conociera también a este conjunto de comandos como comandos AT. Midiendo la longitud de los bits se puede determinar en detalle la velocidad de transmisión.

Comandos

AT Sirve para verificar si el módulo SIM900 está funcionando adecuadamente para entrar en modo comando. Al enviar AT el SIM deberá contestarnos con un OK.

AT+CGMI Veremos en nombre del fabricante

ATI Ver la información del producto.

AT+IPR=? Preguntar el Baud Rate en el que puede operar el SIM

AT+IPR? Sirve para preguntar el Baud Rate actual

AT+IPR=XXXX Configuremos a la frecuencia deseada

AT+COPS? Nombre de la compañía telefónica

AT+CGSN Visualizar el IMEI del chip utilizado

AT+CSCS? Tipo de texto

AT+CSCS=”XXX” Configurar a tipo de texto

AT+CMGF? Ver el formato de un mensaje, ya sea PDU(0) o SMS(1)”

AT+CMGS=04455XXXXXXXX Enviar un SMS Se despliega el símbolo mayor que > Escribir mensaje y al finalizar presiona Ctrl+Z retornará OK si el SMS se envió correctamente.

AT+CMGL=ALL Sirve para ver todos los mensajes que nos han llegado al SIM

ATD04455XXXXXXXX; Sirve para hacer una llamada a cualquier teléfono móvil

ATA Sirve para contestar una llamada

ATH Sirve para colgar una llamada

Cristina Carrasco
  • 5,152
  • 1
  • 15
  • 35
  • Tengo una pregunta, estoy tratando de enviar un SMS con el dispositivo HUAWEI E3531, pero lo que no veo es como se genera el puerto COM, para poder mandarle los datos por medio del COM. – andrezi Apr 24 '17 at 14:58
  • Instala el Driver del dispositivo, el puerto COM es virtual, se crea solo al instalar el driver, lo puedes ver en device manager de windows. Otra forma de validarlo es probando con la aplicación, y cambiando de puerto... – Cristina Carrasco Apr 24 '17 at 21:53
  • ok, gracias, osea que si no se me crea solo "el puerto COM", quiere decir que el dispositivo en cuestión no soporta la comunicación por medio de Puerto Serial Virtual ?. Te comento un poco lo que hice, compre el HUAWEI E353, lo conecte al pc y le di en el archivo AutoRun, después de eso el me abrió una pagina web que es el administrador donde uno puede enviar mensajes, leer mensajes, etc , me fije en el administrador de dispositivos y el no me genera ningún puerto COM, entonces mi duda era si había algún driver que debería descargar, fui a la pagina web del dispositivo y no vi nada de drivers – andrezi Apr 25 '17 at 17:37
  • No estoy segura, pero si descargas el prog de ejemplo que publique, podrías hacer la prueba allí directamente, en mi caso si busque un driver y lo instalé... así fue como funcionó, la otra opción que se me ocurre es buscar algún SDK del fabricante0. – Cristina Carrasco Apr 26 '17 at 16:29
  • En este link recomiendan descargar drivers: [https://www.adslzone.net/postt395244.html](https://www.adslzone.net/postt395244.html) – Cristina Carrasco Apr 26 '17 at 16:36
  • 1
    @CristinaCarrascoAngulo no había visto esta gran respuesta, has hecho un excelente trabajo :D – Phi Jun 15 '17 at 05:19
  • @Flxtr Muchas gracias, saludos – Cristina Carrasco Jun 15 '17 at 17:21
  • @Flxtr pudiste enviar ? – PieroDev Jun 25 '18 at 16:05
  • @andrezi te resulto ? – PieroDev Jun 25 '18 at 16:27
  • @Pierro con ese dispositivo en especifico no, compre otro dispositivo personalizado y si lo pude lograr. – andrezi Jun 27 '18 at 20:29
16

Sé que ya tienes una respuesta y que es muy completa, voy a poner una alternativa con la que no hace falta tampoco pagar a terceros, que no requiere de ningún hardware especial y que permite enviar SMS de forma totalmente gratuita... el principal inconveniente: no funciona en todos los países.

La idea es sencilla: usar los gateways que muchas compañías telefónicas tienen y que transforman emails a mensajes de texto SMS. Es decir, para enviar un mensaje de texto tan sólo tienes que mandar un email a la dirección asociada con el teléfono móvil del destinatario.

En Internet puedes encontrar listas con estos gateways y el modo en el que se formaría el email al que mandar el mensaje (p.e. éste es uno que utilicé hace unos años para desarrollar una aplicación que me permitiera mandar mensajes gratis a través de una tableta con webOS). Generalmente es algo como [número_de_teléfono]@[proveedor_de_telefonía], aunque hay muchas excepciones.

Por poner un ejemplo, si quieres enviar un SMS en Estados Unidos al número (ficticio) 123-456-7890 de T-Mobile, lo que harías sería mandar un email a 1234567890@tmomail.net y la persona lo recibirá como SMS. Algo que en C# se podría hacer así:

Nota: Esto es una variante del código compartido por Josh Ledgard en MSDN. No lo he probado y puede contener fallos.

System.Net.Mail.MailMessage mensaje = new System.Net.Mail.MailMessage();
mensaje.To.Add("1234567890@tmomile");
mensaje.Subject = "Asunto";
mensaje.From = new System.Net.Mail.MailAddress("alvaro@montoro");
mensaje.Body = "Cuerpo del mensaje";
System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient("tu_host_smtp");
smtp.Send(mensaje);

Para continuar el ejemplo, he enviado un email a la dirección asociada con mi número de teléfono, con el asunto "Hola" y el cuerpo "Caracola" y a los pocos segundos he recibido el mensaje que se ve así (en iPhone):

SMS recibido desde un email


Hasta aquí lo he puesto todo como muy bonito y muy sencillo... pero ahora vamos a ver los inconvenientes. Este sistema presenta dos problemas importantes:

  1. Los gateways no funcionan en todos los países (están disponibles, pero las compañías telefónicas pueden no completar el envío). Esto se debe, entre otros motivos, a una cuestión económica: suele funcionar en países en los que tanto emisor como destinatario pagan por el mensaje, mientras que suele fallar en los que sólo el emisor paga por el mensaje (porque en este caso el emisor sería un email y la compañía no vería ni un céntimo).
  2. Tienes que conocer el proveedor de telefonía del receptor. Esto es más complejo, pero hay alternativas para solventarlo:

    • Cuando pidas el número de teléfono al usuario, pídele también el proveedor. Rápido, sencillo y sin problemas.
    • Utiliza un servicio que comprobación de proveedores. Hay sitios online donde puedes obtener el proveedor a partir del número de teléfono. Sólo lo necesitarías realizar una vez y guardarlo.
    • Inundar la red. Sí, suena mal y es feo y poco recomendable... pero funciona: Si no sabes el proveedor del destinatario y no sabes a qué dirección de email mandar el mensaje, ¡mándaselo a todos los proveedores! Los que no tengan el número ignorarán tu petición, y el que lo tenga la completará.

Alternativamente, puedes usar APIs gratuitas como la de SMS Gateway, que incluyendo su librería (en PHP), te permite mandar mensajes con apenas unas líneas de código (además que tienen otros servicios interesantes).

Alvaro Montoro
  • 48,157
  • 26
  • 100
  • 179
  • También investigue por ese lado, y no encontré ninguno que funcionara para México, hice varias pruebas enviando email directo desde gmail, si conoces alguno que funcione en México, con alguna de las compañías Mexicanas, seria muy buena opción :-) gracias. – Cristina Carrasco Dec 25 '16 at 03:33
  • @CristinaCarrascoAngulo Tienen conocimiento si para Colombia funciona alguna? He probado y no he conseguido que funcione – Sxntk Dec 28 '16 at 16:36
  • @Sxntk No he probado en Colombia, sé que funciona en Francia y Estados Unidos (aunque no he probado en Francia desde hace años). Si quieres te puedo ayudar a probar. – Alvaro Montoro Dec 28 '16 at 17:28
  • @AlvaroMontoro He ensayado con Tigo y Virgin pero no consigo que dar con el servidor después del @. Intenté con vmobl.co, vmobl.com y vmobile.com – Sxntk Dec 28 '16 at 18:48