Add intro to any Chrome app API with no overview docs.
[chromium-blink-merge.git] / chrome / common / extensions / docs / templates / articles / app_hardware.html
blob316f33dffe92678bee671c4a44cfac59a8c7623a
1 <h1>Accessing Hardware Devices</h1>
3 <p>
4 This doc shows you how packaged apps can connect to USB devices
5 and read from and write to a user's serial ports.
6 See also the reference docs for the
7 <a href="usb.html">USB API</a>
8 and the
9 <a href="serial.html">Serial API</a>.
10 The <a href="bluetooth.html">Bluetooth API</a> is also available
11 (<a href="app_known_issues.html">known issues</a> still to be resolved);
12 we've include a link to a Bluetooth sample below.
13 </p>
15 <p class="note">
16 <b>API Samples: </b>
17 Want to play with the code?
18 Check out the
19 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/serial">serial</a>,
20 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/servo">servo</a>,
21 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/usb">usb</a>,
22 and <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/zephyr_hxm">zephyr_hxm Bluetooth</a> samples.
23 </p>
25 <h2 id="usb">Accessing USB devices</h2>
27 <p>
28 You can use the USB API to send messages to connected devices using only JavaScript code. Some devices are not accessible through this API - see the <a href="#caveats">Caveats section</a> below for more details.
29 </p>
31 <h3 id="manifest">Manifest requirement</h3>
33 <p>
34 The USB API requires a special permission "usb" in the manifest file:
35 </p>
37 <pre>
38 "permissions": [
39 "usb"
41 </pre>
43 <h3 id="finding_device">Finding a device</h3>
45 <p>
46 Every device in a USB bus is identified
47 by its vendor and product IDs.
48 To find a device,
49 use the <code>findDevice()</code> method
50 which has four parameters:
51 </p>
53 <pre>
54 chrome.usb.findDevice(vendorId, productId, options, onDeviceFoundCallback)
55 </pre>
57 <br>
59 <table class="simple">
60 <tr>
61 <th scope="col"> Parameter (type) </th>
62 <th scope="col"> Description </th>
63 </tr>
64 <tr>
65 <td>vendorId (long)</td>
66 <td>The vendor ID for your USB device (in Linux, use lsusb to find it).</td>
67 </tr>
68 <tr>
69 <td>productId (long)</td>
70 <td>The product ID for your USB device (in Linux, use lsusb to find it).</td>
71 </tr>
72 <tr>
73 <td>options (object)</td>
74 <td>An object with a single key, "onEvent",
75 that will be called whenever an event is received from the corresponding device.
76 This will be the primary method of receiving information from the device.
77 As the host-initiated USB protocol is complex, read on to learn more.
78 </td>
79 </tr>
80 <tr>
81 <td>onDeviceFoundCallback (function)</td>
82 <td>Called when the device is found.
83 The callback will be executed with one parameter, an object
84 with three properties: <code>handle</code>,
85 <code>vendorId</code>,
86 <code>productId</code>. If the device could not be found, the parameter will be <code>null</code>. Save this parameter for later use, as it's required in the other methods.</td>
87 </tr>
88 </table>
90 <p>
91 Example:
92 </p>
94 <pre>
95 var onDeviceFound = function(device) {
96 _this.device=device;
97 if (device) {
98 console.log(“Device found: ”+device.handle);
99 } else {
100 console.log(“Device could not be found”);
104 var onUsbEvent = function(event) {
105 console.log(“Got some message from the USB device!”);
108 chrome.usb.findDevice(vendorId, productId, {"onEvent": onUsbEvent}, onDeviceFound);
109 </pre>
111 <h3 id="usb_transfers">USB transfers and receiving data from a device</h3>
114 USB protocol defines four types of transfers:
115 <a href="#control_transfers">control</a>, <a href="#bulk_transfers">bulk</a>, <a href="#isochronous_transfers">isochronous</a> and <a href="#interrupt_transfers">interrupt</a>.
116 Theoretically they can occur in both directions:<br>
117 device-to-host (inbound) and host-to-device (outbound).
118 </p>
121 However, due to the nature of the USB protocol, both inbound and outbound messages must be initiated by the host (your computer). For inbound (device-to-host) messages, the host, your JavaScript code, sends a message flagged as "inbound" to the device. The exact contents of the message depends on the device, but usually will have some identification of what you are requesting from it. The device then responds with the requested data. The device's response is handled by Chrome and delivered assynchronously to the <code>onEvent</code> callback you defined in the <code>findDevice</code> method.
122 An outbound (host-to-device) message is much simpler and doesn't generate an answer from the device.</p>
124 <p>For each message from the device,
125 the <code>onEvent</code> callback will receive
126 an event object with the following properties:
127 </p>
129 <br>
131 <table class="simple">
132 <tr>
133 <th scope="col"> Property </th>
134 <th scope="col"> Description </th>
135 </tr>
136 <tr>
137 <td>type (string)</td>
138 <td>Currently always contains the string "transferResult".</td>
139 </tr>
140 <tr>
141 <td>resultCode (integer)</td>
142 <td>0 is success; other values indicate failure.</td>
143 </tr>
144 <tr>
145 <td>data (arraybuffer)</td>
146 <td>Contains the data sent by the device.
147 </td>
148 </tr>
149 <tr>
150 <td>error (string)</td>
151 <td>If resultCode is not 0, this field will contain a textual description of the error that occurred during the transfer.
152 </td>
153 </tr>
154 </table>
157 Example:
158 </p>
160 <pre>
161 var onUsbEvent = function(event) {
162 if (event &amp;&amp; event.resultCode===0 &amp;&amp; event.data) {
163 console.log(“got ”+event.data.byteLength+" bytes");
167 chrome.usb.findDevice( vendorId, productId, {"onEvent": onUsbEvent}, onDeviceFound);
168 </pre>
170 <h3 id="control_transfers">CONTROL transfers</h3>
173 Control transfers are generally used to send or receive configuration
174 or command parameters to a USB device.
175 The method is simple and receives three parameters:
176 </p>
178 <pre>
179 chrome.usb.controlTransfer(deviceObj, transferInfo, transferCallback)
180 </pre>
182 <br>
184 <table class="simple">
185 <tr>
186 <th scope="col"> Parameter (types)</th>
187 <th scope="col"> Description </th>
188 </tr>
189 <tr>
190 <td>deviceObj</td>
191 <td>Object sent in <code>findDevice()</code> callback.</td>
192 </tr>
193 <tr>
194 <td>transferInfo</td>
195 <td>Parameter object with values from the table below.
196 Check your USB device protocol specification for specifics.</td>
197 </tr>
198 <tr>
199 <td>transferCallback()</td>
200 <td>Invoked when the transfer has completed.
201 Please note this only indicates that
202 the transfer has been processed.
203 The device's response, if any, will always be sent through
204 the <code>onEvent()</code> callback set on <code>findDevice()</code>.
205 </td>
206 </tr>
207 </table>
210 Values for <code>transferInfo</code> object:
211 </p>
213 <table class="simple">
214 <tr>
215 <th scope="col"> Value </th>
216 <th scope="col"> Description </th>
217 </tr>
218 <tr>
219 <td>requestType&nbsp;(string)</td>
220 <td>"vendor", "standard", "class" or "reserved".</td>
221 </tr>
222 <tr>
223 <td>recipient&nbsp;(string)</td>
224 <td>"device", "interface", "endpoint" or "other".</td>
225 </tr>
226 <tr>
227 <td>direction&nbsp;(string)</td>
228 <td>"in" or "out".
229 "in" direction is used to notify the device
230 that it should send information to the host.
231 All communication in a USB bus is host-initiated,
232 so use an 'in' transfer to allow a device
233 to send information back.</td>
234 </tr>
235 <tr>
236 <td>request&nbsp;(integer)</td>
237 <td>Defined by your device's protocol.</td>
238 </tr>
239 <tr>
240 <td>value&nbsp;(integer)</td>
241 <td>Defined by your device's protocol.</td>
242 </tr>
243 <tr>
244 <td>index&nbsp;(integer)</td>
245 <td>Defined by your device's protocol.</td>
246 </tr>
247 <tr>
248 <td>length&nbsp;(integer)</td>
249 <td>Only used when direction is "in".
250 Notifies the device that this is the amount
251 of data the host is expecting in response.</td>
252 </tr>
253 <tr>
254 <td>data&nbsp;(arraybuffer)</td>
255 <td>Defined by your device's protocol,
256 required when direction is "out".</td>
257 </tr>
258 </table>
261 Example:
262 </p>
264 <pre>
265 var transferInfo = {
266 "requestType": "vendor",
267 "recipient": "device",
268 "direction": "out",
269 "request": 0x31,
270 "value": 120,
271 "index": 0,
272 "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer
274 chrome.usb.controlTransfer(deviceObj, transferInfo, optionalCallback);
275 </pre>
277 <h3 id="isochronous_transfers">ISOCHRONOUS transfers</h3>
280 Isochronous transfers is the most complex type of USB transfers. They are commonly used for streams of data, like video and sound. To initiate an isochronous transfer (either inbound or outbound), you must use:
281 </p>
283 <pre>
284 chrome.usb.isochronousTransfer(deviceObj, isochronousTransferInfo, transferCallback)
285 </pre>
287 <br>
289 <table class="simple">
290 <tr>
291 <th scope="col"> Parameter </th>
292 <th scope="col"> Description </th>
293 </tr>
294 <tr>
295 <td>deviceObj</td>
296 <td>Object sent on <code>findDevice()</code> callback.</td>
297 </tr>
298 <tr>
299 <td>isochronousTransferInfo</td>
300 <td>Parameter object with the values in the table below.</td>
301 </tr>
302 <tr>
303 <td>transferCallback()</td>
304 <td>Invoked when the transfer has completed.
305 Notice that this callback doesn't contain any response from the device.
306 It's just to notify you that the asynchronous transfer request
307 has been processed and you can go ahead.
308 The device's response, if any, will always be sent through
309 the <code>onEvent()</code> callback set on <code>findDevice()</code>.
310 </td>
311 </tr>
312 </table>
315 Values for <code>isochronousTransferInfo</code> object:
316 </p>
318 <table class="simple">
319 <tr>
320 <th scope="col"> Value </th>
321 <th scope="col"> Description </th>
322 </tr>
323 <tr>
324 <td>transferInfo&nbsp;(object)</td>
325 <td>An object with the following attributes:<br>
326 <b>direction (string): </b>"in" or "out".<br>
327 <b>endpoint (integer): </b>defined by your device. Usually can be found by looking at an USB instrospection tool, like <code>lsusb -v</code><br>
328 <b>length (integer): </b>only used when direction is "in".
329 Notifies the device that this is the amount
330 of data the host is expecting in response. Should be AT LEAST <code>packets * packetLength</code><br>
331 <b>data (arraybuffer): </b>defined by your device's protocol;
332 only used when direction is "out".
333 </td>
334 </tr>
335 <tr>
336 <td>packets&nbsp;(integer)</td>
337 <td>Total number of packets expected in this transfer.</td>
338 </tr>
339 <tr>
340 <td>packetLength&nbsp;(integer)</td>
341 <td>Expected length of each packet in this transfer.</td>
342 </tr>
343 </table>
346 Example:
347 </p>
349 <pre>
350 var transferInfo = {
351 "direction": "in",
352 "endpoint": 1,
353 "length": 2560
355 var isoTransferInfo = {
356 "transferInfo": transferInfo,
357 "packets": 20,
358 "packetLength": 128
360 chrome.usb.isochronousTransfer(deviceObj, isoTransferInfo, optionalCallback);
361 </pre>
364 <b>Notes:</b> One isochronous transfer will contain <code>isoTransferInfo.packets</code> packets of <code>isoTransferInfo.packetLength</code> bytes. If it is an inbound transfer (your code requested data from the device), the <code>data</code> field in the onUsbEvent will be an ArrayBuffer of size <code>transferInfo.length</code>. It is your duty to walk through this ArrayBuffer and extract the different packets, each starting at a multiple of <code>isoTransferInfo.packetLength</code> bytes. If you are expecting a stream of data from the device, remember that you will have to send one "inbound" transfer for each transfer you expect back. USB devices don't send transfers to the bus unless the host explicitly requests them through "inbound" transfers.
365 </p>
367 <h3 id="bulk_transfers">BULK transfers</h3>
370 Bulk transfer is an USB transfer type commonly used
371 to transfer a large amount of data in a reliable way.
372 The method has three parameters:
373 </p>
375 <pre>
376 chrome.usb.bulkTransfer(deviceObj, transferInfo, transferCallback)
377 </pre>
379 <br>
381 <table class="simple">
382 <tr>
383 <th scope="col"> Parameter </th>
384 <th scope="col"> Description </th>
385 </tr>
386 <tr>
387 <td>deviceObj</td>
388 <td>Object sent on <code>findDevice()</code> callback.</td>
389 </tr>
390 <tr>
391 <td>transferInfo</td>
392 <td>Parameter object with the values in the table below.</td>
393 </tr>
394 <tr>
395 <td>transferCallback</td>
396 <td>Invoked when the transfer has completed.
397 Notice that this callback doesn't contain the device's response.
398 It's just to notify your code that the asynchronous transfer request
399 has been processed and you can go ahead.
400 The device's response, if any, will always be sent through
401 the <code>onEvent()</code> callback set on <code>findDevice()</code>.
402 </td>
403 </tr>
404 </table>
407 Values for <code>transferInfo</code> object:
408 </p>
410 <table class="simple">
411 <tr>
412 <th scope="col"> Value </th>
413 <th scope="col"> Description </th>
414 </tr>
415 <tr>
416 <td>direction (string)</td>
417 <td>"in" or "out".</td>
418 </tr>
419 <tr>
420 <td>endpoint (integer)</td>
421 <td>Defined by your device's protocol.</td>
422 </tr>
423 <tr>
424 <td>length (integer)</td>
425 <td>Only used when direction is "in".
426 Notifies the device that this is the amount
427 of data the host is expecting in response.</td>
428 </tr>
429 <tr>
430 <td>data (ArrayBuffer)</td>
431 <td>Defined by your device's protocol;
432 only used when direction is "out".</td>
433 </tr>
434 </table>
437 Example:
438 </p>
440 <pre>
441 var transferInfo = {
442 "direction": "out",
443 "endpoint": 1,
444 "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer
446 </pre>
448 <h3 id="interrupt_transfers">INTERRUPT transfers</h3>
451 Interrupt transfers are used to send important notifications.
452 Since all USB communication is initiated by the host,
453 host code usually polls the device periodically,
454 sending interrupt IN transfers that will make the device send data back
455 if there is anything in the interrupt queue.
456 The method has three parameters:
457 </p>
459 <pre>
460 chrome.usb.interruptTransfer(deviceObj, transferInfo, transferCallback)
461 </pre>
463 <br>
465 <table class="simple">
466 <tr>
467 <th scope="col"> Parameter </th>
468 <th scope="col"> Description </th>
469 </tr>
470 <tr>
471 <td>deviceObj</td>
472 <td>Object sent on <code>findDevice()</code> callback.</td>
473 </tr>
474 <tr>
475 <td>transferInfo</td>
476 <td>Parameter object with the values in the table below.</td>
477 </tr>
478 <tr>
479 <td>transferCallback</td>
480 <td>Invoked when the transfer has completed.
481 Notice that this callback doesn't contain the device's response.
482 It's just to notify your code that the asynchronous transfer request
483 has been processed and you can go ahead.
484 The device's response, if any, will always be sent through
485 the <code>onEvent()</code> callback set on <code>findDevice()</code>.
486 </td>
487 </tr>
488 </table>
491 Values for <code>transferInfo</code> object:
492 </p>
494 <table class="simple">
495 <tr>
496 <th scope="col"> Value </th>
497 <th scope="col"> Description </th>
498 </tr>
499 <tr>
500 <td>direction (string)</td>
501 <td>"in" or "out".</td>
502 </tr>
503 <tr>
504 <td>endpoint (integer)</td>
505 <td>Defined by your device's protocol.</td>
506 </tr>
507 <tr>
508 <td>length (integer)</td>
509 <td>Only used when direction is "in".
510 Notifies the device that this is the amount
511 of data the host is expecting in response.</td>
512 </tr>
513 <tr>
514 <td>data (ArrayBuffer)</td>
515 <td>Defined by your device's protocol;
516 only used when direction is "out".</td>
517 </tr>
520 Example:
521 </p>
523 <pre>
524 var transferInfo = {
525 "direction": "in",
526 "endpoint": 1,
527 "length": 2
529 chrome.usb.interruptTransfer(deviceObj, transferInfo, optionalCallback);
530 </pre>
532 <h3 id="caveats">Caveats</h3>
535 On most Linux systems, USB devices are mapped with read-only permissions by default. To access it through this API, your user will need to have write access too. A simple solution is to set a udev rule. Create a file <code>/etc/udev/rules.d/50-yourdevicename.rules</code>
536 with the following content:
537 </p>
539 <pre>
540 SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
541 </pre>
544 Then, just restart the udev daemon: <code>service udev restart</code>. You can check if the permissions are correctly set by:
545 <ul>
546 <li>Find the bus and device numbers in <code>lsusb</code></li>
547 <li><code>ls -al /dev/bus/usb/[bus]/[device]</code>. This file should be owned by group "plugdev" and have group write permissions.</li>
548 </ul>
549 </p>
552 Not all devices can be accessed through this API. In general, devices are not accessible either because the Operating System's kernel or a native driver holds them off from user space code. Some examples are devices with HID profiles on OSX systems and USB pen drives.
553 </p>
555 <h2 id="serial">Accessing serial devices</h2>
558 You can use the serial API to read
559 and write from a serial device.
560 </p>
562 <h3 id="requirement">Manifest requirement</h3>
565 You must add the "serial" permission to the manifest file:
566 </p>
567 <pre>
568 "permissions": [
569 "serial"
571 </pre>
573 <h3 id="listing">Listing available serial ports</h3>
576 To get a list of available serial ports,
577 use the <code>getPorts()</code> method. <b>Note:</b> not all serial ports are available. The API uses a heuristic based on the name of the port to only expose serial devices that are expected to be safe.
578 </p>
580 <pre>
581 var onGetPorts = function(ports) {
582 for (var i=0; i&lt;ports.length; i++) {
583 console.log(ports[i]);
586 chrome.serial.getPorts(onGetPorts);
587 </pre>
589 <h3 id="opening">Opening a serial device</h3>
592 If you know the serial port name, you can open it for read and write using the <code>open</code> method:
593 </p>
595 <pre>
596 chrome.usb.open(portName, options, openCallback)
597 </pre>
599 <table border="0">
600 <tr>
601 <th scope="col"> Parameter </th>
602 <th scope="col"> Description </th>
603 </tr>
604 <tr>
605 <td>portName&nbsp;(string)</td>
606 <td>If your device's port name is unknown, you can use the <code>getPorts</code> method.</td>
607 </tr>
608 <tr>
609 <td>options&nbsp;(object)</td>
610 <td>Parameter object with one single value: <code>bitrage</code>, an integer specifying the desired bitrate used to communicate with the serial port.</td>
611 </tr>
612 <tr>
613 <td>openCallback</td>
614 <td>Invoked when the port has been successfully opened. The callback will be called with one parameter, <code>openInfo</code>, that has one attribute, <code>connectionId</code>. Save this id, because you will need it to actually communicate with the port.
615 </td>
616 </tr>
617 </table>
619 <p>A simple example:</p>
621 <pre>
622 var onOpen = function(connectionInfo) {
623 // The serial port has been opened. Save its id to use later.
624 _this.connectionId = connectionInfo.connectionId;
625 // Do whatever you need to do with the opened port.
627 // Open the serial port /dev/ttyS01
628 chrome.serial.open("/dev/ttyS01", {bitrate: 115200}, onOpen);
629 </pre>
631 <h3 id="closing">Closing a serial port</h3>
634 Closing a serial port is simple but very important. See the example below:
635 </p>
637 <pre>
638 var onClose = function(result) {
639 console.log(“Serial port closed”);
641 chrome.serial.close(connectionId, onClose);
642 </pre>
644 <h3 id="reading">Reading from a serial port</h3>
647 The serial API reads from the serial port and
648 delivers the read bytes as an ArrayBuffer.
649 There is no guarantee that all the requested bytes, even if available in the port, will be read in one chunk.
650 The following example can accumulate read bytes, at most 128 at a time, until a new line is read,
651 and then call a listener with the <code>ArrayBuffer</code> bytes converted to a String:
652 </p>
654 <pre>
655 var dataRead='';
657 var onCharRead=function(readInfo) {
658 if (!connectionInfo) {
659 return;
661 if (readInfo &amp;&amp; readInfo.bytesRead>0 &amp;&amp; readInfo.data) {
662 var str=ab2str(readInfo.data);
663 if (str[readInfo.bytesRead-1]==='\n') {
664 dataRead+=str.substring(0, readInfo.bytesRead-1);
665 onLineRead(dataRead);
666 dataRead="";
667 } else {
668 dataRead+=str;
671 chrome.serial.read(connectionId, 128, onCharRead);
674 /* Convert an ArrayBuffer to a String, using UTF-8 as the encoding scheme.
675 This is consistent with how Arduino sends characters by default */
676 var ab2str=function(buf) {
677 return String.fromCharCode.apply(null, new Uint8Array(buf));
679 </pre>
681 <h3 id="writing">Writing to a serial port</h3>
684 The writing routine is simpler than reading,
685 since the writing can occur all at once.
686 The only catch is that if your data protocol is String based,
687 you have to convert your output string to an <code>ArrayBuffer</code>.
688 See the code example below:
689 </p>
691 <pre>
692 var writeSerial=function(str) {
693 chrome.serial.write(connectionId, str2ab(str), onWrite);
695 // Convert string to ArrayBuffer
696 var str2ab=function(str) {
697 var buf=new ArrayBuffer(str.length);
698 var bufView=new Uint8Array(buf);
699 for (var i=0; i&lt;str.length; i++) {
700 bufView[i]=str.charCodeAt(i);
702 return buf;
704 </pre>
706 <h3 id="flushing">Flushing a serial port buffer</h3>
709 You can flush your serial port buffer by issuing the flush command:
710 </p>
712 <pre>
713 chrome.serial.flush(connectionId, onFlush);
714 </pre>
716 <p class="backtotop"><a href="#top">Back to top</a></p>