SMS küldés és fogadás @ PHP

november 10th, 2011 | 0 Comments | PHP Scripts, Utiles

Nem tudod, hogy mit kezdjél azzal a rengeteg SMS-el amit a szolgáltatód ad?
Undod telefonon írni az SMS-t?
Olcsóbbnak találod a szolgáltatód SMS árait mint az SMS küldő cégekét?
Drágának találod az “egyedi” megoldásokat? (securix-sms)
!!!Van megoldás!!! Csak most csak neked csak itt! AKCIÓ!!!


*Szerintem azt az embert aki a marquee és blink -et bármilyen kombinációban használja ki kellene vizsgálni.

 

Azért folytatom innen lentről, hogy le tudd görgetni és ne zavarjon a “reklámcsík”

Szükséges dolgok:
1. PC (Linux + Apache? + PHP)
2. Telefon

A telefont meg a PC-t össze kell kötnünk valamilyen módon, én 2 megoldás ismerek:
1 – Soros port
2 – Soros port

Bármelyiket is választjuk az eredmény ugyan az. Soros kapcsolatot létesíthetünk adatkábellel vagy Bluetoothon keresztül.

Ha olyan telefon modellünk van ami egy USB (vagy adat) kábel csatlakozása után felajánlja a “COM port” módot akkor nincsen is más dolgunk ugorhatunk a XVII részhez, ha meg nem akkor “emulálunk” egy COM portot.

Párosítsuk a telefont a PC vel.
-2 Bekapcsoljuk a telefonon a Bluetooth-ot
-1 Ellenőrizzük hogy a PC ben van Bluetooth és bekapcsoljuk azt .

$ sudo service bluetooth restart
$ hcitool dev
Devices:
    hci0    E8:39:DF:1C:C7:03

1. Megkeressük az eszközt.

$hcitool scan
Scanning ...

    30:7B:10:83:F7:8E   Pl:Samsung

Ami-után azonosítottuk párosítsuk

$sudo hcitool cc [az eszkoz "MAC" száma]
$sudo hcitool auth [az eszkoz "MAC" száma]

Amint ezzel megvagyunk megkeressük rajta a COM porot

sdptool browse [az eszkoz "MAC" száma]  | grep -A 4 'Serial'

Kikeressük a “Chanel” számát a “Serial Port” -nak az én esetmben 5 de ez változhat.

“Csatlakoztatjük” (bind) a Serial Port -ot.

sudo rfcomm bind rfcomm[tetszöleges szam] [az eszkoz "MAC" száma]  [Serial Port Chanel száma]

Az én estemben:

sudo rfcomm bind rfcomm0 30:7B:10:83:F7:8E  5

Leellenőrizzük egy ls paranccsal és megnézhetjük a dmesg el is.

ls /dev/rfcomm0
dmesg
..
[43268.541068] Bluetooth: RFCOMM TTY layer initialized
[43268.541075] Bluetooth: RFCOMM socket layer initialized
[43268.541077] Bluetooth: RFCOMM ver 1.11
..

** Egyes telefonok estében érdemes “Authorizálni” a PC-t így nem fog minden kikapcsolódnál, rákérdezni.

XVII

Most már van egy soros kapcsolatunk a telefonunkhoz, itt még fontosnak tartom megemlíteni, hogy ha hosszútávon és sokat szeretnénk használni, akkor érdemes beszerezni egy olcsó telefont amelynek a töltése USB portól működik és csatlakoztatásnál felajánlja a COM port üzemmódot egy példa rá az “Alkatel 707 (Vodafone 541)” amit már 3-4000 ért lehet kapni.

Akik USB / Adat-kábelt használnak azok az eszköz elérését kileshetik

$ dmesg | grep tty

[2095340.683226] cdc_acm 3-2:1.1: ttyACM0: USB ACM device

és több mint valószínű hogy /dev/ttyACM0

