Rise of the Machines: PHP and IoT - php[world] 2016

65
Rise of the Machines: PHP & IoT php[world] 2016 @colinodell - joind.in/talk/18676

Transcript of Rise of the Machines: PHP and IoT - php[world] 2016

Page 1: Rise of the Machines: PHP and IoT - php[world] 2016

Rise of the Machines:PHP & IoTphp[world] 2016

@colinodell - joind.in/talk/18676

Page 2: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Colin O’Dell• Lead Web Developer at Unleashed Technologies

• PHP League Member league/commonmark league/html-to-markdown

• PHP 7 Upgrade Guide e-book

• Arduino / RasPi / 3D printing Enthusiast

Page 3: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Internet of ThingsThe Internet of Things is the internetworking of physical devices, vehicles, buildings and other items—embedded with electronics, software, sensors, actuators, and network connectivity that enable these objects to collect and exchange data.

- https://en.wikipedia.org/wiki/Internet_of_things

Page 4: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Internet of ThingsThe Internet of Things is the internetworking of physical devices, vehicles, buildings and other items—embedded with electronics, software, sensors, actuators, and network connectivity that enable these objects to collect and exchange data.

- https://en.wikipedia.org/wiki/Internet_of_things

Page 5: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 6: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Connectivity• Ethernet / WiFi

• NFC / RFID

• Bluetooth LE

• Zigbee / Z-Wave

• Cellular (LTE / CDMA / GSM)

• And many others

Page 7: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

2003 2010 2015 20200

10

20

30

40

50

60

Population vs Connected Devices

People Connected Devices

(Bill

ions

)

Source: https://www.cisco.com/c/dam/en_us/about/ac79/docs/innov/IoT_IBSG_0411FINAL.pdf

Page 8: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 9: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Today’s Agenda• Device overview

• Four examples of combining IoT with PHP Project Goals Hardware & Platform Choice Building the device Demo

• Q&A

Page 10: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

DIY IoT DevicesFor the software or hardware enthusiast

Page 11: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 12: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

How to choose?• Identify required features

• Ideal programming language

• Consider power requirements

• Size / footprint

Page 13: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Four Examples Using PHP

Page 14: Rise of the Machines: PHP and IoT - php[world] 2016

Uptime MonitorUse Raspberry Pi to monitor if site is up; change LED color accordingly

Page 15: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Why Raspberry Pi?• Runs Linux (Raspian – a derivative of Debian)

• Has GPIO pins

• PHP easily installed

• Raspberry Pi Zero only $5

• Handles HTTPs out-of-the-box

• Why not?

Page 16: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Hardware• Raspberry Pi (any variant)• Micro USB cable (power)• USB WiFi dongle

• RGB LED• Two resistors• Wires

Page 17: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

RGB LEDs

Page 18: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Wiring

Page 19: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

$ composer require piphp/gpio

Page 20: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

<?php

const URL = 'https://www.colinodell.com';const GPIO_PIN_GREEN = 12;const GPIO_PIN_RED = 16;

require_once 'vendor/autoload.php';

$gpio = new PiPHP\GPIO\GPIO();$greenLed = $gpio->getOutputPin(GPIO_PIN_GREEN);$redLed = $gpio->getOutputPin(GPIO_PIN_RED);

$httpClient = new GuzzleHttp\Client();

while (true) { try { $response = $httpClient->head(URL, [ 'connect_timeout' => 3, 'timeout' => 3, ]);

echo URL . " is online\n"; $greenLed->setValue(1); $redLed->setValue(0); } catch (\RuntimeException $ex) { echo URL . " is OFFLINE!\n"; $greenLed->setValue(0); $redLed->setValue(1); }

sleep(3);}

Page 21: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Recap• PHP on a Raspberry Pi

• PHP checks if site is up

• PiPHP/GPIO library used to control output pins (LEDs)

Other uses for output pins:

• Control motors/servos

• Control relays (turn higher-voltage things on/off)

• Drive digital displays (LCD screens, LED number displays, etc.)

Page 22: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Conference Room Occupancy SensorRaspberry Pi detects movement in room; sends room status to Slack

Page 23: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Why Raspberry Pi?• Runs Linux (Raspian – a derivative of Debian)

• Has GPIO pins

• PHP easily installed

• Raspberry Pi Zero only $5

• Handles HTTPs out-of-the-box

• Why not?

TL;DR: Same reasons as before!

Page 24: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Hardware• Raspberry Pi (any variant)• Micro USB cable (power)• USB WiFi dongle

• PIR Sensor• Wires

Page 25: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

PIR Sensors

Images from https://learn.adafruit.com/pir-passive-infrared-proximity-motion-sensor/overview

Page 26: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Wiring

Page 27: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 28: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Signal Motion Detected

Occupied Vacant Occupied

Page 29: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

