This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan
<p>This new year <a href="https://twitter.com/andreban">Andre Bandarra</a> left me a little surprise on my desk: A physical airhorner built with Web USB!</p>
<figure><video src="https://paul.kinlan.me/videos/2020-01-06-airhorner-with-added-web-usb-0.mp4" alt="Airhorner with usb" controls></video></figure>
<p><a href="https://webusb-horn.firebaseapp.com/">Check it out</a>, well actually it will be hard, Andre created a small sketch for an Arduino Uno that connects over USB that is not yet available, however the code on the site is rather neat and not too complex if you are experienced with any form of USB programming.</p>
<p>Andre's code connects to the device and waits for the user to approve, configures the connection, and then continuously reads from the device looking for the string 'ON' (which is a flag that is set when the button is pressed).</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">HardwareButton</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">airhorn</span>) {
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">airhorn</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">airhorn</span>;
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">decoder</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">TextDecoder</span>();
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">connected</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">self</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span>;
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">_loopRead</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">async</span> <span style="color:#66d9ef">function</span>() {
<span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">device</span>) {
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'no device'</span>);
<span style="color:#66d9ef">return</span>;
}
<span style="color:#66d9ef">try</span> {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">device</span>.<span style="color:#a6e22e">transferIn</span>(<span style="color:#ae81ff">2</span>, <span style="color:#ae81ff">64</span>);
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">command</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">decoder</span>.<span style="color:#a6e22e">decode</span>(<span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">data</span>);
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">command</span>.<span style="color:#a6e22e">trim</span>() <span style="color:#f92672">===</span> <span style="color:#e6db74">'ON'</span>) {
<span style="color:#a6e22e">airhorn</span>.<span style="color:#a6e22e">start</span>({<span style="color:#a6e22e">loop</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>});
} <span style="color:#66d9ef">else</span> {
<span style="color:#a6e22e">airhorn</span>.<span style="color:#a6e22e">stop</span>();
}
<span style="color:#a6e22e">self</span>.<span style="color:#a6e22e">_loopRead</span>();
} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">e</span>) {
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Error reading data'</span>, <span style="color:#a6e22e">e</span>);
}
};
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">connect</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">async</span> <span style="color:#66d9ef">function</span>() {
<span style="color:#66d9ef">try</span> {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">device</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">navigator</span>.<span style="color:#a6e22e">usb</span>.<span style="color:#a6e22e">requestDevice</span>({
<span style="color:#a6e22e">filters</span><span style="color:#f92672">:</span> [{<span style="color:#e6db74">'vendorId'</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0x2341</span>, <span style="color:#e6db74">'productId'</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0x8057</span>}]
});
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">device</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">device</span>;
<span style="color:#a6e22e">await</span> <span style="color:#a6e22e">device</span>.<span style="color:#a6e22e">open</span>();
<span style="color:#a6e22e">await</span> <span style="color:#a6e22e">device</span>.<span style="color:#a6e22e">selectConfiguration</span>(<span style="color:#ae81ff">1</span>);
<span style="color:#a6e22e">await</span> <span style="color:#a6e22e">device</span>.<span style="color:#a6e22e">claimInterface</span>(<span style="color:#ae81ff">0</span>);
<span style="color:#a6e22e">await</span> <span style="color:#a6e22e">device</span>.<span style="color:#a6e22e">selectAlternateInterface</span>(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>);
<span style="color:#a6e22e">await</span> <span style="color:#a6e22e">device</span>.<span style="color:#a6e22e">controlTransferOut</span>({
<span style="color:#e6db74">'requestType'</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'class'</span>,
<span style="color:#e6db74">'recipient'</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'interface'</span>,
<span style="color:#e6db74">'request'</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0x22</span>,
<span style="color:#e6db74">'value'</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0x01</span>,
<span style="color:#e6db74">'index'</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0x00</span>,
});
<span style="color:#a6e22e">self</span>.<span style="color:#a6e22e">_loopRead</span>();
} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">e</span>) {
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Failed to Connect: '</span>, <span style="color:#a6e22e">e</span>);
}
};
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">disconnect</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">async</span> <span style="color:#66d9ef">function</span>() {
<span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">device</span>) {
<span style="color:#66d9ef">return</span>;
}
<span style="color:#a6e22e">await</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">device</span>.<span style="color:#a6e22e">controlTransferOut</span>({
<span style="color:#e6db74">'requestType'</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'class'</span>,
<span style="color:#e6db74">'recipient'</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'interface'</span>,
<span style="color:#e6db74">'request'</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0x22</span>,
<span style="color:#e6db74">'value'</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0x00</span>,
<span style="color:#e6db74">'index'</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0x00</span>,
});
<span style="color:#a6e22e">await</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">device</span>.<span style="color:#a6e22e">close</span>();
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">device</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>;
};
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">init</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>() {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">buttonDiv</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">'#connect'</span>);
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">button</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">buttonDiv</span>.<span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">'button'</span>);
<span style="color:#a6e22e">button</span>.<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">'click'</span>, <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">connect</span>.<span style="color:#a6e22e">bind</span>(<span style="color:#66d9ef">this</span>));
<span style="color:#a6e22e">button</span>.<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">'touchend'</span>, <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">connect</span>.<span style="color:#a6e22e">bind</span>(<span style="color:#66d9ef">this</span>));
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">navigator</span>.<span style="color:#a6e22e">usb</span>) {
<span style="color:#a6e22e">buttonDiv</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">add</span>(<span style="color:#e6db74">'available'</span>);
}
};
<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">init</span>();
};
</code></pre></div><p>If you are interested in what the Arduino side of things looks like, Andre will release the code soon, but it's directly inspired by the <a href="https://github.com/webusb/arduino">WebUSB examples for Arduino</a>.</p>
This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan