From 633e254958fb66ebd82c8fc2d4ebcfcc29046820 Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Fri, 6 Jun 2014 19:50:17 +0200 Subject: [PATCH] Implemented base of GpioMapperInterface with WiringPiMapper --- composer.json | 7 +- composer.lock | 51 +++++++++ examples/basic.php | 42 +++++-- lib/Gpio.php | 82 ++++++++++---- lib/GpioMapper/WiringPiMapper.php | 86 +++++++++++++++ lib/GpioMapperInterface.php | 37 +++++++ lib/GpioPin.php | 176 ++++++++++++++++++++++++++++++ 7 files changed, 445 insertions(+), 36 deletions(-) create mode 100644 composer.lock create mode 100644 lib/GpioMapper/WiringPiMapper.php create mode 100644 lib/GpioMapperInterface.php create mode 100644 lib/GpioPin.php diff --git a/composer.json b/composer.json index 9c8c11e..61c8141 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,11 @@ } ], "require": { - + "noccylabs/sansi": "dev-master" + }, + "autoload": { + "psr-4": { + "NoccyLabs\\Gpio\\": "lib/" + } } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..5cdc18d --- /dev/null +++ b/composer.lock @@ -0,0 +1,51 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "6ede4cbc5ab4ef05b58811c306b5376f", + "packages": [ + { + "name": "noccylabs/sansi", + "version": "dev-master", + "source": { + "type": "git", + "url": "http://satis.noccylabs.info/packages/php-sansi.git", + "reference": "9e347698a898dc966802987fc4fbfa7bec7225b0" + }, + "type": "library", + "autoload": { + "psr-4": { + "NoccyLabs\\Sansi\\": "lib/" + } + }, + "license": [ + "GPL-3.0" + ], + "authors": [ + { + "name": "Christopher Vagnetoft", + "email": "cvagnetoft@gmail.com" + } + ], + "description": "Simple ANSI User Interface", + "time": "2014-06-05 12:10:14" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": { + "noccylabs/sansi": 20 + }, + "platform": [ + + ], + "platform-dev": [ + + ] +} diff --git a/examples/basic.php b/examples/basic.php index 5404211..b970d0e 100644 --- a/examples/basic.php +++ b/examples/basic.php @@ -2,21 +2,41 @@ require_once __DIR__."/../vendor/autoload.php"; -use NoccyLabs\Linux\Gpio; +use NoccyLabs\Gpio\Gpio; +use NoccyLabs\Gpio\GpioMapper\WiringPiMapper; try { - $gpio = new Gpio\Gpio(); + $gpio = new Gpio(); } catch (Gpio\Exception $e) { error_log("Error: {$e}"); } -$pin = $gpio->export(0); -$pin->setInterrupt("rising", function() { - echo "Interrupt on GPIO0\n"; -}); +// The mapper translates GPIO to logical pins and vice versa +$gpio->setMapper( new WiringPiMapper(2) ); + +// Access logical pin 0, since we got a mapper assigned. Otherwise this would +// be the actual GPIO0 pin. +$led = $gpio[0] + ->export() + ->setDirection("output") + ->setValue(0) + ->setLabel("red led") + ->dumpStatus(true); + +$btn = $gpio[1] + ->setDirection("input") + ->setValue(0) + ->setEdge("rising") + ->setHandler(function() { echo "INTERRUPT!\n"; }) + ->setLabel("button 1") + ->dumpStatus(true); + +$gpio[2] + ->setDirection("input") + ->setValue(0) + ->setEdge("rising") + ->setHandler(function() { echo "INTERRUPT!\n"; }) + ->setLabel("button 2") + ->dumpStatus(true); + -while(true) { - // We need to call this for our interrupt status to be polled - $gpio->update(); - usleep(10000); -} diff --git a/lib/Gpio.php b/lib/Gpio.php index 48d9cc2..c3ec036 100644 --- a/lib/Gpio.php +++ b/lib/Gpio.php @@ -1,38 +1,72 @@ + */ + namespace NoccyLabs\Gpio; -class Gpio +class Gpio implements \ArrayAccess { - public function __construct() - {} + protected $gpio = array(); + + /** @var NoccyLabs\Gpio\GpioMapperInterface */ + protected $mapper; - // call export before getpin - public function getPin($pinno) - {} + public function refresh() + { + $read = $this->fd_gpio; + $write = array(); + $except = $read; + select($read, $write, $except, 0); + foreach($except as $fd) { + $pin = $this->getPinFromFd($fd); + $pin->doInterrupt(); + + } - // export the pin - public function exportPin($pinno) - {} + } - // unexport the pin - public function unexportPin($pinno) - {} + public function setMapper(GpioMapperInterface $mapper=null) + { + $this->mapper = $mapper; + return $this; + } - // set handler, pass null to disable interrupts - public function setInterruptHandler(GpioPin $pin, callable $handler=null) - {} + public function offsetGet($index) + { + if ($this->mapper) { $index = $this->mapper->mapLogicalToGpioPin($index); } + if (empty($this->gpio[$index])) { + $this->gpio[$index] = new GpioPin($index); + } + return $this->gpio[$index]; + } - // get the interrupt handler for the pin - public function getInterruptHandler(GpioPin $pin) - {} + public function offsetExists($index) + { + return array_key_exists($index, $this->gpio); + } - // set edge - public function setInterruptEdge(GpioPin $pin, $edge) - {} + + public function offsetSet($index,$value) + { throw new \Exception(); } - // get edge - public function getInterruptEdge(GpioPin $pin) - {} + public function offsetUnset($index) + { + unset($this->gpio[$index]); + } } diff --git a/lib/GpioMapper/WiringPiMapper.php b/lib/GpioMapper/WiringPiMapper.php new file mode 100644 index 0000000..9e1799a --- /dev/null +++ b/lib/GpioMapper/WiringPiMapper.php @@ -0,0 +1,86 @@ + + */ + +namespace NoccyLabs\Gpio\GpioMapper; + +use NoccyLabs\Gpio\GpioMapperInterface; + +class WiringPiMapper implements GpioMapperInterface +{ + protected $version; + + public function __construct($version=1) + { + $this->version = (int)$version; + } + + /** {@inheritdoc} */ + public function mapGpioToLogicalPin($gpio) + { + switch ($gpio) { + case 17: return 0; + case 18: return 1; + // .. + + } + } + + /** {@inheritdoc} */ + public function mapLogicalToGpioPin($logical) + { + switch ($logical) { + case 0: return 17; + case 1: return 18; + case 2: + if ($this->version == 2) { + return 27; + } + return 21; + case 3: return 22; + case 4: return 23; + case 5: return 24; + case 6: return 25; + case 7: return 11; + case 8: + if ($this->version == 2) { + return 2; + } + return 0; + case 9: + if ($this->version == 2) { + return 3; + } + return 1; + case 10: return 8; + case 11: return 7; + case 12: return 10; + case 13: return 9; + case 14: return 11; + case 15: return 14; + case 16: return 15; + case 17: return 28; + case 18: return 29; + case 19: return 30; + case 20: return 31; + default: + throw new \Exception; + } + } + +} diff --git a/lib/GpioMapperInterface.php b/lib/GpioMapperInterface.php new file mode 100644 index 0000000..11e2ce9 --- /dev/null +++ b/lib/GpioMapperInterface.php @@ -0,0 +1,37 @@ + + */ + +namespace NoccyLabs\Gpio; + +interface GpioMapperInterface +{ + /** + * Map the GPIO pin numbers to a more logical number set, such as that of + * wiringPi. + */ + public function mapGpioToLogicalPin($gpio); + + /** + * Map a logical pin number, such as the ones used by wiringPi, into the + * actual GPIO pin number. + * + */ + public function mapLogicalToGpioPin($logical); + +} diff --git a/lib/GpioPin.php b/lib/GpioPin.php new file mode 100644 index 0000000..86c6ed1 --- /dev/null +++ b/lib/GpioPin.php @@ -0,0 +1,176 @@ + + */ + +namespace NoccyLabs\Gpio; + +use NoccyLabs\Sansi\Charset as CS; + +/** + * GPIO pin implementation. This class wraps all interaction with a GPIO pin, + * thus decoupling it from the main Gpio class. + * + */ +class GpioPin +{ + protected $fd; + + protected $pin; + + protected $value = 0; + + protected $direction; + + protected $edge; + + protected $handler; + + protected $label; + + public function __construct($pin) + { + $this->pin = (int)$pin; + //$this->fd = fopen("/sys/class/gpio/gpio{$pin}/value", "rb"); + } + + public function __destruct() + { + //fclose($this->fd); + } + + public function setDirection($direction) + { + if (!in_array($direction, array("input", "output"))) { + throw new \Exception; + } + $this->direction = $direction; + //file_put_contents("/sys/class/gpio/{$this->pin}/direction", $direction); + return $this; + } + + public function getDirection() + { + return $this->direction; + } + + public function setValue($value) + { + $this->value = (bool)$value; + //file_put_contents("/sys/class/gpio/{$this->pin}/value", (int)$this->value); + return $this; + } + + public function getValue() + { + return $this->value; + } + + public function setLabel($label) + { + $this->label = (string)$label; + return $this; + } + + public function getLabel() + { + return $this->label; + } + + public function export() + { + /* + file_put_contents("/sys/class/gpio/export", $this->pin); + if (!file_exists("/sys/class/gpio/gpio{$this->pin}")) { + throw new \Exception(); + } + */ + return $this; + } + + public function unexport() + { + //file_put_contents("/sys/class/gpio/unexport", $this->pin); + return $this; + } + + public function setEdge($edge) + { + if (!in_array($edge, array("rising", "falling", "both", "none"))) { + throw new \Exception; + } + $this->edge = $edge; + //file_put_contents("/sys/class/gpio/{$this->pin}/edge", $edge); + return $this; + } + + public function getEdge() + { + return $this->edge; + } + + public function setHandler(callable $handler=null) + { + return $this; + } + + public function doInterrupt() + { + fseek($this->fd,0,SEEK_SET); + $val = fgets($this->fd); + } + + public function dumpStatus($ansi=false) + { + if ($ansi) { + $status = "\e[43;37;1m GPIO{$this->pin} \e[0m\n"; + $direction = "\e[36;1m". + ($this->direction=="input"?(CS::chr(0x2190)):(CS::chr(0x2192))). + "\e[0m ".$this->direction; + $edge = "\e[33;1m"; + if ($this->edge == "rising") { + $edge.= CS::chr(0x21A5)."\e[0m rising"; + } elseif ($this->edge == "falling") { + $edge.= CS::chr(0x21A7)."\e[0m falling"; + } else { + $edge.= "\e[0m".$this->edge; + } + } else { + $status = "********** GPIO{$this->pin} **********\n"; + $direction = $this->direction; + } + + + foreach(array( + "Direction" => $direction, + "Value" => ($this->value?1:0), + "Edge" => $edge, + "Label" => $this->label, + "Hardware" => "unknown", + "Int count" => 0 + ) as $k=>$v) { + if ($ansi) { + $status.= sprintf(" \e[32;1m%10s\e[0m: \e[0m%s\e[0m\n", $k, $v); + } else { + $status.= sprintf(" %-10s: %s\n", $k, $v); + } + } + error_log($status); + return $this; + } + +}