php – Management a Servo linked on Arduino via an online web page on Raspberry Pi

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

/**
 * Serial port management class
 *
 * THIS PROGRAM COMES WITH ABSOLUTELY NO WARANTIES !
 * USE IT AT YOUR OWN RISKS !
 *
 * @creator Rémy Sanchez <thenux@gmail.com>
 * @thanks Aurélien Derouineau for locating tips on how to open serial ports with home windows
 * @thanks Alec Avedisyan for assist and testing with studying
 * @copyright beneath 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 needs to be flushed by sendMessage (true) or manualy (false)
     *
     * @var bool
     */
    var $autoflush = true;

    /**
     * Constructor. Carry out some checks in regards to the OS and setserial
     *
     * @return phpSerial
     */
    operate 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) === "Home windows")
        {
            $this->_os = "home windows";
            register_shutdown_function(array($this, "deviceClose"));
        }
        else
        {
            trigger_error("Host OS is neither linux nor home windows, unable tu run.", E_USER_ERROR);
            exit();
        }
    }

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

    /**
     * Machine set operate : used to set the gadget title/handle.
     * -> linux : use the gadget handle, like /dev/ttyS0
     * -> home windows : use the COMxx gadget title, like COM1 (can be used
     *     with linux)
     *
     * @param string $gadget the title of the gadget for use
     * @return bool
     */
    operate deviceSet ($gadget)
    {
        if ($this->_dState !== SERIAL_DEVICE_OPENED)
        {
            if ($this->_os === "linux")
            {
                if (preg_match("@^COM(d+):?$@i", $gadget, $matches))
                {
                    $gadget = "/dev/ttyS" . ($matches[1] - 1);
                }

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

            trigger_error("Specified serial port just isn't legitimate", E_USER_WARNING);
            return false;
        }
        else
        {
            trigger_error("You have to shut your gadget earlier than to set an different one", E_USER_WARNING);
            return false;
        }
    }

    /**
     * Opens the gadget for studying and/or writing.
     *
     * @param string $mode Opening mode : similar parameter as fopen()
     * @return bool
     */
    operate deviceOpen ($mode = "r+b")
    {
        if ($this->_dState === SERIAL_DEVICE_OPENED)
        {
            trigger_error("The gadget is already opened", E_USER_NOTICE);
            return true;
        }

        if ($this->_dState === SERIAL_DEVICE_NOTSET)
        {
            trigger_error("The gadget should be set earlier than 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 gadget", E_USER_WARNING);
        return false;
    }

    /**
     * Closes the gadget
     *
     * @return bool
     */
    operate 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 shut the gadget", E_USER_ERROR);
        return false;
    }

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

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

    /**
     * Configure the Baud Charge
     * Potential charges : 110, 150, 300, 600, 1200, 2400, 4800, 9600, 38400,
     * 57600 and 115200.
     *
     * @param int $fee the speed to set the port in
     * @return bool
     */
    operate confBaudRate ($fee)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set the baud fee : the gadget is both 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) $fee, $out);
            }
            elseif ($this->_os === "home windows")
            {
                $ret = $this->_exec("mode " . $this->_windevice . " BAUD=" . $validBauds[$rate], $out);
            }
            else return false;

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

    /**
     * Configure parity.
     * Modes : odd, even, none
     *
     * @param string $parity one of many modes
     * @return bool
     */
    operate confParity ($parity)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set parity : the gadget is both 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;
    }

    /**
     * Units the size of a personality.
     *
     * @param int $int size of a personality (5 <= size <= 8)
     * @return bool
     */
    operate confCharacterLength ($int)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set size of a personality : the gadget is both 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 size : " .$out[1], E_USER_WARNING);
        return false;
    }

    /**
     * Units the size of cease bits.
     *
     * @param float $size the size of a cease bit. It should be both 1,
     * 1.5 or 2. 1.5 just isn't supported beneath linux and on some computer systems.
     * @return bool
     */
    operate confStopBits ($size)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set the size of a cease bit : the gadget is both not set or opened", E_USER_WARNING);
            return false;
        }

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

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

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

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

    /**
     * Configures the circulate management
     *
     * @param string $mode Set the circulate management mode. Availible modes :
     *  -> "none" : no circulate management
     *  -> "rts/cts" : use RTS/CTS handshaking
     *  -> "xon/xoff" : use XON/XOFF protocol
     * @return bool
     */
    operate confFlowControl ($mode)
    {
        if ($this->_dState !== SERIAL_DEVICE_SET)
        {
            trigger_error("Unable to set circulate management mode : the gadget is both 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 circulate management 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 circulate management : " . $out[1], E_USER_ERROR);
            return false;
        }
    }

    /**
     * Units a setserial parameter (cf man setserial)
     * NO MORE USEFUL !
     *  -> Now not supported
     *  -> Solely use it when you want it
     *
     * @param string $param parameter title
     * @param string $arg parameter worth
     * @return bool
     */
    operate 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} === "https://stackoverflow.com/")
        {
            trigger_error("setserial: Error with gadget file", E_USER_WARNING);
            return false;
        }
        else
        {
            return true;
        }
    }

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

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

    /**
     * Sends a string to the gadget
     *
     * @param string $str string to be despatched to the gadget
     * @param float $waitForReply time to attend for the reply (in seconds)
     */
    operate sendMessage ($str, $waitForReply = 0.1)
    {
        $this->_buffer .= $str;

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

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

    /**
     * Reads the port till no new datas are availible, then return the content material.
     *
     * @pararm int $depend variety of characters to be learn (will cease earlier than
     *  if much less characters are within the buffer)
     * @return string
     */
    operate readPort ($depend = 0)
    {
        if ($this->_dState !== SERIAL_DEVICE_OPENED)
        {
            trigger_error("Machine should be opened to learn it", E_USER_WARNING);
            return false;
        }

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

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

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

        trigger_error("Studying serial port just isn't applied for Home windows", E_USER_WARNING);
        return false;
    }

    /**
     * Flushes the output buffer
     *
     * @return bool
     */
    operate flush ()
    {
        if (!$this->_ckOpened()) return false;

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

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

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

    operate _ckOpened()
    {
        if ($this->_dState !== SERIAL_DEVICE_OPENED)
        {
            trigger_error("Machine should be opened", E_USER_WARNING);
            return false;
        }

        return true;
    }

    operate _ckClosed()
    {
        if ($this->_dState !== SERIAL_DEVICE_CLOSED)
        {
            trigger_error("Machine should be closed", E_USER_WARNING);
            return false;
        }

        return true;
    }

    operate _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}
    //
}
?>


Source link

Leave a Reply

Your email address will not be published. Required fields are marked *