Ahhoz, hogy SMS-t tudjunk küldeni illetve fogadni AT pancsokat kell küldenünk a “telefonnak” inkább GSM modem de maradjunk a telefonban, az interneten rengeteg helyen fellelhetőek ezek a parancsok, így nem is részletezném őket, annyira azonban kitérek, hogy ha a SIM kártya védve van PIN kóddal akkor azt jobb kikapcsolni, megoldható a PIN megadás is de szerintem túl kockázatos vállalkozás minden esetre aki akarja az alkalmazhatja.

Ahhoz, hogy tudjunk kommunikálni a soros port-al PHP ban szükségünk lesz a PHP Serial Calass-ra, valamit ha webservice ként szeretnénk használni akkor némi jogosultságra a /dev/[eszköz] ön, én ezt az én esetemben ezzel oldottam meg:

sudo chmod 666 /dev/ttyACM0

Az SMS küldés és fogadása innentől kezdve gyerekjáték, néhány AT parancsot kiküldünk a soros portra majd a kapott választ feldogozzuk.

PHP Serial Class

<?php
define ("SERIAL_DEVICE_NOTSET", 0);
define ("SERIAL_DEVICE_SET", 1);
define ("SERIAL_DEVICE_OPENED", 2);

/**
 * Serial port control class
 *
 * THIS PROGRAM COMES WITH ABSOLUTELY NO WARANTIES !
 * USE IT AT YOUR OWN RISKS !
 *
 * @author Rémy Sanchez <thenux@gmail.com>
 * @thanks Aurélien Derouineau for finding how to open serial ports with windows
 * @thanks Alec Avedisyan for help and testing with reading
 * @copyright under GPL 2 licence
 */

class phpSerial
{
    var $_device = null;
    var $_windevice = null;
    var $_dHandle = null;
    var $_dState = SERIAL_DEVICE_NOTSET;
    var $_buffer = "";
    var $_os = "";

    /**
     * This var says if buffer should be flushed by sendMessage (true) or manualy (false)
     *
     * @var bool
     */

    var $autoflush = true;

    /**
     * Constructor. Perform some checks about the OS and setserial
     *
     * @return phpSerial
     */

    function phpSerial ()
    {
        setlocale(LC_ALL, "en_US");

        $sysname = php_uname();

        if (substr($sysname, 0, 5) === "Linux")
        {
            $this->_os = "linux";

            if($this->_exec("stty --version") === 0)
            {
                register_shutdown_function(array($this, "deviceClose"));
            }
            else
            {
                trigger_error("No stty availible, unable to run.", E_USER_ERROR);
            }
        }
        elseif(substr($sysname, 0, 7) === "Windows")
        {
            $this->_os = "windows";
            register_shutdown_function(array($this, "deviceClose"));
        }
        else
        {
            trigger_error("Host OS is neither linux nor windows, unable tu run.", E_USER_ERROR);
            exit();
        }
    }

    //
    // OPEN/CLOSE DEVICE SECTION -- {START}
    //

    /**
     * Device set function : used to set the device name/address.
     * -> linux : use the device address, like /dev/ttyS0
     * -> windows : use the COMxx device name, like COM1 (can also be used
     *     with linux)
     *
     * @param string $device the name of the device to be used
     * @return bool
     */

    function deviceSet ($device)
    {
        if ($this->_dState !== SERIAL_DEVICE_OPENED)
        {
            if ($this->_os === "linux")
            {
                if (preg_match("@^COM(\d+):?$@i", $device, $matches))
                {
                    $device = "/dev/ttyS" . ($matches[1] - 1);
                }

                if ($this->_exec("stty -F " . $device) === 0)
                {
                    $this->_device = $device;
                    $this->_dState = SERIAL_DEVICE_SET;
                    return true;
                }
            }
            elseif ($this->_os === "windows")
            {
                if (preg_match("@^COM(\d+):?$@i", $device, $matches) and $this->_exec(exec("mode " . $device)) === 0)
                {
                    $this->_windevice = "COM" . $matches[1];
                    $this->_device = "\\.\com" . $matches[1];
                    $this->_dState = SERIAL_DEVICE_SET;
                    return true;
                }
            }

            trigger_error("Specified serial port is not valid", E_USER_WARNING);
            return false;
        }
        else
        {
            trigger_error("You must close your device before to set an other one", E_USER_WARNING);
            return false;
        }
    }

