May 30, 2016

Prototyping a LED array based cellphone case

Everyone loves staring at LEDs.

I put it into an instrument on/off switch in a previous job.
Now imagine what I can do with multiple LEDs!  I want to make a beautiful cellphone case that will show different patterns on cellphone event notifications (e.g. incoming call or message, alarm).  I mocked it up with a hand-soldered APA102 arrays on a cardboard.
I am drawing power from the 4.3 V available on my development board.  Imagination is the only limit for the patterns, but a product will require many improvements:
  1. A Bluetooth connection from the cellphone to the firmware, to receive various notifications.
  2. A custom BLE service from the phone to the controller, to map different patterns to predefined events.
  3. A lightning connector to draw power straight from the phone.  The case will also have to provide another lightning port so the user can continue to charge the phone.
I used the Nordic BLE example "proximity" app--which uses the Bluetooth Immediate Alert Service and the Link Loss Service to notify both the phone and the device about the link state change--to show different LED patterns.  Here is the "link established" pattern, pulsing with the confidence of a solid connection with the phone:
And here is the "link lost" pattern, showing distress.

Regardless of whether for the iPhone or Android, the sine-qua-non for this project is a high current power source.  After reading about the MFI (made for iOS) slavery program, I decided I should work with Android instead at least at first.  I am not sure about the lightning connector, but after reading about the USB OTG, I believe it is NOT possible to both charge an Android phone and drive the OTG in host most at the same time--which would mean that the user will have to slide a switch to toggle between charging and supplying current to the LED array.

I COULD get rid of the switch by using an OTG enabled FW inside the case, which will switch itself between the peripheral and a hub role automatically, as shown below.
I do have an ST-nucleo development board, which packs a USB OTG FS peripheral inside an M3.  It can serve the peripheral role without a problem.  But when I need to charge the phone, the case will have to relay power and data to the phone.

Since the phone cannot control the case in this mode, I will just have to put a hub inside the case.  If I still want to light up the LED array even while charging, I could send the notifications over Bluetooth, as shown below:

If the case is capable of a USB hub role anyway and can be controlled over BLE, it might as well always provide the benefit of USB hub feature even when the phone is not charging.
But before introducing all these complications, let's just see some LEDs blinking when I get a phone call.

Minimum viable product using USB to SPI bridge

The APA102 timing requirement is 1 MHz +/- 20%.  If I can drive a USB slave that can drive out correct SPI signals, the APA102 array will be happy.  From a quick search for USB-SPI bridge on digikey, it seems there are roughly 3 viable options: FTDI FT4222H (~$1.43 for a lot order), Micron MCP2210 (~$1.50 for a lot order), SiLab CP2130 ($1.57 for lot order).  CP2130 does not seem to support Android, which I must have.  I've used FTDI's D2XX driver in a previous project, and the J2XX driver should be usable from my Android app.  I thought 12 MHz external crystal is required, but apparently it is not required in the bus powered configuration).  MCP2210 is exposed to the host as a HID device, with a report that can drive out up to 62 bytes per USB command.  An SPI transaction may be up to 0xFFFF bytes long.  Initially, it looked quite unattractive to me because of the requirement for an external 12 MHz oscillator, but the much lower current draw when bus powered configuration won me over (13 mA vs. 50 mA for the FT4222H--when run at the slowest rate of 24 MHz).

I bought an ADM00419 (MCP2210 breakout module) from the Microchip online store, and ported the Microchip's example Android app MCP2210 (which is currently in the ADT project form) into Android Studio 2 to connect to the device on my phone.  The ADT project does NOT import cleanly into the Android Studio--Android Studio could improve in this area.  For example, I manually fixed the minimum SDK version discrepancy among the 3 projects.  The import put the min SDK version should have been imported into the build.gradle files instead of the AndroidManifest.xml.

App crashes on Samsung Galaxy S5, Android 5.1.1

The example app crashed right away after detecting the MCP2210.  Here's the stack trace:

