| <h1>Accessing Hardware Devices</h1> |
| |
| <p> |
| This doc shows you how Chrome Apps can connect to USB devices and read from |
| and write to a user's serial ports. See also the reference docs for the <a |
| href="usb.html">USB API</a> and the <a href="serial.html">Serial API</a>. |
| The <a href="bluetooth.html">Bluetooth API</a> is also available; we've |
| included a link to a Bluetooth sample below. |
| </p> |
| |
| <p class="note"> |
| <b>API Samples: </b> Want to play with the code? Check out the |
| <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/serial">serial</a>, |
| <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/servo">servo</a>, |
| <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/usb">usb</a>, |
| and |
| <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/zephyr_hxm">zephyr_hxm |
| Bluetooth</a> samples. |
| </p> |
| |
| <h2 id="usb">Accessing USB devices</h2> |
| |
| <p> |
| You can use the <a href="usb.html">USB API</a> to communicate with USB devices |
| using only JavaScript code. Some devices are not accessible through this API |
| – see <a href="#caveats">Caveats</a> below for details. |
| </p> |
| |
| <p> |
| For background information about USB, see the official |
| <a href="http://www.usb.org/home">USB specifications</a>. <br/> |
| <a href="http://www.beyondlogic.org/usbnutshell/usb1.shtml"> |
| <i>USB in a NutShell</i></a> |
| is a reasonable crash course that you may find helpful. |
| </p> |
| |
| <h3 id="manifest">Manifest requirement</h3> |
| |
| <p>The USB API requires the "usb" permission in the manifest file:</p> |
| |
| <pre data-filename="manifest.json"> |
| "permissions": [ |
| "usb" |
| ] |
| </pre> |
| |
| <p>In addition, in order to prevent |
| <a href="http://en.wikipedia.org/wiki/Device_fingerprint">finger-printing</a>, |
| you must declare all the device types you want to access in the manifest file. |
| Each type of USB device corresponds to a vendor id/product id (VID/PID) pair. |
| You can use $ref:usb.getDevices to enumerate devices by their VID/PID |
| pair. |
| </p> |
| <p> |
| You must declare the VID/PID pairs for each type of device you want to use |
| under the "usbDevices" permission in your app's manifest file, as shown in the |
| example below:</p> |
| |
| <pre data-filename="manifest.json"> |
| "permissions": [ |
| "usbDevices": [ |
| { |
| "vendorId": 123, |
| "productId": 456 |
| } |
| ] |
| ] |
| </pre> |
| |
| <p class="note">Note that only decimal numbers are allowed in JSON format. |
| You cannot use hexadecimal numbers in these fields.</p> |
| |
| <h3 id="finding_device">Finding a device</h3> |
| |
| <p> |
| To determine whether one or more specific devices are connected to a user's |
| system, use the $ref:usb.getDevices method: |
| </p> |
| |
| <pre> |
| chrome.usb.getDevices(enumerateDevicesOptions, callback); |
| </pre> |
| |
| <br/> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Parameter (type)</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>EnumerateDevicesOptions (object)</td> |
| <td>An object specifying both a <code>vendorId</code> (long) and |
| <code>productId</code> (long) used to find the correct type of device on |
| the bus. Your manifest must declare the <code>usbDevices</code> permission |
| section listing all the <code>vendorId</code> and <code>deviceId</code> |
| pairs your app wants to access. |
| </td> |
| </tr> |
| <tr> |
| <td>callback (function)</td> |
| <td>Called when the device enumeration is finished. The callback will be |
| executed with one parameter, an array of <code>Device</code> objects with |
| three properties: <code>device</code>, <code>vendorId</code>, |
| <code>productId</code>. The device property is a stable identifier for a |
| connected device. It will not change until the device is unplugged. The |
| detail of the identifier is opaque and subject to change. Do not rely on |
| its current type. <br/> |
| If no devices are found, the array will be empty. |
| </td> |
| </tr> |
| </table> |
| |
| <p>Example:</p> |
| |
| <pre> |
| function onDeviceFound(devices) { |
| this.devices=devices; |
| if (devices) { |
| if (devices.length > 0) { |
| console.log("Device(s) found: "+devices.length); |
| } else { |
| console.log("Device could not be found"); |
| } |
| } else { |
| console.log("Permission denied."); |
| } |
| } |
| |
| chrome.usb.getDevices({"vendorId": vendorId, "productId": productId}, onDeviceFound); |
| </pre> |
| |
| <h3 id="usb_open">Opening a device</h3> |
| <p> |
| Once the <code>Device</code> objects are returned, you can open a device using |
| usb.openDevice to obtain a connection handle. You can only |
| communicate with USB devices using connection handles. |
| </p> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Property</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>device</td> |
| <td>Object received in $ref:usb.getDevices callback.</td> |
| </tr> |
| <tr> |
| <td>data (arraybuffer)</td> |
| <td>Contains the data sent by the device if the transfer was inbound.</td> |
| </tr> |
| </table> |
| |
| <p>Example:</p> |
| |
| <pre> |
| var usbConnection = null; |
| var onOpenCallback = function(connection) { |
| if (connection) { |
| usbConnection = connection; |
| console.log("Device opened."); |
| } else { |
| console.log("Device failed to open."); |
| } |
| }; |
| |
| chrome.usb.openDevice(device, onOpenCallback); |
| </pre> |
| |
| <p class="note"> |
| Not every device can be opened successfully. In general, operating systems |
| lock down many types of USB interfaces (e.g. keyboards and mice, mass storage |
| devices, webcams, etc.) and they cannot be claimed by user applications. |
| On Linux (other than Chrome OS), once an interface of a device is locked down by |
| the OS, the whole device is locked down (because all the interfaces shares the |
| same device file), even if the other interfaces of the device can be used in |
| theory. On Chrome OS, you can request access to unlocked interfaces using the |
| $ref:usb.requestAccess method. If permitted, the permission broker will |
| unlock the device file for you. |
| </p> |
| |
| <p> |
| To simplify the opening process, you can use the $ref:usb.findDevices |
| method, which enumerates, requests access, and opens devices in one call: |
| </p> |
| |
| <pre> |
| chrome.usb.findDevices({"vendorId": vendorId, "productId": productId, "interfaceId": interfaceId}, callback); |
| </pre> |
| <p>which is equivalent to:</p> |
| <pre> |
| chrome.usb.getDevices({"vendorId": vendorId, "productId": productId}, function (devices) { |
| if (!devices) { |
| console.log("Error enumerating devices."); |
| callback(); |
| return; |
| } |
| var connections = [], pendingAccessRequests = devices.length; |
| devices.forEach(function (device) { |
| chrome.usb.requestAccess(interfaceId, function () { |
| // No need to check for errors at this point. |
| // Nothing can be done if an error occurs anyway. You should always try |
| // to open the device. |
| chrome.usb.openDevices(device, function (connection) { |
| if (connection) connections.push(connection); |
| pendingAccessRequests--; |
| if (pendingAccessRequests == 0) { |
| callback(connections); |
| } |
| }); |
| }); |
| }) |
| }); |
| </pre> |
| |
| <h3 id="usb_transfers">USB transfers and receiving data from a device</h3> |
| |
| <p> |
| The USB protocol defines four types of transfers: |
| <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>. |
| These transfers are described below. |
| </p> |
| |
| <p> |
| Transfers can occur in both directions: device-to-host (inbound), and |
| host-to-device (outbound). Due to the nature of the USB protocol, |
| both inbound and outbound messages must be initiated by the host (the |
| computer that runs the Chrome app). |
| For inbound (device-to-host) messages, the host (initiated by your JavaScript |
| code) sends a message flagged as "inbound" to the device. |
| The details of the message depend 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 asynchronously to the |
| callback you specify in the transfer method. |
| An outbound (host-to-device) message is similar, but the response doesn't |
| contain data returned from the device. |
| </p> |
| |
| <p> |
| For each message from the device, the specified callback will receive an event |
| object with the following properties: |
| </p> |
| |
| <br> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Property</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>resultCode (integer)</td> |
| <td>0 is success; other values indicate failure. An error string can be<br/> |
| read from <code>chrome.extension.lastError</code> when a failure is<br/> |
| indicated. |
| </td> |
| </tr> |
| <tr> |
| <td>data (arraybuffer)</td> |
| <td>Contains the data sent by the device if the transfer was inbound.</td> |
| </tr> |
| </table> |
| |
| <p>Example:</p> |
| |
| <pre> |
| var onTransferCallback = function(event) { |
| if (event && event.resultCode === 0 && event.data) { |
| console.log("got " + event.data.byteLength + " bytes"); |
| } |
| }; |
| |
| chrome.usb.bulkTransfer(connectionHandle, transferInfo, onTransferCallback); |
| </pre> |
| |
| <h3 id="control_transfers">CONTROL transfers</h3> |
| |
| <p>Control transfers are generally used to send or receive configuration or |
| command parameters to a USB device. The controlTransfer method always sends |
| to/reads from endpoint 0, and no claimInterface is required. |
| The method is simple and receives three parameters:</p> |
| |
| <pre> |
| chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback) |
| </pre> |
| |
| <br> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Parameter (types)</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>connectionHandle</td> |
| <td>Object received in $ref:usb.openDevice callback. |
| </td> |
| </tr> |
| <tr> |
| <td>transferInfo</td> |
| <td>Parameter object with values from the table below. Check your USB |
| device protocol specification for details. |
| </td> |
| </tr> |
| <tr> |
| <td>transferCallback()</td> |
| <td>Invoked when the transfer has completed.</td> |
| </tr> |
| </table> |
| |
| <p> |
| Values for |
| <code>transferInfo</code> |
| object: |
| </p> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Value</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>requestType (string)</td> |
| <td>"vendor", "standard", "class" or "reserved".</td> |
| </tr> |
| <tr> |
| <td>recipient (string)</td> |
| <td>"device", "interface", "endpoint" or "other".</td> |
| </tr> |
| <tr> |
| <td>direction (string)</td> |
| <td>"in" or "out". The "in" direction is used to notify the device that<br/> |
| it should send information to the host. All communication on a USB<br/> |
| bus is host-initiated, so use an "in" transfer to allow a device to<br/> |
| send information back. |
| </td> |
| </tr> |
| <tr> |
| <td>request (integer)</td> |
| <td>Defined by your device's protocol.</td> |
| </tr> |
| <tr> |
| <td>value (integer)</td> |
| <td>Defined by your device's protocol.</td> |
| </tr> |
| <tr> |
| <td>index (integer)</td> |
| <td>Defined by your device's protocol.</td> |
| </tr> |
| <tr> |
| <td>length (integer)</td> |
| <td>Only used when direction is "in". Notifies the device that this is |
| the amount of data the host is expecting in response. |
| </td> |
| </tr> |
| <tr> |
| <td>data (arraybuffer)</td> |
| <td>Defined by your device's protocol, required when direction is |
| "out". |
| </td> |
| </tr> |
| </table> |
| |
| <p>Example:</p> |
| |
| <pre> |
| var transferInfo = { |
| "requestType": "vendor", |
| "recipient": "device", |
| "direction": "out", |
| "request": 0x31, |
| "value": 120, |
| "index": 0, |
| // Note that the ArrayBuffer, not the TypedArray itself is used. |
| "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer |
| }; |
| chrome.usb.controlTransfer(connectionHandle, transferInfo, optionalCallback); |
| </pre> |
| |
| <h3 id="isochronous_transfers">ISOCHRONOUS transfers</h3> |
| |
| <p>Isochronous transfers are the most complex type of USB transfer. They are |
| commonly used for streams of data, like video and sound. To initiate an |
| isochronous transfer (either inbound or outbound), you must use |
| the $ref:usb.isochronousTransfer method:</p> |
| |
| <pre> |
| chrome.usb.isochronousTransfer(connectionHandle, isochronousTransferInfo, transferCallback) |
| </pre> |
| |
| <br/> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Parameter</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>connectionHandle</td> |
| <td>Object received in $ref:usb.openDevice callback. |
| </td> |
| </tr> |
| <tr> |
| <td>isochronousTransferInfo</td> |
| <td>Parameter object with the values in the table below.</td> |
| </tr> |
| <tr> |
| <td>transferCallback()</td> |
| <td>Invoked when the transfer has completed.</td> |
| </tr> |
| </table> |
| |
| <p> |
| Values for |
| <code>isochronousTransferInfo</code> |
| object: |
| </p> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Value</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>transferInfo (object)</td> |
| <td>An object with the following attributes:<br/> |
| <b>direction (string): </b>"in" or "out".<br/> |
| <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/> |
| <b>length (integer): </b>only |
| used when direction is "in". Notifies the device that this is the amount |
| of data the host is expecting in response. <br/> |
| |
| Should be AT LEAST <code>packets</code> × <code>packetLength</code>. |
| <br/> |
| <b>data (arraybuffer): </b>defined by your device's protocol; |
| only used when direction is "out". |
| </td> |
| </tr> |
| <tr> |
| <td>packets (integer)</td> |
| <td>Total number of packets expected in this transfer.</td> |
| </tr> |
| <tr> |
| <td>packetLength (integer)</td> |
| <td>Expected length of each packet in this transfer.</td> |
| </tr> |
| </table> |
| |
| <p>Example:</p> |
| |
| <pre> |
| var transferInfo = { |
| "direction": "in", |
| "endpoint": 1, |
| "length": 2560 |
| }; |
| |
| var isoTransferInfo = { |
| "transferInfo": transferInfo, |
| "packets": 20, |
| "packetLength": 128 |
| }; |
| |
| chrome.usb.isochronousTransfer(connectionHandle, isoTransferInfo, optionalCallback); |
| </pre> |
| |
| <p class="note"> |
| <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. <br/> |
| <br/> |
| 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 USB bus unless the host |
| explicitly requests them through "inbound" transfers. |
| </p> |
| |
| <h3 id="bulk_transfers">BULK transfers</h3> |
| |
| <p>Bulk transfers are commonly used to transfer a large amount of |
| non-time-sensitive data in a reliable way. |
| $ref:usb.bulkTransfer has three parameters:</p> |
| |
| <pre> |
| chrome.usb.bulkTransfer(connectionHandle, transferInfo, transferCallback); |
| </pre> |
| |
| <br> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Parameter</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>connectionHandle</td> |
| <td>Object received in $ref:usb.openDevice callback. |
| </td> |
| </tr> |
| <tr> |
| <td>transferInfo</td> |
| <td>Parameter object with the values in the table below.</td> |
| </tr> |
| <tr> |
| <td>transferCallback</td> |
| <td>Invoked when the transfer has completed.</td> |
| </tr> |
| </table> |
| |
| <p> |
| Values for |
| <code>transferInfo</code> |
| object: |
| </p> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Value</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>direction (string)</td> |
| <td>"in" or "out".</td> |
| </tr> |
| <tr> |
| <td>endpoint (integer)</td> |
| <td>Defined by your device's protocol.</td> |
| </tr> |
| <tr> |
| <td>length (integer)</td> |
| <td>Only used when direction is "in". Notifies the device that this is |
| the amount of data the host is expecting in response. |
| </td> |
| </tr> |
| <tr> |
| <td>data (ArrayBuffer)</td> |
| <td>Defined by your device's protocol; only used when direction is |
| "out". |
| </td> |
| </tr> |
| </table> |
| |
| <p>Example:</p> |
| |
| <pre> |
| var transferInfo = { |
| "direction": "out", |
| "endpoint": 1, |
| "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer |
| }; |
| </pre> |
| |
| <h3 id="interrupt_transfers">INTERRUPT transfers</h3> |
| |
| <p>Interrupt transfers are used to small amount of time sensitive data. |
| Since all USB communication is initiated by the host, host code usually polls |
| the device periodically, sending interrupt IN transfers that will make the |
| device send data back if there is anything in the interrupt queue (maintained |
| by the device). |
| $ref:usb.interruptTransfer has three parameters:</p> |
| |
| <pre> |
| chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback); |
| </pre> |
| |
| <br> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Parameter</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>connectionHandle</td> |
| <td>Object received in $ref:usb.openDevice callback. |
| </td> |
| </tr> |
| <tr> |
| <td>transferInfo</td> |
| <td>Parameter object with the values in the table below.</td> |
| </tr> |
| <tr> |
| <td>transferCallback</td> |
| <td>Invoked when the transfer has completed. Notice that this callback |
| doesn't contain the device's response. The purpose of the callback is |
| simply to notify your code that the asynchronous transfer requests has |
| been processed. |
| </td> |
| </tr> |
| </table> |
| |
| <p> |
| Values for <code>transferInfo</code> object: |
| </p> |
| |
| <table class="simple"> |
| <tr> |
| <th scope="col">Value</th> |
| <th scope="col">Description</th> |
| </tr> |
| <tr> |
| <td>direction (string)</td> |
| <td>"in" or "out".</td> |
| </tr> |
| <tr> |
| <td>endpoint (integer)</td> |
| <td>Defined by your device's protocol.</td> |
| </tr> |
| <tr> |
| <td>length (integer)</td> |
| <td>Only used when direction is "in". Notifies the device that this is |
| the amount of data the host is expecting in response. |
| </td> |
| </tr> |
| <tr> |
| <td>data (ArrayBuffer)</td> |
| <td>Defined by your device's protocol; only used when direction is |
| "out". |
| </td> |
| </tr> |
| </table> |
| |
| <p>Example:</p> |
| |
| <pre> |
| var transferInfo = { |
| "direction": "in", |
| "endpoint": 1, |
| "length": 2 |
| }; |
| chrome.usb.interruptTransfer(connectionHandle, transferInfo, optionalCallback); |
| </pre> |
| |
| <h3 id="caveats">Caveats</h3> |
| |
| <p>Not all devices can be accessed through the USB API. In general, devices |
| are not accessible because either 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.</p> |
| |
| <p> |
| On most Linux systems, USB devices are mapped with read-only permissions by |
| default. To open a device through this API, your user will need to have |
| write access to it too. |
| A simple solution is to set a udev rule. Create a file |
| <code>/etc/udev/rules.d/50-yourdevicename.rules</code> |
| with the following content: |
| </p> |
| |
| <pre> |
| SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev" |
| </pre> |
| |
| <p> |
| Then, just restart the udev daemon: <code>service udev restart</code>. |
| You can check if device permissions are set correctly by following these |
| steps: |
| </p> |
| |
| <ul> |
| <li>Run <code>lsusb</code> to find the bus and device numbers.</li> |
| <li>Run <code>ls -al /dev/bus/usb/[bus]/[device]</code>. This file should be |
| owned by group "plugdev" and have group write permissions. |
| </li> |
| </ul> |
| |
| <p>Your app cannot do this automatically since this this procedure requires root |
| access. We recommend that you provide instructions to end-users and link to the |
| <a href="#caveats">Caveats</a> section on this page for an explanation.</p> |
| |
| <p>On Chrome OS, simply call $ref:usb.requestAccess. The permission |
| broker does this for you.</p> |
| |
| <h2 id="serial">Accessing serial devices</h2> |
| |
| <p> |
| You can use the serial API to read |
| and write from a serial device. |
| </p> |
| |
| <h3 id="requirement">Manifest requirement</h3> |
| |
| <p> |
| You must add the "serial" permission to the manifest file: |
| </p> |
| <pre data-filename="manifest.json"> |
| "permissions": [ |
| "serial" |
| ] |
| </pre> |
| |
| <h3 id="listing">Listing available serial ports</h3> |
| |
| <p> |
| To get a list of available serial ports, |
| 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. |
| </p> |
| |
| <pre> |
| var onGetPorts = function(ports) { |
| for (var i=0; i<ports.length; i++) { |
| console.log(ports[i]); |
| } |
| } |
| chrome.serial.getPorts(onGetPorts); |
| </pre> |
| |
| <h3 id="opening">Opening a serial device</h3> |
| |
| <p> |
| If you know the serial port name, you can open it for read and write using the <code>open</code> method: |
| </p> |
| |
| <pre> |
| chrome.serial.open(portName, options, openCallback) |
| </pre> |
| |
| <table border="0"> |
| <tr> |
| <th scope="col"> Parameter </th> |
| <th scope="col"> Description </th> |
| </tr> |
| <tr> |
| <td>portName (string)</td> |
| <td>If your device's port name is unknown, you can use the <code>getPorts</code> method.</td> |
| </tr> |
| <tr> |
| <td>options (object)</td> |
| <td>Parameter object with one single value: <code>bitrate</code>, an integer specifying the desired bitrate used to communicate with the serial port.</td> |
| </tr> |
| <tr> |
| <td>openCallback</td> |
| <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. |
| </td> |
| </tr> |
| </table> |
| |
| <p>A simple example:</p> |
| |
| <pre> |
| var onOpen = function(connectionInfo) { |
| // The serial port has been opened. Save its id to use later. |
| _this.connectionId = connectionInfo.connectionId; |
| // Do whatever you need to do with the opened port. |
| } |
| // Open the serial port /dev/ttyS01 |
| chrome.serial.open("/dev/ttyS01", {bitrate: 115200}, onOpen); |
| </pre> |
| |
| <h3 id="closing">Closing a serial port</h3> |
| |
| <p> |
| Closing a serial port is simple but very important. See the example below: |
| </p> |
| |
| <pre> |
| var onClose = function(result) { |
| console.log("Serial port closed"); |
| } |
| chrome.serial.close(connectionId, onClose); |
| </pre> |
| |
| <h3 id="reading">Reading from a serial port</h3> |
| |
| <p> |
| The serial API reads from the serial port and |
| delivers the read bytes as an ArrayBuffer. |
| There is no guarantee that all the requested bytes, even if available in the port, will be read in one chunk. |
| The following example can accumulate read bytes, at most 128 at a time, until a new line is read, |
| and then call a listener with the <code>ArrayBuffer</code> bytes converted to a String: |
| </p> |
| |
| <pre> |
| var dataRead=''; |
| |
| var onCharRead=function(readInfo) { |
| if (!connectionInfo) { |
| return; |
| } |
| if (readInfo && readInfo.bytesRead>0 && readInfo.data) { |
| var str=ab2str(readInfo.data); |
| if (str[readInfo.bytesRead-1]==='\n') { |
| dataRead+=str.substring(0, readInfo.bytesRead-1); |
| onLineRead(dataRead); |
| dataRead=""; |
| } else { |
| dataRead+=str; |
| } |
| } |
| chrome.serial.read(connectionId, 128, onCharRead); |
| } |
| |
| /* Convert an ArrayBuffer to a String, using UTF-8 as the encoding scheme. |
| This is consistent with how Arduino sends characters by default */ |
| var ab2str=function(buf) { |
| return String.fromCharCode.apply(null, new Uint8Array(buf)); |
| }; |
| </pre> |
| |
| <h3 id="writing">Writing to a serial port</h3> |
| |
| <p> |
| The writing routine is simpler than reading, |
| since the writing can occur all at once. |
| The only catch is that if your data protocol is String based, |
| you have to convert your output string to an <code>ArrayBuffer</code>. |
| See the code example below: |
| </p> |
| |
| <pre> |
| var writeSerial=function(str) { |
| chrome.serial.write(connectionId, str2ab(str), onWrite); |
| } |
| // Convert string to ArrayBuffer |
| var str2ab=function(str) { |
| var buf=new ArrayBuffer(str.length); |
| var bufView=new Uint8Array(buf); |
| for (var i=0; i<str.length; i++) { |
| bufView[i]=str.charCodeAt(i); |
| } |
| return buf; |
| } |
| </pre> |
| |
| <h3 id="flushing">Flushing a serial port buffer</h3> |
| |
| <p> |
| You can flush your serial port buffer by issuing the flush command: |
| </p> |
| |
| <pre> |
| chrome.serial.flush(connectionId, onFlush); |
| </pre> |
| |
| <p class="backtotop"><a href="#top">Back to top</a></p> |