    /**
     * Opens the device for reading and/or writing.
     *
     * @param string $mode Opening mode : same parameter as fopen()
     * @return bool
     */

    function deviceOpen ($mode = "r+b")
    {
        if ($this->_dState === SERIAL_DEVICE_OPENED)
        {
            trigger_error("The device is already opened", E_USER_NOTICE);
            return true;
        }

        if ($this->_dState === SERIAL_DEVICE_NOTSET)
        {
            trigger_error("The device must be set before to be open", E_USER_WARNING);
            return false;
        }

        if (!preg_match("@^[raw]\+?b?$@", $mode))
        {
            trigger_error("Invalid opening mode : ".$mode.". Use fopen() modes.", E_USER_WARNING);
            return false;
        }

        $this->_dHandle = @fopen($this->_device, $mode);

        if ($this->_dHandle !== false)
        {
            stream_set_blocking($this->_dHandle, 0);
            $this->_dState = SERIAL_DEVICE_OPENED;
            return true;
        }

        $this->_dHandle = null;
        trigger_error("Unable to open the device", E_USER_WARNING);
        return false;
    }

    /**
     * Closes the device
     *
     * @return bool
     */

    function deviceClose ()
    {
        if ($this->_dState !== SERIAL_DEVICE_OPENED)
        {
            return true;
        }

        if (fclose($this->_dHandle))
        {
            $this->_dHandle = null;
            $this->_dState = SERIAL_DEVICE_SET;
            return true;
        }

        trigger_error("Unable to close the device", E_USER_ERROR);
        return false;
    }

    //
    // OPEN/CLOSE DEVICE SECTION -- {STOP}
    //

    //
    // CONFIGURE SECTION -- {START}
    //

    /**
     * Configure the Baud Rate
     * Possible rates : 110, 150, 300, 600, 1200, 2400, 4800, 9600, 38400,
     * 57600 and 115200.
     *
     * @param int $rate the rate to set the port in
     * @return bool
     */

