├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE ├── README.md ├── bin └── quality.sh ├── blinker ├── blinkerTest.php ├── composer.json ├── phpunit.xml.dist ├── src └── PhpGpio │ ├── Gpio.php │ ├── GpioDevelop.php │ ├── GpioInterface.php │ ├── Pi.php │ └── Sensors │ ├── DS18B20.php │ ├── MCP3002.php │ └── SensorInterface.php └── tests ├── PhpGpio └── Tests │ ├── GpioTest.php │ ├── PiTest.php │ └── Sensors │ └── DS18B20Test.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | composer.phar 4 | phpunit.phar 5 | phpunit.xml 6 | php-cs-fixer.phar 7 | METRICS 8 | .idea/* 9 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: [vendor/*, tests/*, app/*, bin/*, library/*] 3 | 4 | tools: 5 | # Similar Code Detector 6 | php_sim: true 7 | php_cpd: false 8 | 9 | # Metrics 10 | php_pdepend: true 11 | 12 | # Some Metrics + Bug Detection/Auto-Fixes 13 | php_analyzer: true 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | 7 | before_script: 8 | - curl -s http://getcomposer.org/installer | php 9 | - php composer.phar install --dev 10 | 11 | script: phpunit --coverage-text 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Aaron Pearce, Ronan Guilloux, Bas Bloemsaat 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | php-gpio 2 | ======== 3 | 4 | 5 | **php-gpio** is a simple PHP library to play with the Raspberry PI's GPIO pins. 6 | 7 | It provides simple tools such as reading & writing to pins. 8 | 9 | [![Latest Stable Version](https://poser.pugx.org/ronanguilloux/php-gpio/v/stable.png)](https://packagist.org/packages/ronanguilloux/php-gpio) [![Build Status](https://secure.travis-ci.org/ronanguilloux/php-gpio.png?branch=master)](http://travis-ci.org/ronanguilloux/php-gpio) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/ronanguilloux/php-gpio/badges/quality-score.png?s=199e653b7ec9627593843ba15c961f9c0be7701d)](https://scrutinizer-ci.com/g/ronanguilloux/php-gpio/) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/fde42adb-344d-4055-b78d-20b598040ac8/mini.png)](https://insight.sensiolabs.com/projects/fde42adb-344d-4055-b78d-20b598040ac8) [![Total Downloads](https://poser.pugx.org/ronanguilloux/php-gpio/downloads.png)](https://packagist.org/packages/ronanguilloux/php-gpio) 10 | 11 | ![Circuit snapshot](https://raw.github.com/ronanguilloux/temperature-pi/master/resources/images/mounting.jpg) 12 | 13 | [UPDATE Fall 2014] Now compatible with both Raspberry Pi **B Model & B+ Revision** 14 | 15 | Tl;dr 16 | ----- 17 | 18 | ``` 19 | - "Hey, I just want to blink a LED from my raspberry pi hosted website!" 20 | - "OK good guy: git clone https://github.com/ronanguilloux/php-gpio-web.git` 21 | & remember to come back here when you're lost ;-)" 22 | ``` 23 | 24 | => [php-gpio-web: a simple example for you to play with Leds & PHP](https://github.com/ronanguilloux/php-gpio-web) 25 | 26 | 27 | GPIO 28 | ---- 29 | 30 | General Purpose Input/Output (a.k.a. GPIO) is a generic pin on a chip whose behavior 31 | (including whether it is an input or output pin) can be controlled (programmed) through software. 32 | The Raspberry Pi allows peripherals and expansion boards (such as the Rpi Gertboard) 33 | to access the CPU by exposing the inputs and outputs. 34 | 35 | For further informations about the Raspberry Pi's GPIO capabilities, see docs & schemas at http://elinux.org/RPi_Low-level_peripherals. 36 | 37 | For Raspbeery Pi's GPIO controlling *LEDs*, have a look at a sample complete circuit diagram for a single LED, 38 | with detailled explanations & schemas, [here](https://projects.drogon.net/raspberry-pi/gpio-examples/tux-crossing/gpio-examples-1-a-single-led/). 39 | 40 | For Raspbeery Pi's GPIO controlling *sensors*, check the [DS18B20 (temperature sensor)](http://learn.adafruit.com/adafruits-raspberry-pi-lesson-11-ds18b20-temperature-sensing/overview) in action [here](https://github.com/ronanguilloux/temperature-pi) 41 | 42 | 43 | Hardware prerequisites 44 | ---------------------- 45 | 46 | After having installed & wired your LED & resistor on a breadboard, 47 | add appropriate modules from the Linux Kernel: 48 | 49 | For *LEDs*, enable the gpio module : 50 | 51 | ``` bash 52 | $ sudo modprobe w1-gpio 53 | ``` 54 | 55 | ([see a complete circuit diagram for a single LED + explanations & schemas here](https://projects.drogon.net/raspberry-pi/gpio-examples/tux-crossing/gpio-examples-1-a-single-led/)) 56 | 57 | For *sensors*, enable the appropriate sensor. 58 | By example for a [DS18B20 1-Wire digital temperature sensor](http://learn.adafruit.com/adafruits-raspberry-pi-lesson-11-ds18b20-temperature-sensing/overview): 59 | 60 | ``` bash 61 | $ sudo modprobe w1-therm 62 | ``` 63 | 64 | ([see the DS18B20 in action on a Raspberry Pi here](https://github.com/ronanguilloux/temperature-pi)) 65 | 66 | To load such kernel modules automatically at boot time, edit the `/etc/modules` file & add these two lines: 67 | 68 | ``` 69 | w1-gpio 70 | w1-therm 71 | ``` 72 | 73 | 74 | Installation 75 | ------------ 76 | 77 | The recommended way to install php-gpio is through [composer](http://getcomposer.org). 78 | 79 | Just run these three commands to install it 80 | 81 | ``` bash 82 | $ sudo apt-get install git 83 | $ wget http://getcomposer.org/composer.phar 84 | $ php composer.phar create-project --stability='dev' ronanguilloux/php-gpio intoYourPath 85 | ``` 86 | 87 | Now you can add the autoloader, and you will have access to the library: 88 | 89 | ``` php 90 | setup(17, "out"); 120 | 121 | echo "Turning on pin 17\n"; 122 | $gpio->output(17, 1); 123 | 124 | echo "Sleeping!\n"; 125 | sleep(3); 126 | 127 | echo "Turning off pin 17\n"; 128 | $gpio->output(17, 0); 129 | 130 | echo "Unexporting all pins\n"; 131 | $gpio->unexportAll(); 132 | ``` 133 | 134 | 135 | Understanding I/O permissions 136 | ----------------------------- 137 | 138 | Permissions make sense: 139 | * it's bad practice to run your webserver user (say, Apache2's www-data) as `root` 140 | * it's bad practice to `chmod 777 /dev` only because someone wants to blink a led freely 141 | 142 | Such practices are regularly proposed on RPi forums, but they aren't security-aware 143 | & therefore not recommendable in an Internet environment. 144 | Instead, the good old `/etc/sudoers` file allow your linux users to execute single files 145 | with sudo permissions without password to type. 146 | 147 | 148 | The blinker file solution ("one-file-to-blink-them-all") 149 | -------------------------------------------------------- 150 | 151 | In a PHP-based project, the API can only be used with sudo permissions. But there is a solution to avoid exposing your Raspbery Pi to security issues : Preparing & packaging inside an API client, in a single PHP file, the GPIO operation you need to run. In a hardware-based project, such operations are usualy few in number: blink a led, run a servomotor, etc. Such single PHP file containing your GPIO-related action can be called with determinated parameters from within your web-based application using `exec()` command : 152 | 153 | ```php 154 | (...) 155 | $result = exec('sudo -t /usr/bin/php ./blinker 17 20000'); // calling the API client file 156 | (...) 157 | ``` 158 | 159 | Such one-single PHP file to act as an API client for one GPIO action makes easier to configure specific sudo permissions in your `/etc/sudoers` file, as you'll see below. If you have more hardware operations to run (say, a LED + a servomotor + 2-3 sensors), more dedicated API client files, with their own parameters, is also very OK. 160 | 161 | As an example of such solution, we provide a simple *blinker php file*, executable from the shell & from within your web based app. To run this blinker with sudo permissions but without password inputting, 162 | just allow your `www-data` or your `pi` user to run the blinker script using `exec()`. 163 | With the solution provided below, only one blinker script is needed to manage all your leds, 164 | and your webserver application needs only one php file to be specified in `/etc/sudoers`. 165 | 166 | This is the regular linux-file-permission-system way to do such things, not a dummy `chmod 777` bullshit. 167 | 168 | Edit your `/etc/sudoers` file: 169 | 170 | ``` bash 171 | $ sudo visudo 172 | ``` 173 | 174 | Then add this two lines in your `/etc/sudoers` file : 175 | 176 | ``` 177 | www-data ALL=NOPASSWD:/path/to/the/blinker 178 | ``` 179 | 180 | Replace `/path/to/the/blinker` with your single API client PHP file. 181 | 182 | The blinker file provided now has the sufficient permissions & is ready to use the GPIO API. You do not need to install apache2-suexec nor suPHP. 183 | 184 | You can test the blinker file solution with the `blinkerTest.php` file provided here: 185 | 186 | ``` php 187 | 241 | * Bas Bloemsaat , Raspberry Pi version dependency 242 | * Alex Ciarlillo (@alexciarlillo), Raspberry Pi B+ revision support 243 | * [All contributors](https://github.com/ronanguilloux/php-gpio/contributors) 244 | 245 | 246 | License 247 | ------- 248 | 249 | **php-gpio** is released under the MIT License. 250 | See the bundled LICENSE file for details. 251 | You can find a copy of this software here: https://github.com/ronanguilloux/php-gpio 252 | -------------------------------------------------------------------------------- /bin/quality.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # phploc: https://github.com/sebastianbergmann/phploc 3 | # phpmd: https://github.com/phpmd/phpmd 4 | # phpcpd: https://github.com/sebastianbergmann/phpcpd 5 | # pdepend: https://github.com/pdepend/pdepend 6 | # php-cs-fixer: https://github.com/fabpot/PHP-CS-Fixer 7 | 8 | phploc src/ > METRICS 9 | phpmd src/ text codesize,unusedcode,naming >> METRICS 10 | phpcpd src/ >> METRICS 11 | pdepend src/ >> METRICS 12 | php-cs-fixer fix src/ --dry-run >> METRICS 13 | cat METRICS 14 | rm METRICS 15 | 16 | -------------------------------------------------------------------------------- /blinker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | = (int)($argv[1])) 30 | || (0 >= (int)($argv[2])) 31 | ) { 32 | echo $msg = "This script expect 2 positive integer arguments: please check the README file"; 33 | throw new \Exception($msg); 34 | } 35 | 36 | 37 | $pin = (int)$argv[1]; 38 | $sleeper = (int)$argv[2]; 39 | $gpio = new GPIO(); 40 | 41 | if(!in_array($pin, $gpio->getHackablePins())){ 42 | echo $msg = "$pin is not a hackable gpio pin number"; 43 | throw new \InvalidArgumentException($msg); 44 | } 45 | 46 | $gpio->setup($pin, "out"); 47 | $gpio->output($pin, 1); 48 | usleep($sleeper); 49 | $gpio->output($pin, 0); 50 | 51 | $gpio->unexportAll(); 52 | 53 | -------------------------------------------------------------------------------- /blinkerTest.php: -------------------------------------------------------------------------------- 1 | =5.3.0" 16 | }, 17 | "autoload": { 18 | "psr-0": { "PhpGpio": "src/" } 19 | }, 20 | "extra": { 21 | "branch-alias": { 22 | "dev-master": "1.0.x-dev" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests/ 6 | 7 | 8 | 9 | 10 | ./src/PhpGpio/ 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/PhpGpio/Gpio.php: -------------------------------------------------------------------------------- 1 | getVersion() < 4) { 18 | $this->pins = array( 19 | 0, 1, 4, 7, 8, 9, 20 | 10, 11, 14, 15, 17, 18, 21 | 21, 22, 23, 24, 25 22 | ); 23 | $this->hackablePins = array( 24 | 4, 7, 8, 9, 25 | 10, 11, 17, 18, 26 | 21, 22, 23, 24, 25 27 | ); 28 | } else if($raspi->getVersion() < 16) { 29 | # new GPIO layout (REV2) 30 | $this->pins = array( 31 | 2, 3, 4, 7, 8, 9, 32 | 10, 11, 14, 15, 17, 18, 33 | 22, 23, 24, 25, 27 34 | ); 35 | $this->hackablePins = array( 36 | 4, 7, 8, 9, 37 | 10, 11, 17, 18, 38 | 22, 23, 24, 25, 27 39 | ); 40 | } else { 41 | # new GPIO layout (B+) 42 | $this->pins = array( 43 | 2, 3, 4, 5, 6, 7, 44 | 8, 9, 10, 11, 12, 13, 45 | 14, 15, 16, 17, 18, 19, 46 | 20, 21, 22, 23, 24, 25, 47 | 26, 27 48 | ); 49 | $this->hackablePins = array( 50 | 4, 5, 6, 51 | 12, 13, 16, 17, 18, 19, 52 | 20, 21, 22, 23, 24, 25, 26, 27 53 | ); 54 | } 55 | } 56 | 57 | /** 58 | * getHackablePins : the pins you can hack with. 59 | * @link http://elinux.org/RPi_Low-level_peripherals 60 | * @return integer[] 61 | */ 62 | public function getHackablePins() 63 | { 64 | return $this->hackablePins; 65 | } 66 | 67 | private $directions = array( 68 | GpioInterface::DIRECTION_IN, 69 | GpioInterface::DIRECTION_OUT, 70 | ); 71 | 72 | private $outputs = array( 73 | GpioInterface::IO_VALUE_ON, 74 | GpioInterface::IO_VALUE_OFF, 75 | ); 76 | 77 | // exported pins for when we unexport all 78 | private $exportedPins = array(); 79 | 80 | /** 81 | * Setup pin, takes pin number and direction (in or out) 82 | * 83 | * @param int $pinNo 84 | * @param string $direction 85 | * @return mixed string GPIO value or boolean false 86 | */ 87 | public function setup($pinNo, $direction) 88 | { 89 | if (!$this->isValidPin($pinNo)) { 90 | return false; 91 | } 92 | 93 | // if exported, unexport it first 94 | if ($this->isExported($pinNo)) { 95 | $this->unexport($pinNo); 96 | } 97 | 98 | // Export pin 99 | file_put_contents(GpioInterface::PATH_EXPORT, $pinNo); 100 | 101 | // if valid direction then set direction 102 | if ($this->isValidDirection($direction)) { 103 | file_put_contents(GpioInterface::PATH_GPIO.$pinNo.'/direction', $direction); 104 | } 105 | 106 | // Add to exported pins array 107 | $this->exportedPins[] = $pinNo; 108 | 109 | return $this; 110 | } 111 | 112 | /** 113 | * Get input value 114 | * 115 | * @param int $pinNo 116 | * @return false|string string GPIO value or boolean false 117 | */ 118 | public function input($pinNo) 119 | { 120 | if (!$this->isValidPin($pinNo)) { 121 | return false; 122 | } 123 | if ($this->isExported($pinNo)) { 124 | if ($this->currentDirection($pinNo) != "out") { 125 | return trim(file_get_contents(GpioInterface::PATH_GPIO.$pinNo.'/value')); 126 | } 127 | throw new \Exception('Error!' . $this->currentDirection($pinNo) . ' is a wrong direction for this pin!'); 128 | } 129 | 130 | return false; 131 | } 132 | 133 | /** 134 | * Set output value 135 | * 136 | * @param int $pinNo 137 | * @param string $value 138 | * @return mixed Gpio current instance or boolean false 139 | */ 140 | public function output($pinNo, $value) 141 | { 142 | if (!$this->isValidPin($pinNo)) { 143 | return false; 144 | } 145 | if (!$this->isValidOutput($value)) { 146 | return false; 147 | } 148 | if ($this->isExported($pinNo)) { 149 | if ($this->currentDirection($pinNo) != "in") { 150 | file_put_contents(GpioInterface::PATH_GPIO.$pinNo.'/value', $value); 151 | } else { 152 | throw new \Exception('Error! Wrong Direction for this pin! Meant to be out while it is ' . $this->currentDirection($pinNo)); 153 | } 154 | } 155 | 156 | return $this; 157 | } 158 | 159 | /** 160 | * Unexport Pin 161 | * 162 | * @param int $pinNo 163 | * @return mixed Gpio current instance or boolean false 164 | */ 165 | public function unexport($pinNo) 166 | { 167 | if (!$this->isValidPin($pinNo)) { 168 | return false; 169 | } 170 | if ($this->isExported($pinNo)) { 171 | file_put_contents(GpioInterface::PATH_UNEXPORT, $pinNo); 172 | foreach ($this->exportedPins as $key => $value) { 173 | if($value == $pinNo) unset($key); 174 | } 175 | } 176 | 177 | return $this; 178 | } 179 | 180 | /** 181 | * Unexport all pins 182 | * 183 | * @return Gpio Gpio current instance or boolean false 184 | */ 185 | public function unexportAll() 186 | { 187 | foreach ($this->exportedPins as $pinNo) { 188 | file_put_contents(GpioInterface::PATH_UNEXPORT, $pinNo); 189 | } 190 | $this->exportedPins = array(); 191 | 192 | return $this; 193 | } 194 | 195 | /** 196 | * Check if pin is exported 197 | * 198 | * @return boolean 199 | */ 200 | public function isExported($pinNo) 201 | { 202 | if (!$this->isValidPin($pinNo)) { 203 | return false; 204 | } 205 | 206 | return file_exists(GpioInterface::PATH_GPIO.$pinNo); 207 | } 208 | 209 | /** 210 | * get the pin's current direction 211 | * 212 | * @return false|string string pin's direction value or boolean false 213 | */ 214 | public function currentDirection($pinNo) 215 | { 216 | if (!$this->isValidPin($pinNo)) { 217 | return false; 218 | } 219 | 220 | return trim(file_get_contents(GpioInterface::PATH_GPIO.$pinNo.'/direction')); 221 | } 222 | 223 | /** 224 | * Check for valid direction, in or out 225 | * 226 | * @exception InvalidArgumentException 227 | * @return boolean true 228 | */ 229 | public function isValidDirection($direction) 230 | { 231 | if (!is_string($direction) || empty($direction)) { 232 | throw new \InvalidArgumentException(sprintf('Direction "%s" is invalid (string expected).', $direction)); 233 | } 234 | if (!in_array($direction, $this->directions)) { 235 | throw new \InvalidArgumentException(sprintf('Direction "%s" is invalid (unknown direction).', $direction)); 236 | } 237 | 238 | return true; 239 | } 240 | 241 | /** 242 | * Check for valid output value 243 | * 244 | * @exception InvalidArgumentException 245 | * @return boolean true 246 | */ 247 | public function isValidOutput($output) 248 | { 249 | if (!is_int($output)) { 250 | throw new \InvalidArgumentException(sprintf('Pin value "%s" is invalid (integer expected).', $output)); 251 | } 252 | if (!in_array($output, $this->outputs)) { 253 | throw new \InvalidArgumentException(sprintf('Output value "%s" is invalid (out of exepected range).', $output)); 254 | } 255 | 256 | return true; 257 | } 258 | 259 | /** 260 | * Check for valid pin value 261 | * 262 | * @exception InvalidArgumentException 263 | * @return boolean true 264 | */ 265 | public function isValidPin($pinNo) 266 | { 267 | if (!is_int($pinNo)) { 268 | throw new \InvalidArgumentException(sprintf('Pin number "%s" is invalid (integer expected).', $pinNo)); 269 | } 270 | if (!in_array($pinNo, $this->pins)) { 271 | throw new \InvalidArgumentException(sprintf('Pin number "%s" is invalid (out of exepected range).', $pinNo)); 272 | } 273 | 274 | return true; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/PhpGpio/GpioDevelop.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class GpioDevelop implements GpioInterface 12 | { 13 | /** 14 | * @var array 15 | */ 16 | public $pins = array(14, 15, 17, 18); 17 | 18 | /** 19 | * @var array 20 | */ 21 | public $hackablePins = array(17, 18); 22 | 23 | /** 24 | * @var int 25 | */ 26 | public $inputValue = GpioInterface::IO_VALUE_OFF; 27 | 28 | /** 29 | * @var string 30 | */ 31 | public $direction = GpioInterface::DIRECTION_OUT; 32 | 33 | /** 34 | * getHackablePins : the pins you can hack with. 35 | * @link http://elinux.org/RPi_Low-level_peripherals 36 | * 37 | * @return array 38 | */ 39 | public function getHackablePins() 40 | { 41 | return $this->hackablePins; 42 | } 43 | 44 | /** 45 | * Setup pin, takes pin number and direction (in or out) 46 | * 47 | * @param int $pinNo 48 | * @param string $direction 49 | * 50 | * @return GpioDevelop or boolean false 51 | */ 52 | public function setup($pinNo, $direction) 53 | { 54 | return $this; 55 | } 56 | 57 | /** 58 | * Get input value 59 | * 60 | * @param int $pinNo 61 | * 62 | * @return int GPIO value or boolean false 63 | */ 64 | public function input($pinNo) 65 | { 66 | return $this->inputValue; 67 | } 68 | 69 | /** 70 | * Set output value 71 | * 72 | * @param int $pinNo 73 | * @param string $value 74 | * 75 | * @return GpioDevelop or boolean false 76 | */ 77 | public function output($pinNo, $value) 78 | { 79 | return $this; 80 | } 81 | 82 | /** 83 | * Unexport Pin 84 | * 85 | * @param int $pinNo 86 | * 87 | * @return GpioDevelop or boolean false 88 | */ 89 | public function unexport($pinNo) 90 | { 91 | return $this; 92 | } 93 | 94 | /** 95 | * Unexport all pins 96 | * 97 | * @return GpioDevelop or boolean false 98 | */ 99 | public function unexportAll() 100 | { 101 | return $this; 102 | } 103 | 104 | /** 105 | * Check if pin is exported 106 | * 107 | * @param int $pinNo 108 | * 109 | * @return boolean 110 | */ 111 | public function isExported($pinNo) 112 | { 113 | return in_array($pinNo, $this->pins) || in_array($pinNo, $this->hackablePins); 114 | } 115 | 116 | /** 117 | * get the pin's current direction 118 | * 119 | * @param int $pinNo 120 | * 121 | * @return string pin's direction value or boolean false 122 | */ 123 | public function currentDirection($pinNo) 124 | { 125 | return $this->direction; 126 | } 127 | 128 | /** 129 | * Check for valid direction, in or out 130 | * 131 | * @param string $direction 132 | * 133 | * @return boolean 134 | */ 135 | public function isValidDirection($direction) 136 | { 137 | return $direction == GpioInterface::DIRECTION_IN || $direction == GpioInterface::DIRECTION_OUT; 138 | } 139 | 140 | /** 141 | * Check for valid output value 142 | * 143 | * @param mixed $output 144 | * 145 | * @return boolean 146 | */ 147 | public function isValidOutput($output) 148 | { 149 | return $output == GpioInterface::IO_VALUE_ON || $output == GpioInterface::IO_VALUE_OFF; 150 | } 151 | 152 | /** 153 | * Check for valid pin value 154 | * 155 | * @param int $pinNo 156 | * 157 | * @return boolean 158 | */ 159 | public function isValidPin($pinNo) 160 | { 161 | return in_array($pinNo, $this->pins) || in_array($pinNo, $this->hackablePins); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/PhpGpio/GpioInterface.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface GpioInterface 11 | { 12 | const DIRECTION_IN = 'in'; 13 | const DIRECTION_OUT = 'out'; 14 | 15 | const IO_VALUE_ON = 1; 16 | const IO_VALUE_OFF = 0; 17 | 18 | const PATH_GPIO = '/sys/class/gpio/gpio'; 19 | const PATH_EXPORT = '/sys/class/gpio/export'; 20 | const PATH_UNEXPORT = '/sys/class/gpio/unexport'; 21 | 22 | /** 23 | * getHackablePins : the pins you can hack with. 24 | * @link http://elinux.org/RPi_Low-level_peripherals 25 | * 26 | * @return array 27 | */ 28 | public function getHackablePins(); 29 | 30 | /** 31 | * Setup pin, takes pin number and direction (in or out) 32 | * 33 | * @param int $pinNo 34 | * @param string $direction 35 | * 36 | * @return GpioDevelop string GPIO value or boolean false 37 | */ 38 | public function setup($pinNo, $direction); 39 | 40 | /** 41 | * Get input value 42 | * 43 | * @param int $pinNo 44 | * 45 | * @return integer string GPIO value or boolean false 46 | */ 47 | public function input($pinNo); 48 | 49 | /** 50 | * Set output value 51 | * 52 | * @param int $pinNo 53 | * @param string $value 54 | * 55 | * @return GpioDevelop Gpio current instance or boolean false 56 | */ 57 | public function output($pinNo, $value); 58 | 59 | /** 60 | * Unexport Pin 61 | * 62 | * @param int $pinNo 63 | * 64 | * @return GpioDevelop Gpio current instance or boolean false 65 | */ 66 | public function unexport($pinNo); 67 | 68 | /** 69 | * Unexport all pins 70 | * 71 | * @return GpioDevelop Gpio current instance or boolean false 72 | */ 73 | public function unexportAll(); 74 | 75 | /** 76 | * Check if pin is exported 77 | * 78 | * @param int $pinNo 79 | * 80 | * @return boolean 81 | */ 82 | public function isExported($pinNo); 83 | 84 | /** 85 | * get the pin's current direction 86 | * 87 | * @param int $pinNo 88 | * 89 | * @return string string pin's direction value or boolean false 90 | */ 91 | public function currentDirection($pinNo); 92 | 93 | /** 94 | * Check for valid direction, in or out 95 | * 96 | * @param string $direction 97 | * 98 | * @return boolean 99 | */ 100 | public function isValidDirection($direction); 101 | 102 | /** 103 | * Check for valid output value 104 | * 105 | * @param mixed $output 106 | * 107 | * @return boolean 108 | */ 109 | public function isValidOutput($output); 110 | 111 | /** 112 | * Check for valid pin value 113 | * 114 | * @param int $pinNo 115 | * 116 | * @return boolean 117 | */ 118 | public function isValidPin($pinNo); 119 | } 120 | -------------------------------------------------------------------------------- /src/PhpGpio/Pi.php: -------------------------------------------------------------------------------- 1 | bus; 25 | } 26 | 27 | /** 28 | * Set-Accesssor 29 | */ 30 | public function setBus($value) 31 | { 32 | // ? is a non empty string, & a valid file path 33 | if (empty($value) || !is_string($value) || !file_exists($value)) { 34 | throw new \InvalidArgumentException("$value is not a valid w1 bus path"); 35 | } 36 | 37 | // ? is a regular w1-bus path on a Raspbery ? 38 | if (!strstr($value, self::BASEPATH)) { 39 | throw new \InvalidArgumentException("$value does not seem to be a regular w1 bus path"); 40 | } 41 | 42 | $this->bus = $value; 43 | } 44 | 45 | /** 46 | * Setup 47 | * 48 | * @return $this 49 | */ 50 | public function __construct() 51 | { 52 | $this->bus = $this->guessBus(); 53 | 54 | return $this; 55 | } 56 | 57 | /** 58 | * guessBus: Guess the thermal sensor bus folder path 59 | * 60 | * the directory 28-*** indicates the DS18B20 thermal sensor is wired to the bus 61 | * (28 is the family ID) and the unique ID is a 12-chars numerical digit 62 | * 63 | * @return string $busPath 64 | */ 65 | public function guessBus() 66 | { 67 | $busFolders = glob(self::BASEPATH . '*'); // predictable path on a Raspberry Pi 68 | if (0 === count($busFolders)) { 69 | return false; 70 | } 71 | $busPath = $busFolders[0]; // get the first thermal sensor found 72 | 73 | return $busPath . '/w1_slave'; 74 | } 75 | 76 | /** 77 | * Read 78 | * 79 | * @param array $args 80 | * @return float $value 81 | */ 82 | public function read($args = array()) 83 | { 84 | if (!is_string($this->bus) || !file_exists($this->bus)) { 85 | throw new \Exception("No bus file found: please run sudo modprobe w1-gpio; sudo modprobe w1-therm & check the guessBus() method result"); 86 | } 87 | $raw = file_get_contents($this->bus); 88 | $raw = str_replace("\n", "", $raw); 89 | $boom = explode('t=',$raw); 90 | 91 | return floatval($boom[1]/1000); 92 | } 93 | 94 | /** 95 | * Write 96 | * 97 | * @param array $args 98 | * @return boolean 99 | */ 100 | public function write($args = array()) 101 | { 102 | return false; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/PhpGpio/Sensors/MCP3002.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class MCP3002 implements SensorInterface { 11 | 12 | private $_clockpin; 13 | private $_mosipin; 14 | private $_misopin; 15 | private $_cspin; 16 | 17 | private $_gpio; 18 | 19 | /** 20 | * Constructor with the sused GPIOs. 21 | * 22 | * Use: 23 | * $adc = new MCP3002(11, 10, 9, 8); 24 | * $value = adc->read(array('channel' => 0)); 25 | * echo $value; 26 | * 27 | * @param integer $clockpin The clock (CLK) pin (ex. 11) 28 | * @param integer $mosipin The Master Out Slave In (MOSI) pin (ex. 10) 29 | * @param integer $misopin The Master In Slave Out (MISO) pin (ex. 9) 30 | * @param integer $cspin The Chip Select (CSna) pin (ex. 8) 31 | */ 32 | public function __construct($clockpin, $mosipin, $misopin, $cspin) { 33 | $this->_gpio = new GPIO(); 34 | 35 | $this->_clockpin = $clockpin; 36 | $this->_mosipin = $mosipin; 37 | $this->_misopin = $misopin; 38 | $this->_cspin = $cspin; 39 | 40 | $this->_gpio->setup($this->_mosipin, "out"); 41 | $this->_gpio->setup($this->_misopin, "in"); 42 | $this->_gpio->setup($this->_clockpin, "out"); 43 | $this->_gpio->setup($this->_cspin, "out"); 44 | } 45 | 46 | /** 47 | * Read the specified channel. 48 | * You should specify the channel (0|1) to read with the channel argument. 49 | * 50 | * @param array $args 51 | * @return integer 52 | */ 53 | public function read($args = array()) { 54 | $channel = $args['channel']; 55 | if (!is_integer($channel) || !in_array($channel, array(0, 1))) { 56 | echo $msg = "Only 2 channels are available on a Mcp3002: 0 or 1"; 57 | throw new \InvalidArgumentException($msg); 58 | } 59 | 60 | // init comm 61 | $this->_gpio->output($this->_cspin, 1); 62 | $this->_gpio->output($this->_clockpin, 0); 63 | $this->_gpio->output($this->_cspin, 0); 64 | 65 | // channel selection 66 | $cmdout = (6 + $channel) << 5; 67 | for ($i = 0; $i < 3; $i++) { 68 | if ($cmdout & 0x80) { 69 | $this->_gpio->output($this->_mosipin, 1); 70 | } else { 71 | $this->_gpio->output($this->_mosipin, 0); 72 | } 73 | $cmdout <<= 1; 74 | $this->_gpio->output($this->_clockpin, 1); 75 | $this->_gpio->output($this->_clockpin, 0); 76 | } 77 | 78 | $adcout = 0; 79 | // read in one empty bit, one null bit and 10 ADC bits 80 | for ($i = 0; $i < 12; $i++) { 81 | $this->_gpio->output($this->_clockpin, 1); 82 | $this->_gpio->output($this->_clockpin, 0); 83 | $adcout <<= 1; 84 | if ($this->_gpio->input($this->_misopin)) { 85 | $adcout |= 0x1; 86 | } 87 | } 88 | 89 | $this->_gpio->output($this->_cspin, 1); 90 | return $adcout >> 1; 91 | } 92 | 93 | public function write($args = array()) { 94 | return false; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/PhpGpio/Sensors/SensorInterface.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class GpioTest extends \PhpUnit_Framework_TestCase 11 | { 12 | private $gpio; 13 | private $rpi ='raspberrypi'; 14 | private $hackablePins = array(); 15 | 16 | public function setUp() 17 | { 18 | $this->gpio = new Gpio(); 19 | 20 | // defaut should be: $this->hackablePins = $this->gpio->getHackablePins(); 21 | // but in this test set, the Raspi is wired to a breadboard 22 | // and the 4th Gpio pin is reserved to read the DS18B20 sensor. 23 | // Other available gpio pins are connected to LEDs 24 | $this->hackablePins = array( 25 | 17, 18, 21, 22, 23,24, 25 26 | ); 27 | } 28 | 29 | /** 30 | * @outputBuffering enabled 31 | */ 32 | public function assertPreconditionOrMarkTestSkipped() 33 | { 34 | if ($this->rpi !== $nodename = exec('uname --nodename')) { 35 | $warning = sprintf(" Precondition is not met : %s is not a %s machine! ", $nodename, $this->rpi); 36 | $this->markTestSkipped($warning); 37 | } 38 | } 39 | 40 | /** 41 | * Setting up gpio pins 42 | */ 43 | public function testSetupWithRightParamaters() 44 | { 45 | $this->assertPreconditionOrMarkTestSkipped(); 46 | foreach ($this->hackablePins as $pin) { 47 | $result = $this->gpio->setup($pin, 'out'); 48 | $this->assertTrue($result instanceof Gpio); 49 | } 50 | } 51 | 52 | /** 53 | * Outputting gpio pins (ON) 54 | * @depends testSetupWithRightParamaters 55 | */ 56 | public function testOutPutWithRightParametersOn() 57 | { 58 | foreach ($this->hackablePins as $pin) { 59 | $result = $this->gpio->output($pin, 1); 60 | $this->assertTrue($result instanceof Gpio); 61 | } 62 | } 63 | 64 | /** 65 | * Outputting gpio pins (OFF) 66 | * @depends testOutPutWithRightParametersOn 67 | */ 68 | public function testOutPutWithRightParametersOut() 69 | { 70 | sleep(1); 71 | foreach ($this->hackablePins as $pin) { 72 | $result = $this->gpio->output($pin, 0); 73 | $this->assertTrue($result instanceof Gpio); 74 | } 75 | } 76 | 77 | /** 78 | * @expectedException InvalidArgumentException 79 | */ 80 | public function testSetupWithNegativePinAndRightDirection() 81 | { 82 | $this->gpio->setup(-1, 'out'); 83 | } 84 | 85 | /** 86 | * @expectedException InvalidArgumentException 87 | */ 88 | public function testSetupWithNullPinAndRightDirection() 89 | { 90 | $this->gpio->setup(null, 'out'); 91 | } 92 | 93 | /** 94 | * @expectedException InvalidArgumentException 95 | */ 96 | public function testSetupWithWrongPinAndRightDirection() 97 | { 98 | $this->gpio->setup('wrongPin', 'out'); 99 | } 100 | 101 | /** 102 | * @expectedException InvalidArgumentException 103 | */ 104 | public function testSetupWithRightPinAndWrongDirection() 105 | { 106 | $this->assertPreconditionOrMarkTestSkipped(); 107 | $this->gpio->setup(17, 'wrongDirection'); 108 | } 109 | 110 | /** 111 | * @expectedException InvalidArgumentException 112 | */ 113 | public function testSetupWithRightPinAndNullDirection() 114 | { 115 | $this->assertPreconditionOrMarkTestSkipped(); 116 | $this->gpio->setup(17, null); 117 | } 118 | 119 | /** 120 | * @expectedException PHPUnit_Framework_Error_Warning 121 | */ 122 | public function testSetupWithMissingArguments() 123 | { 124 | $this->gpio->setup(17); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /tests/PhpGpio/Tests/PiTest.php: -------------------------------------------------------------------------------- 1 | , Bas Bloemsaat 9 | */ 10 | class PiTest extends \PhpUnit_Framework_TestCase 11 | { 12 | private $gpio; 13 | private $rpi ='raspberrypi'; 14 | private $pi; 15 | 16 | public function setUp() 17 | { 18 | $this->pi = new Pi(); 19 | } 20 | 21 | /** 22 | * @outputBuffering enabled 23 | */ 24 | public function assertPreconditionOrMarkTestSkipped() 25 | { 26 | if ($this->rpi !== $nodename = exec('uname --nodename')) { 27 | $warning = sprintf(" Precondition is not met : %s is not a %s machine! ", $nodename, $this->rpi); 28 | $this->markTestSkipped($warning); 29 | } 30 | } 31 | 32 | public function testGetVersion() 33 | { 34 | $this->assertPreconditionOrMarkTestSkipped(); 35 | $this->assertTrue($this->pi instanceof Pi); 36 | $version = $this->pi->getVersion(); 37 | $this->assertInternalType('integer' , $version); 38 | 39 | } 40 | 41 | public function testGetCpuLoad() 42 | { 43 | $this->assertPreconditionOrMarkTestSkipped(); 44 | $this->assertTrue($this->pi instanceof Pi); 45 | $result = $this->pi->getCpuLoad(); 46 | $this->assertTrue(is_array($result)); 47 | $this->assertCount(3, $result); 48 | 49 | } 50 | 51 | public function testGetCpuTemp() 52 | { 53 | $this->assertPreconditionOrMarkTestSkipped(); 54 | $this->assertTrue($this->pi instanceof Pi); 55 | $result = $this->pi->getCpuTemp(); 56 | $this->assertInternalType('float' , $result); 57 | 58 | } 59 | 60 | public function testGetGpuTemp() 61 | { 62 | $this->assertPreconditionOrMarkTestSkipped(); 63 | $this->assertTrue($this->pi instanceof Pi); 64 | $result = $this->pi->getGpuTemp(); 65 | $this->assertInternalType('float' , $result); 66 | 67 | } 68 | 69 | public function testGetCpuFrequence() 70 | { 71 | $this->assertPreconditionOrMarkTestSkipped(); 72 | $this->assertTrue($this->pi instanceof Pi); 73 | $result = $this->pi->getCpuFrequency(); 74 | $this->assertInternalType('float' , $result); 75 | 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /tests/PhpGpio/Tests/Sensors/DS18B20Test.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class DS18B20Test extends \PhpUnit_Framework_TestCase 11 | { 12 | private $sensor; 13 | private $rpi = 'raspberrypi'; 14 | 15 | public function setUp() 16 | { 17 | $this->sensor = new DS18B20(); 18 | } 19 | 20 | /** 21 | * @outputBuffering enabled 22 | */ 23 | public function assertPreconditionOrMarkTestSkipped() 24 | { 25 | if ($this->rpi !== $nodename = exec('uname --nodename')) { 26 | $warning = sprintf(" Precondition is not met : %s is not a %s machine! ", $nodename, $this->rpi); 27 | $this->markTestSkipped($warning); 28 | } 29 | } 30 | 31 | /** 32 | * @expectedException InvalidArgumentException 33 | */ 34 | public function testSetBusWithWrongNonExisitingFilePath() 35 | { 36 | //$this->assertPreconditionOrMarkTestSkipped(); 37 | $result = $this->sensor->setBus('/foo/bar/.baz'); 38 | } 39 | 40 | /** 41 | * @expectedException InvalidArgumentException 42 | */ 43 | public function testSetBusWithWrongNullParameter() 44 | { 45 | $result = $this->sensor->setBus(null); 46 | } 47 | 48 | /** 49 | * @expectedException InvalidArgumentException 50 | */ 51 | public function testSetBusWithWrongExistingFile() 52 | { 53 | $result = $this->sensor->setBus('/etc/hosts'); 54 | } 55 | 56 | /** 57 | * @expectedException InvalidArgumentException 58 | */ 59 | public function testSetBusWithWrongStringParameter() 60 | { 61 | $result = $this->sensor->setBus('foo'); 62 | } 63 | 64 | /** 65 | * @expectedException InvalidArgumentException 66 | */ 67 | public function testSetBusWithWrongIntParameter() 68 | { 69 | $result = $this->sensor->setBus(1); 70 | } 71 | 72 | /** 73 | * a valid guessBus test 74 | */ 75 | public function testValidGuessBus() 76 | { 77 | $this->assertPreconditionOrMarkTestSkipped(); 78 | $result = $this->sensor->guessBus(); 79 | $this->assertTrue(file_exists((string)$result)); 80 | } 81 | 82 | /** 83 | * a valid read test 84 | */ 85 | public function testRead() 86 | { 87 | $this->assertPreconditionOrMarkTestSkipped(); 88 | $result = $this->sensor->read(); 89 | $this->assertTrue(is_float($result)); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('PhpGpio\Tests', __DIR__); 49 | --------------------------------------------------------------------------------