<?php

namespace ColinODell\PHPIoTExamples\PHPPIRSensor;

class Room{ /** * @var bool */ private $occupied = false;

/** * @var int */ private $lastMovement;

/** * @var callable */ private $onRoomStateChange;

/** * @var int */ private $roomEmptyTimeout;

/** * Room constructor. */ public function __construct() { $this->lastMovement = time(); }

/** * After $timeout seconds of no movement, the room will be marked as vacant. * * @param int $timeout */ public function setRoomEmptyTimeout($timeout) { $this->roomEmptyTimeout = $timeout; }

/** * Provide a callback to respond to the room becoming vacant or occupied. * * A single boolean parameter will be passed, denoting whether: * - The room is now occupied (true) * - The room is now vacant (false) * * @param callable $callback */ public function setOnRoomChangeCallback(callable $callback) { $this->onRoomStateChange = $callback; }

/** * Call this function whenever there is motion. * * @return void */ public function motionDetected() { $this->setOccupied(true);

$this->lastMovement = time(); }

/** * Call this function regularly to see if enough time has elapsed * to mark the room vacant. * * @return void */ public function tick() { // If there's been no movement lately, set the room to vacant if (time() - $this->lastMovement >= $this->roomEmptyTimeout) { $this->setOccupied(false); } }

/** * @param bool $newState */ private function setOccupied($newState) { if ($newState !== $this->occupied) { $this->occupied = $newState; if (is_callable($this->onRoomStateChange)) { $func = $this->onRoomStateChange; $func($newState); } } }}

Page 30: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

<?php

// Configuration:

const GPIO_PIN = 17;const DECLARE_ROOM_EMPTY_AFTER = 60*5; // 5 minutesconst SLACK_INCOMING_WEBHOOK_URL = 'https://hooks.slack.com/services/xxxx/xxxx/xxxx';

// End configuration

require_once 'vendor/autoload.php';

use ColinODell\PHPIoTExamples\PHPPIRSensor\Room;use PiPHP\GPIO\GPIO;use PiPHP\GPIO\Pin\InputPinInterface;

// HTTP client for communicating with Slack$http = new \GuzzleHttp\Client();

// Instantiate a new room object to keep track of its state$room = new Room();$room->setRoomEmptyTimeout(DECLARE_ROOM_EMPTY_AFTER);$room->setOnRoomChangeCallback(function($isOccupied) use ($http) { $message = 'The conference room is now ' . ($isOccupied ? 'occupied.' : 'vacant.');

$http->postAsync(SLACK_INCOMING_WEBHOOK_URL, [ 'json' => [ 'text' => $message, ], ]);});

// Create a GPIO object$gpio = new GPIO();

// Set the pin to watch for rising edges$pin = $gpio->getInputPin(GPIO_PIN);$pin->setEdge(InputPinInterface::EDGE_RISING);

// Call $room->motionDetected() whenever the pin goes HIGH$interruptWatcher = $gpio->createWatcher();$interruptWatcher->register($pin, function($pin, $value) use ($room) { $room->motionDetected();});

// Run foreverwhile (true) { $interruptWatcher->watch(DECLARE_ROOM_EMPTY_AFTER * 1000); $room->tick();}

Page 31: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 32: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Recap• PHP on a Raspberry Pi

• PiPHP/GPIO library monitors GPIO pin for signal changes

• PHP tracks duration between rising edges (low-to-high signal changes)

• Notifications pushed to Slack via webhook

Other uses for input pins:

• Switches & buttons

• Sensors (temperature, humidity, motion, accelerometer, GPS)

• Receiving data from other devices

Page 33: Rise of the Machines: PHP and IoT - php[world] 2016

Alexa Custom SkillUse PHP + Laravel to handle request for php[world] session information

@colinodell - joind.in/talk/18676

Page 34: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Why Alexa / Amazon Echo?• Amazon handles voice recognition

• Parsed request passed from Amazon to our API endpoint

• We handle accordingly; send formatted response for Alexa to read aloud

• Published skills easily installable

Page 35: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

User Interaction Flow

Diagram from https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/overviews/understanding-custom-skills

Page 36: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Invoking Custom Skills

• Ask [skill name] [action] Ask PHP World which sessions are next

• Tell [skill name] [action] Tell PHP World this talk is great

• [action] using [skill name] Rate this talk 5 stars using PHP World

Page 37: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Actions• What sessions are next?

• What session is next in {room}?

• What sessions are at {time} in {room}?

• Who is speaking in {room} {day} at {time}?

Page 38: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Defining ActionsGetSessions what sessions are nextGetSessions what talks are nextGetSessions who is talking nextGetSessions who is speaking nextGetSessions which sessions are nextGetSessions which talks are next