    function confBaudRate ($rate)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set the baud rate : the device is either not set or opened", E_USER_WARNING);
            return false;
        }

        $validBauds = array (
            110    => 11,
            150    => 15,
            300    => 30,
            600    => 60,
            1200   => 12,
            2400   => 24,
            4800   => 48,
            9600   => 96,
            19200  => 19,
            38400  => 38400,
            57600  => 57600,
            115200 => 115200
        );

        if (isset($validBauds[$rate]))
        {
            if ($this->_os === "linux")
            {
                $ret = $this->_exec("stty -F " . $this->_device . " " . (int) $rate, $out);
            }
            elseif ($this->_os === "windows")
            {
                $ret = $this->_exec("mode " . $this->_windevice . " BAUD=" . $validBauds[$rate], $out);
            }
            else return false;

            if ($ret !== 0)
            {
                trigger_error ("Unable to set baud rate: " . $out[1], E_USER_WARNING);
                return false;
            }
        }
    }

    /**
     * Configure parity.
     * Modes : odd, even, none
     *
     * @param string $parity one of the modes
     * @return bool
     */

    function confParity ($parity)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set parity : the device is either not set or opened", E_USER_WARNING);
            return false;
        }

        $args = array(
            "none" => "-parenb",
            "odd"  => "parenb parodd",
            "even" => "parenb -parodd",
        );

        if (!isset($args[$parity]))
        {
            trigger_error("Parity mode not supported", E_USER_WARNING);
            return false;
        }

        if ($this->_os === "linux")
        {
            $ret = $this->_exec("stty -F " . $this->_device . " " . $args[$parity], $out);
        }
        else
        {
            $ret = $this->_exec("mode " . $this->_windevice . " PARITY=" . $parity{0}, $out);
        }

        if ($ret === 0)
        {
            return true;
        }

        trigger_error("Unable to set parity : " . $out[1], E_USER_WARNING);
        return false;
    }

    /**
     * Sets the length of a character.
     *
     * @param int $int length of a character (5 <= length <= 8)
     * @return bool
     */

    function confCharacterLength ($int)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set length of a character : the device is either not set or opened", E_USER_WARNING);
            return false;
        }

        $int = (int) $int;
        if ($int < 5) $int = 5;
        elseif ($int > 8) $int = 8;

        if ($this->_os === "linux")
        {
            $ret = $this->_exec("stty -F " . $this->_device . " cs" . $int, $out);
        }
        else
        {
            $ret = $this->_exec("mode " . $this->_windevice . " DATA=" . $int, $out);
        }

        if ($ret === 0)
        {
            return true;
        }

        trigger_error("Unable to set character length : " .$out[1], E_USER_WARNING);
        return false;
    }

    /**
     * Sets the length of stop bits.
     *
     * @param float $length the length of a stop bit. It must be either 1,
     * 1.5 or 2. 1.5 is not supported under linux and on some computers.
     * @return bool
     */

    function confStopBits ($length)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set the length of a stop bit : the device is either not set or opened", E_USER_WARNING);
            return false;
        }

        if ($length != 1 and $length != 2 and $length != 1.5 and !($length == 1.5 and $this->_os === "linux"))
        {
            trigger_error("Specified stop bit length is invalid", E_USER_WARNING);
            return false;
        }

        if ($this->_os === "linux")
        {
            $ret = $this->_exec("stty -F " . $this->_device . " " . (($length == 1) ? "-" : "") . "cstopb", $out);
        }
        else
        {
            $ret = $this->_exec("mode " . $this->_windevice . " STOP=" . $length, $out);
        }

        if ($ret === 0)
        {
            return true;
        }

        trigger_error("Unable to set stop bit length : " . $out[1], E_USER_WARNING);
        return false;
    }

    /**
     * Configures the flow control
     *
     * @param string $mode Set the flow control mode. Availible modes :
     *  -> "none" : no flow control
     *  -> "rts/cts" : use RTS/CTS handshaking
     *  -> "xon/xoff" : use XON/XOFF protocol
     * @return bool
     */

    function confFlowControl ($mode)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set flow control mode : the device is either not set or opened", E_USER_WARNING);
            return false;
        }

        $linuxModes = array(
            "none"     => "clocal -crtscts -ixon -ixoff",
            "rts/cts"  => "-clocal crtscts -ixon -ixoff",
            "xon/xoff" => "-clocal -crtscts ixon ixoff"
        );
        $windowsModes = array(
            "none"     => "xon=off octs=off rts=on",
            "rts/cts"  => "xon=off octs=on rts=hs",
            "xon/xoff" => "xon=on octs=off rts=on",
        );

        if ($mode !== "none" and $mode !== "rts/cts" and $mode !== "xon/xoff") {
            trigger_error("Invalid flow control mode specified", E_USER_ERROR);
            return false;
        }

        if ($this->_os === "linux")
            $ret = $this->_exec("stty -F " . $this->_device . " " . $linuxModes[$mode], $out);
        else
            $ret = $this->_exec("mode " . $this->_windevice . " " . $windowsModes[$mode], $out);

        if ($ret === 0) return true;
        else {
            trigger_error("Unable to set flow control : " . $out[1], E_USER_ERROR);
            return false;
        }
    }

    /**
     * Sets a setserial parameter (cf man setserial)
     * NO MORE USEFUL !
     *  -> No longer supported
     *  -> Only use it if you need it
     *
     * @param string $param parameter name
     * @param string $arg parameter value
     * @return bool
     */

    function setSetserialFlag ($param, $arg = "")
    {
        if (!$this->_ckOpened()) return false;

        $return = exec ("setserial " . $this->_device . " " . $param . " " . $arg . " 2>&1");

        if ($return{0} === "I")
        {
            trigger_error("setserial: Invalid flag", E_USER_WARNING);
            return false;
        }
        elseif ($return{0} === "/")
        {
            trigger_error("setserial: Error with device file", E_USER_WARNING);
            return false;
        }
        else
        {
            return true;
        }
    }

    //
    // CONFIGURE SECTION -- {STOP}
    //

    //
    // I/O SECTION -- {START}
    //

    /**
     * Sends a string to the device
     *
     * @param string $str string to be sent to the device
     * @param float $waitForReply time to wait for the reply (in seconds)
     */

    function sendMessage ($str, $waitForReply = 0.1)
    {
        $this->_buffer .= $str;

        if ($this->autoflush === true) $this->flush();

        usleep((int) ($waitForReply * 1000000));
    }

    /**
     * Reads the port until no new datas are availible, then return the content.
     *
     * @pararm int $count number of characters to be read (will stop before
     *  if less characters are in the buffer)
     * @return string
     */

    function readPort ($count = 0)
    {
        if ($this->_dState !== SERIAL_DEVICE_OPENED)
        {
            trigger_error("Device must be opened to read it", E_USER_WARNING);
            return false;
        }

        if ($this->_os === "linux")
        {
            $content = ""; $i = 0;

            if ($count !== 0)
            {
                do {
                    if ($i > $count) $content .= fread($this->_dHandle, ($count - $i));
                    else $content .= fread($this->_dHandle, 128);
                } while (($i += 128) === strlen($content));
            }
            else
            {
                do {
                    $content .= fread($this->_dHandle, 128);
                } while (($i += 128) === strlen($content));
            }

            return $content;
        }
        elseif ($this->_os === "windows")
        {
            /* Do nohting : not implented yet */
        }

        trigger_error("Reading serial port is not implemented for Windows", E_USER_WARNING);
        return false;
    }

    /**
     * Flushes the output buffer
     *
     * @return bool
     */

    function flush ()
    {
        if (!$this->_ckOpened()) return false;

        if (fwrite($this->_dHandle, $this->_buffer) !== false)
        {
            $this->_buffer = "";
            return true;
        }
        else
        {
            $this->_buffer = "";
            trigger_error("Error while sending message", E_USER_WARNING);
            return false;
        }
    }

    //
    // I/O SECTION -- {STOP}
    //

    //
    // INTERNAL TOOLKIT -- {START}
    //

    function _ckOpened()
    {
        if ($this->_dState !== SERIAL_DEVICE_OPENED)
        {
            trigger_error("Device must be opened", E_USER_WARNING);
            return false;
        }

        return true;
    }

    function _ckClosed()
    {
        if ($this->_dState !== SERIAL_DEVICE_CLOSED)
        {
            trigger_error("Device must be closed", E_USER_WARNING);
            return false;
        }

        return true;
    }

    function _exec($cmd, &$out = null)
    {
        $desc = array(
            1 => array("pipe", "w"),
            2 => array("pipe", "w")
        );

        $proc = proc_open($cmd, $desc, $pipes);

        $ret = stream_get_contents($pipes[1]);
        $err = stream_get_contents($pipes[2]);

        fclose($pipes[1]);
        fclose($pipes[2]);

        $retVal = proc_close($proc);

        if (func_num_args() == 2) $out = array($ret, $err);
        return $retVal;
    }

    //
    // INTERNAL TOOLKIT -- {STOP}
    //
}
?>

