From 9c7db7c2913529493d2d3bfced8d3d842ad8bf0e Mon Sep 17 00:00:00 2001 From: Andrew Carter Date: Sat, 14 May 2016 11:32:15 +0100 Subject: [PATCH] Split input and output pins --- README.md | 41 +++---- src/GPIO.php | 51 +++++++++ src/GPIOInterface.php | 35 ++++++ src/{ => Interrupt}/InterruptWatcher.php | 10 +- .../InterruptWatcherInterface.php | 14 ++- src/InterruptWatcherFactory.php | 32 ------ src/InterruptWatcherFactoryInterface.php | 13 --- src/Pin/InputPin.php | 44 ++++++++ src/Pin/InputPinInterface.php | 25 +++++ src/Pin/OutputPin.php | 33 ++++++ src/Pin/OutputPinInterface.php | 13 +++ src/{ => Pin}/Pin.php | 56 +++------ src/Pin/PinInterface.php | 33 ++++++ src/PinFactory.php | 29 ----- src/PinFactoryInterface.php | 15 --- src/PinInterface.php | 76 ------------- test/FileSystem/VFS.php | 22 ++++ test/Interrupt/InterruptWatcherTest.php | 76 +++++++++++++ test/InterruptWatcherTest.php | 57 ---------- test/Pin/InputPinTest.php | 32 ++++++ test/Pin/OutputPinTest.php | 25 +++++ test/PinTest.php | 106 ------------------ 22 files changed, 433 insertions(+), 405 deletions(-) create mode 100644 src/GPIO.php create mode 100644 src/GPIOInterface.php rename src/{ => Interrupt}/InterruptWatcher.php (89%) rename src/{ => Interrupt}/InterruptWatcherInterface.php (57%) delete mode 100644 src/InterruptWatcherFactory.php delete mode 100644 src/InterruptWatcherFactoryInterface.php create mode 100644 src/Pin/InputPin.php create mode 100644 src/Pin/InputPinInterface.php create mode 100644 src/Pin/OutputPin.php create mode 100644 src/Pin/OutputPinInterface.php rename src/{ => Pin}/Pin.php (65%) create mode 100644 src/Pin/PinInterface.php delete mode 100644 src/PinFactory.php delete mode 100644 src/PinFactoryInterface.php delete mode 100644 src/PinInterface.php create mode 100644 test/FileSystem/VFS.php create mode 100644 test/Interrupt/InterruptWatcherTest.php delete mode 100644 test/InterruptWatcherTest.php create mode 100644 test/Pin/InputPinTest.php create mode 100644 test/Pin/OutputPinTest.php delete mode 100644 test/PinTest.php diff --git a/README.md b/README.md index 2067646..0be3ef8 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,14 @@ A library for low level access to the GPIO pins on a Raspberry Pi ### Setting Output Pins ```php -use PiPHP\GPIO\PinInterface; -use PiPHP\GPIO\PinFactory; +use PiPHP\GPIO\GPIO; +use PiPHP\GPIO\Pin\PinInterface; -// Retrieve a pin object using the factory class -$pin = (new PinFactory)->getPin(18); +// Create a GPIO object +$gpio = new GPIO(); -// Export the pin (so that it is available to use) -$pin->export(); - -// Set the pin as an output pin -$pin->setDirection(PinInterface::DIRECTION_OUT); +// Retrieve pin 18 and configure it as an output pin +$pin = $gpio->getOutputPin(18); // Set the value of the pin high (turn it on) $pin->setValue(PinInterface::VALUE_HIGH); @@ -31,27 +28,23 @@ $pin->setValue(PinInterface::VALUE_HIGH); ### Input Pin Interrupts ```php -use PiPHP\GPIO\InterruptWatcherFactory; -use PiPHP\GPIO\PinInterface; -use PiPHP\GPIO\PinFactory; - -// Retrieve a pin object using the factory class -$pin = (new PinFactory)->getPin(18); +use PiPHP\GPIO\GPIO; +use PiPHP\GPIO\Pin\InputPinInterface; -// Export the pin (so that it is available to use) -$pin->export(); +// Create a GPIO object +$gpio = new GPIO(); -// Set the pin as an input pin -$pin->setDirection(PinInterface::DIRECTION_IN); +// Retrieve pin 18 and configure it as an input pin +$pin = $gpio->getInputPin(18); -// Configure the pin to trigger interrupts on both rising and falling edges -$pin->setEdge(PinInterface::EDGE_BOTH); +// Configure interrupts for both rising and falling edges +$pin->setEdge(InputPinInterface::EDGE_BOTH); -// Create an interrupt watcher using the factory class -$interruptWatcher = (new InterruptWatcherFactory)->createWatcher(); +// Create an interrupt watcher +$interruptWatcher = $gpio->createWatcher(); // Register a callback to be triggered on pin interrupts -$interruptWatcher->register($pin, function (PinInterface $pin, $value) { +$interruptWatcher->register($pin, function (InputPinInterface $pin, $value) { echo 'Pin ' . $pin->getNumber() . ' changed to: ' . $value . PHP_EOL; // Returning false will make the watcher return false immediately diff --git a/src/GPIO.php b/src/GPIO.php new file mode 100644 index 0000000..13a39d4 --- /dev/null +++ b/src/GPIO.php @@ -0,0 +1,51 @@ +fileSystem = $fileSystem ?: new FileSystem(); + $this->streamSelect = $streamSelect ?: 'stream_select'; + } + + /** + * {@inheritdoc} + */ + public function getInputPin($number) + { + return new InputPin($this->fileSystem, $number); + } + + /** + * {@inheritdoc} + */ + public function getOutputPin($number) + { + return new OutputPin($this->fileSystem, $number); + } + + /** + * {@inheritdoc} + */ + public function createWatcher() + { + return new InterruptWatcher($this->fileSystem, $this->streamSelect); + } +} diff --git a/src/GPIOInterface.php b/src/GPIOInterface.php new file mode 100644 index 0000000..9cef821 --- /dev/null +++ b/src/GPIOInterface.php @@ -0,0 +1,35 @@ +getNumber(); if (!isset($this->streams[$pinNumber])) { $file = '/sys/class/gpio/gpio' . $pinNumber . '/value'; $this->streams[$pinNumber] = $this->fileSystem->open($file, 'r'); + stream_set_blocking($this->streams[$pinNumber], false); } $this->pins[$pinNumber] = $pin; @@ -55,7 +57,7 @@ public function register(PinInterface $pin, callable $callback) /** * {@inheritdoc} */ - public function unregister(PinInterface $pin) + public function unregister(InputPinInterface $pin) { $pinNumber = $pin->getNumber(); @@ -90,7 +92,7 @@ public function watch($timeout) $triggers = []; foreach ($except as $pinNumber => $stream) { - $value = fread($stream, 1024); + $value = fread($stream, 1); @rewind($stream); if ($value !== false) { diff --git a/src/InterruptWatcherInterface.php b/src/Interrupt/InterruptWatcherInterface.php similarity index 57% rename from src/InterruptWatcherInterface.php rename to src/Interrupt/InterruptWatcherInterface.php index e2fe666..c802934 100644 --- a/src/InterruptWatcherInterface.php +++ b/src/Interrupt/InterruptWatcherInterface.php @@ -1,23 +1,25 @@ fileSystem = $fileSystem ?: new FileSystem(); - $this->streamSelect = $streamSelect; - } - - /** - * {@inheritdoc} - */ - public function createWatcher() - { - return new InterruptWatcher($this->fileSystem, $this->streamSelect); - } -} diff --git a/src/InterruptWatcherFactoryInterface.php b/src/InterruptWatcherFactoryInterface.php deleted file mode 100644 index 7000ccc..0000000 --- a/src/InterruptWatcherFactoryInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -fileSystem = $fileSystem; + $this->number = $number; + + parent::__construct($fileSystem, $number); + + $this->setDirection(self::DIRECTION_IN); + } + + /** + * {@inheritdoc} + */ + public function getEdge() + { + $edgeFile = $this->getPinFile(self::GPIO_PIN_FILE_EDGE); + return $this->fileSystem->getContents($edgeFile); + } + + /** + * {@inheritdoc} + */ + public function setEdge($edge) + { + $edgeFile = $this->getPinFile(self::GPIO_PIN_FILE_EDGE); + $this->fileSystem->putContents($edgeFile, $edge); + } +} diff --git a/src/Pin/InputPinInterface.php b/src/Pin/InputPinInterface.php new file mode 100644 index 0000000..d4223cb --- /dev/null +++ b/src/Pin/InputPinInterface.php @@ -0,0 +1,25 @@ +fileSystem = $fileSystem; + $this->number = $number; + + parent::__construct($fileSystem, $number); + + $this->setDirection(self::DIRECTION_OUT); + } + + /** + * {@inheritdoc} + */ + public function setValue($value) + { + $valueFile = $this->getPinFile(self::GPIO_PIN_FILE_VALUE); + $this->fileSystem->putContents($valueFile, $value); + } +} diff --git a/src/Pin/OutputPinInterface.php b/src/Pin/OutputPinInterface.php new file mode 100644 index 0000000..85b75fa --- /dev/null +++ b/src/Pin/OutputPinInterface.php @@ -0,0 +1,13 @@ +fileSystem = $fileSystem; $this->number = $number; + + $this->export(); } /** @@ -61,16 +66,7 @@ public function unexport() /** * {@inheritdoc} */ - public function getDirection() - { - $directionFile = $this->getPinFile(self::GPIO_PIN_FILE_DIRECTION); - return $this->fileSystem->getContents($directionFile); - } - - /** - * {@inheritdoc} - */ - public function setDirection($direction) + protected function setDirection($direction) { $directionFile = $this->getPinFile(self::GPIO_PIN_FILE_DIRECTION); $this->fileSystem->putContents($directionFile, $direction); @@ -85,32 +81,6 @@ public function getValue() return (int) $this->fileSystem->getContents($valueFile); } - /** - * {@inheritdoc} - */ - public function setValue($value) - { - $valueFile = $this->getPinFile(self::GPIO_PIN_FILE_VALUE); - $this->fileSystem->putContents($valueFile, $value); - } - - /** - * {@inheritdoc} - */ - public function getEdge() - { - $edgeFile = $this->getPinFile(self::GPIO_PIN_FILE_EDGE); - return $this->fileSystem->getContents($edgeFile); - } - /** - * {@inheritdoc} - */ - public function setEdge($edge) - { - $edgeFile = $this->getPinFile(self::GPIO_PIN_FILE_EDGE); - $this->fileSystem->putContents($edgeFile, $edge); - } - /** * Get the path of the import or export file. * @@ -130,7 +100,7 @@ private function getFile($file) * * @return string */ - private function getPinFile($file) + protected function getPinFile($file) { return self::GPIO_PATH . self::GPIO_PREFIX . $this->getNumber() . '/' . $file; } diff --git a/src/Pin/PinInterface.php b/src/Pin/PinInterface.php new file mode 100644 index 0000000..3eca7b1 --- /dev/null +++ b/src/Pin/PinInterface.php @@ -0,0 +1,33 @@ +fileSystem = $fileSystem ?: new FileSystem(); - } - - /** - * {@inheritdoc} - */ - public function getPin($number) - { - return new Pin($this->fileSystem, $number); - } -} diff --git a/src/PinFactoryInterface.php b/src/PinFactoryInterface.php deleted file mode 100644 index 939b83c..0000000 --- a/src/PinFactoryInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -vfs[$path]; + } + + public function putContents($path, $buffer) + { + $this->vfs[$path] = $buffer; + } +} diff --git a/test/Interrupt/InterruptWatcherTest.php b/test/Interrupt/InterruptWatcherTest.php new file mode 100644 index 0000000..bd9651e --- /dev/null +++ b/test/Interrupt/InterruptWatcherTest.php @@ -0,0 +1,76 @@ +getMockBuilder(FileSystemInterface::class) + ->setMethods(['open', 'getContents', 'putContents']) + ->getMock(); + + $map = [ + ['/sys/class/gpio/gpio2/value', 'r', $streamPair2[0]], + ['/sys/class/gpio/gpio3/value', 'r', $streamPair3[0]], + ]; + + $fileSystem->method('open') + ->will($this->returnValueMap($map)); + + // Maps $except to $read for testing + $streamSelect = function ($read, $write, $except) { + $dummy = []; + return stream_select($except, $write, $dummy); + }; + + $gpio = new GPIO($fileSystem, $streamSelect); + $watcher = $gpio->createWatcher(); + + $callCount2 = $callCount3 = 0; + + $pin2 = $gpio->getInputPin(2); + $pin3 = $gpio->getInputPin(3); + + $watcher->register($pin2, function ($pin, $value) use (&$callCount2, $pin2) { + $callCount2++; + $this->assertSame($pin2, $pin); + $this->assertEquals(1, $value); + + return true; + }); + + $watcher->register($pin3, function ($pin, $value) use (&$callCount3, $pin3) { + $callCount3++; + $this->assertSame($pin3, $pin); + $this->assertEquals(0, $value); + + return true; + }); + + fwrite($streamPair2[1], '1'); + $watcher->watch(100); + + fwrite($streamPair2[1], '1'); + fwrite($streamPair3[1], '0'); + $watcher->watch(100); + + $watcher->unregister($pin2); + + @fwrite($streamPair2[1], '0'); + $watcher->watch(100); + + $this->assertEquals(2, $callCount2); + $this->assertEquals(3, $callCount3); + + fclose($streamPair2[1]); + fclose($streamPair3[1]); + } +} diff --git a/test/InterruptWatcherTest.php b/test/InterruptWatcherTest.php deleted file mode 100644 index 1b23c0f..0000000 --- a/test/InterruptWatcherTest.php +++ /dev/null @@ -1,57 +0,0 @@ -getMockBuilder(FileSystemInterface::class) - ->setMethods(['open', 'getContents', 'putContents']) - ->getMock(); - - $fileSystem->expects($this->once()) - ->method('open') - ->with('/sys/class/gpio/gpio2/value', 'r') - ->willReturn($streamPair[0]); - - // Maps $except to $read for testing - $streamSelect = function ($read, $write, $except) { - $dummy = []; - return stream_select($except, $write, $dummy); - }; - - $watcherFactory = new InterruptWatcherFactory($fileSystem, $streamSelect); - $watcher = $watcherFactory->createWatcher(); - - $callCount = 0; - $expectedPin = (new PinFactory)->getPin(2); - - $watcher->register($expectedPin, function ($pin, $value) use (&$callCount, $expectedPin) { - $callCount++; - $this->assertSame($expectedPin, $pin); - $this->assertEquals(1, $value); - }); - - fwrite($streamPair[1], '1'); - $watcher->watch(100); - fwrite($streamPair[1], '1'); - $watcher->watch(100); - - $watcher->unregister($expectedPin); - - @fwrite($streamPair[1], '0'); - $watcher->watch(100); - - $this->assertEquals(2, $callCount); - - fclose($streamPair[1]); - } -} diff --git a/test/Pin/InputPinTest.php b/test/Pin/InputPinTest.php new file mode 100644 index 0000000..0bce449 --- /dev/null +++ b/test/Pin/InputPinTest.php @@ -0,0 +1,32 @@ +getInputPin(2); + + $this->assertEquals('2', $vfs->getContents('/sys/class/gpio/export')); + $this->assertEquals('in', $vfs->getContents('/sys/class/gpio/gpio2/direction')); + + $pin->setEdge('both'); + + $this->assertEquals('both', $pin->getEdge()); + $this->assertEquals('both', $vfs->getContents('/sys/class/gpio/gpio2/edge')); + + $vfs->putContents('/sys/class/gpio/gpio2/value', '1'); + + $this->assertEquals(1, $pin->getValue()); + + $pin->unexport(); + $this->assertEquals('2', $vfs->getContents('/sys/class/gpio/unexport')); + } +} diff --git a/test/Pin/OutputPinTest.php b/test/Pin/OutputPinTest.php new file mode 100644 index 0000000..59778a3 --- /dev/null +++ b/test/Pin/OutputPinTest.php @@ -0,0 +1,25 @@ +getOutputPin(2); + + $this->assertEquals('2', $vfs->getContents('/sys/class/gpio/export')); + $this->assertEquals('out', $vfs->getContents('/sys/class/gpio/gpio2/direction')); + + $pin->setValue(1); + + $this->assertEquals(1, $vfs->getContents('/sys/class/gpio/gpio2/value')); + $this->assertEquals(1, $pin->getValue()); + } +} diff --git a/test/PinTest.php b/test/PinTest.php deleted file mode 100644 index de90f5c..0000000 --- a/test/PinTest.php +++ /dev/null @@ -1,106 +0,0 @@ -getPin(); - $this->assertEquals(self::TEST_PIN_NUMBER, $pin->getNumber()); - } - - public function testDirection() - { - $pin = $this->getPin('/sys/class/gpio/gpio' . self::TEST_PIN_NUMBER . '/direction'); - $pin->setDirection(Pin::DIRECTION_IN); - $this->assertEquals(Pin::DIRECTION_IN, $pin->getDirection()); - } - - public function testValue() - { - $pin = $this->getPin('/sys/class/gpio/gpio' . self::TEST_PIN_NUMBER . '/value'); - $pin->setValue(Pin::VALUE_HIGH); - $this->assertEquals(Pin::VALUE_HIGH, $pin->getValue()); - } - - public function testEdge() - { - $pin = $this->getPin('/sys/class/gpio/gpio' . self::TEST_PIN_NUMBER . '/edge'); - $pin->setEdge(Pin::EDGE_RISING); - $this->assertEquals(Pin::EDGE_RISING, $pin->getEdge()); - } - - private function getPin($expectedFile = null) - { - $mockFileSystem = new FileSystemMock($expectedFile); - return (new PinFactory($mockFileSystem))->getPin(self::TEST_PIN_NUMBER); - } - - public function testExport() - { - $exportFile = '/sys/class/gpio/export'; - - $mockFileSystem = new FileSystemMock($exportFile); - $pin = (new PinFactory($mockFileSystem))->getPin(self::TEST_PIN_NUMBER); - $pin->export(); - - $this->assertEquals(self::TEST_PIN_NUMBER, $mockFileSystem->getContents($exportFile)); - } - - public function testUnexport() - { - $unexportFile = '/sys/class/gpio/unexport'; - - $mockFileSystem = new FileSystemMock($unexportFile); - $pin = (new PinFactory($mockFileSystem))->getPin(self::TEST_PIN_NUMBER); - $pin->unexport(); - - $this->assertEquals(self::TEST_PIN_NUMBER, $mockFileSystem->getContents($unexportFile)); - } -} - -class FileSystemMock implements FileSystemInterface -{ - private $expectedFile; - private $contents; - - public function __construct($expectedFile) - { - $this->expectedFile = $expectedFile; - $this->contents = null; - } - - public function open($path, $mode) - { - } - - public function getContents($path) - { - $this->checkExpectedFile($path); - - return $this->contents; - } - - public function putContents($path, $buffer, $flags = 0) - { - $this->checkExpectedFile($path); - - $this->contents = $buffer; - - return strlen($buffer); - } - - private function checkExpectedFile($path) - { - if ($this->expectedFile !== $path) { - throw new \InvalidArgumentException('Expected ' . $this->expectedFile . ' got ' . $path); - } - } -}