GetSessions what session is next in {room}GetSessions what talk is next in {room}GetSessions who is talking next in {room}GetSessions who is speaking next in {room}GetSessions which session is next in {room}GetSessions which talk is next in {room}

GetSessions what sessions are at {time}GetSessions what talks are at {time}GetSessions who is talking at {time}GetSessions who is speaking at {time}GetSessions which sessions are at {time}GetSessions which talks are at {time}

GetSessions what sessions are at {time} {day}GetSessions what talks are at {time} {day}GetSessions who is talking at {time} {day}GetSessions who is speaking at {time} {day}

GetSessions which sessions are at {time} {day}GetSessions which talks are at {time} {day}

GetSessions what session is in {room} at {time}GetSessions what talk is in {room} at {time}GetSessions who is talking in {room} at {time}GetSessions who is speaking in {room} at {time}GetSessions which session is in {room} at {time}GetSessions which talk is in {room} at {time}

GetSessions what session is in {room} at {time} {day}GetSessions what talk is in {room} at {time} {day}GetSessions who is talking in {room} at {time} {day}GetSessions who is speaking in {room} at {time} {day}GetSessions which session is in {room} at {time} {day}GetSessions which talk is in {room} at {time} {day}

GetSessions what session is {day} in {room} at {time}

GetSessions what talk is {day} in {room} at {time}GetSessions who is talking {day} in {room} at {time}GetSessions who is speaking {day} in {room} at {time}GetSessions which session {day} is in {room} at {time}GetSessions which talk {day} is in {room} at {time}

Page 39: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Intent Schema{ "intents": [ { "intent": "GetSessions", "slots": [ { "name": "day", "type": "AMAZON.DATE" }, { "name": "time", "type": "AMAZON.TIME" }, { "name": "room", "type": "ROOM" } ] }, { "intent": "AMAZON.HelpIntent" } ]}

FairfaxGreat FallsPotomacAsh Grove AAsh Grove BAsh Grove C

Page 40: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

$ composer require develpr/alexa-app

AlexaRoute::intent('/alexa-end-point', 'GetDeveloperJoke', function(){ Alexa::say("A SQL query goes into a bar, walks up to two tables and asks, \"Can I join you?\"");});

Page 41: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

<?php

namespace App\Console\Commands;

use Carbon\Carbon;use Illuminate\Support\Facades\DB;use GuzzleHttp\Client;use Illuminate\Console\Command;