Caused by: java.lang.SecurityException: Admin  does not have android.permission.sec.MDM_APP_MGMT
at android.os.Parcel.readException(Parcel.java:1546)
at android.os.Parcel.readException(Parcel.java:1499)
at android.hardware.usb.IUsbManager$Stub$Proxy.requestDevicePermission(IUsbManager.java:653)
at android.hardware.usb.UsbManager.requestPermission(UsbManager.java:432)
at com.microchip.android.microchipusb.MCP2210.requestUsbPermission(MCP2210.java:161)
at com.microchip.android.mcp2210terminal.MainActivity.onCreate(MainActivity.java:530)

What is going on is that a regular app requires a user permission to access a USB device.  The crash happens when the app requests permission.  According to my brief web search, this problem is specific to the MY phone, with its CURRENT Android version.  So I tried another device, and ran into another problem.

Nexus 4 (LG) on CM13 cannot detect the device

The rooted Nexus 4 did not even see ANY USB device.  With the app USB Host Diagnostics (free on the Play Store), I determined there is a problem with the custom kernel.  I've always known about the Android developers' refusal to support custom ROM, but this is the first time I ran into a problem caused by running a custom ROM.

Since the example app has problems on BOTH my phones, I am somewhat stuck.  I hear that Samsung is rolling out the Android 6.0 update for Galaxy S5, so I will just prototype the control app on Windows until I can get the update.

Google Nexus 5 (LG) detected MCP2210 right away

After wrangling a used Nexus 5 running Android 6.0.1 from a Googler friend, I finally confirmed that Android can connect to the MCP2210.  This means that Android can enumerate a custom USB HID device, and I should be able to enumerate my own device similarly.  From browsing the example MCP2210 terminal Android application example (downloaded from the Microchip), it looks like enumeration and detection callback is all done by Android.  To send some bytes to the device, the Android application ultimately forms a USB OUT request:

public final ByteBuffer sendData(final ByteBuffer data) {
    if (data.capacity() > PACKET_SIZE) { // USB packet size is 64 bytes        return null;
    }
...
    mMcp2210UsbOutRequest.initialize(mMcp2210Connection, mMcp2210EpOut);
    mMcp2210Connection.claimInterface(mMcp2210Interface, true);
    mMcp2210UsbOutRequest.queue(usbCommand, PACKET_SIZE);
...

usbCommand is just a byte buffer: the raw data we want to send, and the OUT request object is not device specific.  Let's back-track to how this interface is initialized

import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;

private UsbDeviceConnection mMcp2210Connection;

/** USB request used for queuing data to the OUT USB endpoint. */private final UsbRequest mMcp2210UsbOutRequest = new UsbRequest();

The endpoint is obtained from the device's interface:

if (mMcp2210Interface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
    for (int j = 0; j < mMcp2210Interface.getEndpointCount(); j++) {
        if (mMcp2210Interface.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_OUT) {
            mMcp2210EpOut = mMcp2210Interface.getEndpoint(j);// OUT usb endpoint found
        } else { // IN usb endpoint found            mMcp2210EpIn = mMcp2210Interface.getEndpoint(j);
        }
    }
    break;
}

Controlling MCP2210 from Windows

I installed the MCP2210 terminal example Windows application downloaded from the Microchip website.  It ran without a problem, and I wrote about my experience in another blog entry.

Can't use APA102

While waiting for the USB-SPI bridge, I found 2 show-stopper problems with APA102.
  1. Talking to a ME colleague, it seems that shoving the ~5x5 mm  APA into the user facing sides cellphone case would be a problem.
  2. According to this reference, APA102 draws ~1 mA even when the light is completely off.  If I have 24 APA102 in a cellphone case, that is ~20 mA.  Compared to the MCP2210 which draws maximum 0.5 mA (2.5 mA if remote wakeup enabled) current draw in USB suspend state, the 20 mA extra current draw blows the USB specification limit of 2.5 mA for device in suspend state.
So I began looking for an efficient and small LED that I can drive directly from an LED driver chip.