sms.class.php

<?php

Class SMS{

public $msg = '';
public $serial;

function __construct($comm){
    require_once "php_serial.class.php";
    $serial = new phpSerial;
    $serial->deviceSet($comm);
    $serial->confBaudRate(9600);
    $serial->confParity('none');
    $serial->confCharacterLength(8);
    $serial->deviceOpen();
    $this->serial=$serial;
    $this->msg .= 'Connected'."\n";
}





public function SendSMS($to,$msg){
$this->serial->sendMessage("AT\n");
$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CMGF=?\n");
$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CMGF=1\n");
$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CMGS=\"{$to}\"\n{$msg}".chr(26)."\n");
$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CMSS=1\n");
$this->msg .= $this->serial->readPort();
$this->serial->deviceClose();
}

public function GetSMS(){
$this->serial->sendMessage("AT\n");
$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CMGF=1\n");
$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CPMS=?\n");
$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CPMS=MT\n");
/*
SM. It refers to the message storage area on the SIM card.
ME. It refers to the message storage area on the GSM/GPRS modem or mobile phone.
MT. It refers to all message storage areas associated with the GSM/GPRS modem or mobile phone. For example, suppose a mobile phone can access two message storage areas: "SM" and "ME". The "MT" message storage area refers to the "SM" message storage area and the "ME" message storage area combined together.
BM. It refers to the broadcast message storage area. It is used to store cell broadcast messages.
SR. It refers to the status report message storage area. It is used to store status reports.
TA. It refers to the terminal adaptor message storage area.
Forrás : http://www.developershome.com/sms/cpmsCommand.asp
*/

$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CMGL=\"ALL\"\n");
$msg = $this->serial->readPort();
$this->msg .= $msg;
return $this->MsgToArray($msg);
}

