2013-11-17

Building A Sensor Phalanx With PHP

Sensors are fun. They report from the physical world into the digital. But getting the signal into php is only the first part, you will have to get them out again. This post shows how to get data from analog sensors pushed to the browser. It uses Carica Chip, if you haven't read my previous blog post you should do it first.

Carica Io implements its own event loop, but if it finds ReactPHP installed, it will base it on this implementation, making it compatible and allowing to use the libraries. One of the libraries for ReactPHP is Ratchet, a websocket server implementation.

In this example two servers are used. An http server for file delivery (html and javascript for the UI) and a websocket server that pushes the sensor values to the browser. Smootie Charts displays the data in the browser as a running line chart, not unlike an oscilloscope.

Source

The latest source for this example can be found on Bitbucket:
https://bitbucket.org/ThomasWeinert/carica-sensor-phalanx

Project Setup

Clone Carica Chip Skeleton to create a new project and add Ratchet to the dependencies.

composer create-project carica/chip-skeleton SensorPhalanx \
  --stability=dev 

cd SensorPhalanx

composer require cboden/ratchet

copy dist.configuration.php configuration.php

Edit configure.php to match your hardware setup.

The HTTP Server

In the LED example, the http server had only one route to deliver the html file. Now we need to deliver the javascript, too. An additional route allows to deliver static files from a subdirectory.

$route = new Http\Route();
$route->match('/', new Http\Route\File('index.html'));
$route->startsWith('/files', new Http\Route\Directory(__DIR__));
$httpServer = new Http\Server($route);
$httpServer->listen(8080);

The Sensor Phalanx

The Sensor Phalanx object encapsulates the Ratchet websocket server and provides the callbacks for it. It needs an array of sensors. Port 8080 is already used for the http server, so it should listen on port 8081. An interval triggers the update of the clients.

// create the phalanx with some sensors
include(__DIR__.'/class/SensorPhalanx.php');
$phalanx = new Carica\SensorPhalanx(
  [
    'lightsensor' => new Chip\Sensor\Analog($board->pins[15]),
    'potentiometer' => new Chip\Sensor\Analog($board->pins[16])
  ]
);
// tell the websocket server to listen
$phalanx->listen(8081);

// update clients connected to the phalanx with the current sensor data
$loop->setInterval(
  function () use ($phalanx) {
    $phalanx->update();
  },
  200
);

The SensorPhalanx Class

The SensorPhalanx class needs to implement two interfaces. Carica\Io\Event\HasLoop defines access to the Carica Io event loop. It can be implemented using the trait Carica\Io\Event\Loop\Aggregation.

Ratchet\MessageComponentInterface defines the callback methods for the websocket server.
  • onOpen() - add a client
  • onClose() - remove a client
  • onMessage() - handle data recieved from the browser
  • onError() - log/handle errors
public function onOpen(Ratchet\ConnectionInterface $connection) {
  $this->_clients[spl_object_hash($connection)] = $connection;
}

public function onClose(Ratchet\ConnectionInterface $connection) {
  unset($this->_clients[spl_object_hash($connection)]);
}

public function onMessage(Ratchet\ConnectionInterface $connection, $message) {
}

public function onerror(Ratchet\ConnectionInterface $connection, \Exception $e) {
  echo "Error: ", $e->getMessage(), "\n";
  $connection->close();
}

Create And Listen

The constructor of the class just validates the sensor objects, and stores them. Listen creates the needed subobjects. Unlike the examples on the Ratchet website, the factory methods can not be used. They would create a new event loop. The script already has one, used for the Arduino board and the http server. The $this->loop() method returns the Carica Io event loop and in a second step allows access to the actual ReactPHP event loop.

public function __construct(array $sensors) {
  foreach ($sensors as $index => $sensor) {
    if ($sensor instanceOf Chip\Sensor\Analog) {
      $this->_sensors[$index] = $sensor;
    }
  }
}

public function listen($port = 8081) {
  $socket = new \React\Socket\Server($this->loop()->loop());
  $socket->listen($port);
  $this->_server = new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(
      new Ratchet\WebSocket\WsServer(
        $this
      )
    ),
    $socket,
    $this->loop()->loop()
  );
}

Update

Update collects the data from all sensors into an array, encodes it to json and sends it to all connected clients.