class ImportScheduleCommand extends Command { protected $signature = 'import:schedule'; protected $description = 'Imports the php[world] schedule from the conference website.';

public function handle() { // Manually scraped from the site by viewing the AJAX requests for each day $timestamps = [1479081600, 1479168000, 1479254400, 1479340800, 1479427200];

foreach ($timestamps as $timestamp) { $this->importSchedule($timestamp); } }

private function importSchedule($timestamp) { $client = new Client([ 'base_uri' => '', 'timeout' => 10, ]);

$response = $client->post('https://world.phparch.com/wp-admin/admin-ajax.php', [ 'form_params' => [ 'action' => 'get_schedule', 'data-timestamp' => $timestamp, 'data-location' => 0, 'data-track' => 0, 'data-page' => 1, 'data-max-items' => 100, ], 'headers' => [ 'Accept' => 'application/javascript', // Pretend the request from the website - sorry Eli! 'X-Requested-With' => 'XMLHttpRequest', 'Origin' => 'https://world.phparch.com', 'Referer' => 'https://world.phparch.com/schedule/', ], ]);

$data = json_decode($response->getBody(), true);

foreach ($data['sessions'] as $session) { $speakers = implode('|', array_map(function($speaker){ return $speaker['post_title']; }, $session['speakers']));

$tracks = implode('|', array_map(function($track){ return $track['name']; }, $session['tracks']));

$start = Carbon::parse($session['date'].' '.$session['time'], 'America/New_York'); $end = Carbon::parse($session['date'].' '.$session['end_time'], 'America/New_York');

DB::insert('insert into sessions (start, `end`, room, title, abstract, speakers, tracks) values (?, ?, ?, ?, ?, ?, ?)', [ $start->getTimestamp(), $end->getTimestamp(), $session['location'], $session['post_title'], $session['post_excerpt'], $speakers, $tracks, ]); } }}

Page 42: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

<?php

namespace App\Http\Controllers;

use Carbon\Carbon;use Develpr\AlexaApp\Facades\Alexa;use Develpr\AlexaApp\Request\AlexaRequest;use Illuminate\Database\Query\Builder;use Illuminate\Support\Facades\DB;

class SessionController extends Controller{ public function getSessions(AlexaRequest $request) { $request->getIntent(); $time = $request->slot('time') ?: Carbon::now()->format('G:i'); $day = $request->slot('day') ?: Carbon::now()->format('Y-m-d'); $room = $request->slot('room');

try { $sessions = $this->findSessions($time, $day, $room);

if (count($sessions) === 0) { // No sessions found return Alexa::say('Sorry, I couldn\'t find any sessions on ' . $day . ' at ' . $time)->endSession(); } elseif (count($sessions) === 1) { return Alexa::say($this->getSessionText(reset($sessions)))->endSession();; } else { $text = 'I found ' . count($sessions) . ' sessions. '; foreach ($sessions as $session) { $text .= $this->getSessionText($session) . ' '; }

return Alexa::say($text)->endSession(); } } catch (\Exception $ex) { return Alexa::say('Sorry, I\'m having trouble searching for sessions right now.')->endSession();; } }

/** * @param string|null $time * @param string|null $day * @param string|null $room * * @return array * * @todo: Refactor into a service */ private function findSessions($time, $day, $room) { if (empty($day)) { $day = Carbon::now()->format('Y-m-d'); }

if (empty($time)) { $time = Carbon::now()->format('H:i'); } elseif (strlen($time) === 2) { // We can get a time indicator for certain utterances like "morning", // so we'll need to convert those to a certain time for our purposes // switch ($time) { case 'MO': $time = '08:00'; break; case 'AF': $time = '12:30'; break; case 'EV': $time = ''; break; case 'NI': $time = ''; break; } }

$timestamp = Carbon::parse($day.' '.$time, 'America/New_York')->getTimestamp();

/** @var Builder $query */ $query = DB::table('sessions'); $query->where('start', '<=', $timestamp); $query->where('end', '>', $timestamp);

if (!empty($room)) { $query->where('room', '=', $room); }

$query->orderBy('start');

return $query->get(); }

/** * @param \stdClass $session * * @return string */ private function getSessionText($session) { $speakers = implode(' and ', explode('|', $session->speakers));

$time = Carbon::createFromTimestamp($session->start, 'America/New_York')->format('g:i');

if (empty($speakers)) { return sprintf('%s will be in %s at %s', $session->title, $session->room, $time); }

return sprintf('%s will be giving a talk entitled %s in %s at %s.', $speakers, $session->title, $session->room, $time); }}

Page 43: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

<?php

// app/Http/routes.php

AlexaRoute::intent('/alexa', 'GetSessions', 'App\Http\Controllers\SessionController@getSessions');

Page 44: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Recap• Alexa parses voice request; passes to PHP

• Laravel application handles requests; returns response

• Alexa renders responses as voice and/or cards

Other uses for Alexa:

• Querying for information

• Interactive sessions (online ordering, games, etc.)

• Home automation

Page 45: Rise of the Machines: PHP and IoT - php[world] 2016

Packagist Download CounterDisplaying downloads counts from the Packagist API with Particle Photon

Page 46: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Particle Photon• WiFi built in• Small footprint• Can be powered via MicroUSB• Enough digital GPIO pins

• Programmable via web-based IDE• OTA flashing• Built-in webhook support

Page 47: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Hardware• Particle Photon• MAX7219 8-Digit Red LED Display Module• Micro USB cable (power)• Wires

Page 48: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

MAX7219 8-Digit Red LED Display Module

Images from https://learn.adafruit.com/pir-passive-infrared-proximity-motion-sensor/overview

Page 49: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Wiring

Page 50: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 51: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 52: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 53: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Problem!• JSON document is 32.5 KB

• Photon webhook responses are 512-byte chunks spaced 250ms apart

Page 54: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Problem!• JSON document is 32.5 KB

• Photon webhook responses are 512-byte chunks spaced 250ms apart

Solution!• Use Yahoo YQL to reduce response size

Page 55: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Problem!• JSON document is 32.5 KB

• Photon webhook responses are 512-byte chunks spaced 250ms apart

Solution!• Use Yahoo YQL to reduce response size

• Use PHP to parse JSON, return just one number

Page 56: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

packagist-counter.php<?php

const URL = 'https://packagist.org/packages/league/commonmark.json';

$json = json_decode(file_get_contents(URL), true);header('Content-Type: text/plain');echo $json['package']['downloads']['total'];

Page 57: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 58: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 59: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 60: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Page 61: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Recap• Particle Photon + 8 digit LED display

• Particle webhook fetches data from packagist-counter.php

• packagist-counter.php fetches from packagist.org; trims out the fat

Page 62: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Summary

Page 63: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Summary• IoT is everywhere!

• PHP can bridge the gap

• Run PHP: On the device (RasPi examples) In “the cloud” (Alexa; Particle Photon) Or both!

• Anyone can build web-connected devices

Page 64: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Questions?

Page 65: Rise of the Machines: PHP and IoT - php[world] 2016

@colinodell - joind.in/talk/18676

Thanks!

Slides/Feedback:http://joind.in/talk/18676

Project Source Code / Documentation:https://github.com/colinodell/php-iot-examples