public function MsgToArray($msg){

$tmp = explode('+CMGL: ',$msg);

$pat = '#(\d+),"([^"]+)","([^"]+)",([^,]*),"([^"]+)"\s(.*)#s';
$x=0;
foreach ($tmp as $sms){
preg_match($pat,$sms,$mat);
$remsg[$x]['id'] = $mat[1];
$remsg[$x]['from'] = $mat[3];
$remsg[$x]['time'] = strtotime($mat[5]);
//GSM7 encoded fuked up de nincs iconv support hozza.
$remsg[$x]['msg'] = $mat[6];
$x++;
}
return $remsg;
}



public function DeleteAll(){
$this->serial->sendMessage("AT\n");
$this->msg .= $this->serial->readPort();
$this->serial->sendMessage("AT+CPMS=MT\n");
$this->serial->sendMessage("AT+CMGD=1,4\n");
$this->msg .= $this->serial->readPort();
}


}

?>

contoller :)

<?php
require_once "sms.class.php";
$sms = new SMS("/dev/ttyACM0"); // Itt kell beállítani a COM portot

$do = $_GET['do'];

switch ($do){

    case 'list':
    echo serialize($sms->GetSMS());
    break;

    case 'send':
    $sms->SendSMS($_GET['to'],$_GET['msg']);
    echo '<pre>'.$sms->msg;
    break;

    case 'delete':
    $sms->DeleteAll();
    echo '<pre>'.$sms->msg;
    break; 

}

A teljes forráskód itt letölthető :

SMS.zip

Eléggé BETA a megoldás de a célnak megfelel, run.php?do=list vissza adja az összes SMS üzenetet ami a készüléken található, a run.php?do=send&to0036201234567&msg=test uzenet eküld egy üzenetet valamit a run.php?do=delete törli az összes SMS-t.

Nincsen benne “UTF-8” támogatás nem találtam GSM7 UTF8 [hu] encodert talán majd beépítik az libiconv-be egy napon.

Az interneten rengeteg helyen megtaláljuk az AT comman set et és kedvünkre kísérletezgethetünk, pl elolvashatjuk az SMS értesítőket így megtudhatjuk, hogy célba ért e az SMS, ha több ezer netán több tízezer SMS-t szeretnénk kiküldeni akkor érdemes az előtt egy HRL lookup

Leave a Comment