public function update($log = TRUE) {
  $values = ['type' => 'sensors'];
  foreach ($this->_sensors as $index => $sensor) {
    $values['sensors'][$index] = $sensor->get();
  }
  $json = json_encode($values);
  if ($log) {
    echo $json, "\n";
  }
  foreach ($this->_clients as $client) {
    $client->send($json);
  }
}

User Interface

The html file uses Smoothie Charts. It defines some CSS and a canvas for the chart. The chart is created and connected to the canvas. A list of colors provides a different color for each line.

The websocket server connects and gets a callback. If a message is received it loops over the sensor values. If a line for the index already exists the value is appended. For an unknown index a new line is created and the assigned color is removed from the internal list.

var smoothie = new SmoothieChart(
  {
    maxValue : 1,
    minValue: 0
  }
);
smoothie.streamTo(document.getElementById('monitorCanvas'), 250);

var colors = ['#00FF00', '#FF0000', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF'];
var lines = [];

websocket = new WebSocket('ws://localhost:8081/');
websocket.onmessage = function(event) {
  var data = JSON.parse(event.data);
  if (data && data.type == 'sensors') {
    for (index in data.sensors) {
      if (typeof lines[index] == 'undefined') {
        var color = colors.shift();
        if (!color) {
          color = '#FFFFFF';
        }
        lines[index] = new TimeSeries();
        smoothie.addTimeSeries(
          lines[index],
          {
            lineWidth: 2,
            strokeStyle: color
          }
        );
      }
      lines[index].append(new Date().getTime(), data.sensors[index]);
    }
  }
};

In Action

2013-11-04

Carica Chip 101 - Controlling An LED With PHP

Some time ago, in this blog post, I explained the basic stuff about Arduino, Firmata and PHP. Now it is time for the next step. Carica Io and Carica Firmata have grown and got a third layer called Carica Chip.
  1. Carica Io - Non-Blocking I/O for PHP
  2. Carica Firmata - An implementation of the Firmata protocol
  3. Carica Chip - PHP classes representing hardware devices
Carica Chip provides an easy way to control a device. So let's start with an "Interactive LED" example.

First Step: Project Initialization

Carica Chip uses Composer. Make sure that it is installed and open a console. Go into your projects directory and execute the following line (it will create a new subdirectory "led"):

composer create-project carica/chip-skeleton led \
  --stability=dev

Second Step: Create An HTML Interface

For the interface a simple html file is used. Just two links with an iframe set as the target. This is a basic version, some Javascript and CSS should be used to make it nicer and more usable.

<html>
  <head>
    <title>Led Switch</title>
  </head>
  <body>
    <a href="./switch/on" target="iframe">
      On
    </a>
    <a href="./switch/off" target="iframe">
      Off
    </a>
    <iframe name="iframe" src="about:blank"></iframe>
  </body>
</html>

Store the html source as "index.html" in the project root.

Third Step: Create The PHP Server

This will be a step by step description, the complete file is below and on Gist.

The skeleton includes a bootstrap file that returns a board depending on your configuration. Copy "dist.configuration.php" to "configuration.php" and change it if needed.

Now open "server.php". At the top "bootstrap.php" is included to fetch a Firmata board. If the board is activated, it executes a callback. Carica Chip provides a "Led" class. For the example an instance is created. The constructor needs the pin the led is connected to. Most Arduinos have a led that is connected to pin #13 on board.
$led = new Chip\Led($board->pins[13]);

We need to deliver the html interface to the browser. Carica Io includes a http server and file routings. Add "use Carica\Io\Network\Http as Http;" to the namespace definition, create a new route and add a file delivery for "index.html".
$route = new Http\Route();
// Define html file delivery for /
$route->match(
  '/', 
  new Http\Route\File(__DIR__.'/index.html')
);

A second route defines the switch actions. This is specific and not much source, so an anonymous function is used. For more extensive logic I suggest functors, objects that implement the magic method "__invoke()", like the file handler. The state parameter is fetched from the path. Depending on the parameter the led is switched on or off. A response is created and a string with the new state is used as content. If you don't return a response, a 404 would be send to the browser.
$route->match(
  '/switch/{state}',
  function (Http\Request $request, array $parameters)
    use ($led) {
    $ledOn = ($parameters['state'] == 'on');
    if ($ledOn) {
      $led->on();
    } else {
      $led->off();
    }
    $response = $request->createResponse(
      new Http\Response\Content\String(
        $ledOn ? 'ON' : 'OFF', 
        'text/plain; charset=utf-8'
      )
    );
    return $response;
  }
);

Last the http server is created and started.
$server = new Carica\Io\Network\Http\Server($route);
$server->listen(8080);

Finished!

That's all. You can now start the script on the command line and open the URL in a browser. Clicking the links will (de)activate the led.

I posted the full source of "server.php" including comments on Gist.

2013-06-20

Basics: Using Arduino From PHP

Own Protocol Inside A PHP Page.

This is the version mostly described on the net. For example on Instructables. Basically you write an Arduino Scetch that listens and send plain text data on the serial port. A PHP Script triggered by a webserver request opens the port communicates with the Arduino board an outputs some HTML.

The major downside of this approach is that many Arduino boards reset if the serial port is opened. So the startup and configuration has to be done any time the PHP scripts reads some data or triggers an action. Because the PHP script stops after the page request any data has to be stored in a database, cache or file.

Firmata

Firmata is a generic sketch for Arduino boards that implements an MIDI based protocol. Here are client libraries for several languages. For example Johnny Five for Javascript (Node.js) implements abstraction not only for the communication but for hardware elements. For example you get an led object with blink() and fade() methods.

Firmata Inside A PHP Page. 

Well, It is possible but you don't want to do that. Firmata has a little more complex startup process that takes about two seconds. To much to use it in a web request.

Firmata Over TCP

The official version of Firmata uses the serial port. But here are forks that can use TCP. You need an compatible network shield for your Arduino board and the version may be a little behind the offical one.

Non-Blocking I/O

Node.js uses an concept called Non-blocking I/O or Asynchronous I/O. Basically it means that you add actions to an event loop, trigger events from you objects that execute attached callbacks. Doesn't sound familiar? If you ever programmed some Javascript for your web pages, you used this concept. Each browser window has an event loop. Functions like "window.setTimeout()" add event to the loop. The browser fires event on the page elements that execute callbacks, if you attached them, too.

So using this concept you describe what should happen if an events is triggered and a certain conditions are meet. In classic PHP you work with the current state. In Non-Blocking I/O the result has yet to be calculate at a future time.

Procedual Programming:
If the result of action x is value y now do that.
Non-Blocking I/O
If the result of action x will be y you will have to do that.

Thinking like this is much better suited to hardware control, at the moment you are programming you do not now if somebody will ever press the button on your Arduino device or which values the sensor your connected will send.

Non-Blocking I/O in PHP

PHP has no default event loop implemented but here are several possibilities for it. First it whould be possible just using an while loop and usleep(). This would not be very efficient. Better is an combination of this with stream_select(). The "stream_select()" function  is like a usleep() that monitors streams and return imminently if a stream is changed. It even says which stream is changed how, so it is possible to trigger the exact events for this stream.

Event more efficient is using libevent. PHP has an extension for this event loop implementation. It is used inside the Chrome browser browser and Node.js, too.

Libraries For PHP

Here are libraries in PHP that implement event loops.

ReactPHP
ReactPHP is the most mature project implementing Non-Blocking I/O for PHP. It a general purpose library and has additional components. You can read more about it on reactphp.org.

Carica Io
Caica Io is an implementation I created as a base for Carica Firmata, a client library for the Firmata protocol. It can used separately if you're going to implement your own protocol, too. Here is an basic example with timers:


The event loop uses the API not unlike the Browser. The main difference to Javascript in the Browser or Node.js is that you have to run the event loop explicitly.

Carica Firmata

The Firmata test application
in Carica Firmata
Based on Carica Io, it allows you to create programs that control the Arduino board. They run without an webserver (like Apache HTTP, Nginx, ...). They can even contain an (simple) webserver to control the script from a browser. The php program is started from the command line and runs continuously until you stop it (or it crashes :-) ). The serial connection to the board is opened only once, no reset happens and the start up time is not an issue.

Blink

The "Hello World" for an Arduino is an blinking led. Wait for the board to activate, and add an interval (repeated timer) that switches it on and off.


Dimmer

Carica Firmata can read data, too. Simple attach an callback to the board. On the analog pin this could be a potentiometer dimming an led. The examples uses an OOP api for the pins. The main difference to calling the methods on the board object directly is that values and the mode are only send if the are changed.

On Windows

On Windows the  serial port is seriously broken at the moment. It can not work non-blocking. Any reading action will block your script until it recieves data. One solution is Serpoxy, a little program that maps the serial port to tcp.

Links

x