UsbSerial SPI support

UsbSerial already supports most of the common USB to serial chips out there but these chips are pretty “serial standard” and as you probably know there are a lot of different serial architectures.

SPI is one of them. Developed by Motorola in the 80’s allows synchronous communication between one master devices and nth number of slaves devices.

A typical SPI configuration with one SPI master and three SPI slaves.

Someone requested support for CP2130 Usb to SPI bridge and, as always I like to emphasize I am always open to support new devices but this one is not as “serial generic” as the others (Windows even doesn’t see it as serial port). It is specifically designed to bridge USB to SPI. SPI interfaces presents different capabilities compared to a simple UART. Clock signal is provided by the master so greater speeds than using simple UARTS are possible and there are nth SSX lines to select different slaves.

Because of these characteristics a new interface specific for SPI was necessary for UsbSerial but I’ve tried to preserve the same philosophy of use that governs UsbSerial. Keep in mind that SPI support is still in beta state and this interface could change due to adding new devices.

  //Common SPI operations
  boolean connectSPI(); // connect SPI device
  void writeMOSI(byte[] buffer); // send data to the selected slave via MOSI. Asynchronous call
  void readMISO(int lengthBuffer); // read data sent from the selected slave via MISO.  Asynchronous call
  void writeRead(byte[] buffer, int lenghtRead); // write and read at the same time. Asynchronous call
  void setClock(int clockDivider); // set clock speed
  void selectSlave(int nSlave); // select slave
  void setMISOCallback(UsbMISOCallback misoCallback); // set MISO callback
  void closeSPI(); // close SPI device

  // Status information
  int getClockDivider(); // get clock divider
  int getSelectedSlave(); // get selected slave
  
  // Miso callback for received data
  interface UsbMISOCallback
  {
      int onReceivedData(byte[] data);
  }

  // CP2130 speed values
  CP2130SpiDevice.CLOCK_12MHz
  CP2130SpiDevice.CLOCK_6MHz
  CP2130SpiDevice.CLOCK_3MHz;
  CP2130SpiDevice.CLOCK_1_5MHz;
  CP2130SpiDevice.CLOCK_750KHz;
  CP2130SpiDevice.CLOCK_375KHz;
  CP2130SpiDevice.CLOCK_187_5KHz;
  CP2130SpiDevice.CLOCK_93_75KHz;

Previous examples of UsbSerial provides a good start keeping in mind the minor changes.

// Define MISO callback
private UsbSpiInterface.UsbMISOCallback misoCallback = new UsbSpiInterface.UsbMISOCallback()
{

  @Override
  public int onReceivedData(byte[] data) {
     // Your code here
  }
};

//...

// Setup SPI communication
UsbSpiDevice spi = UsbSpiDevice.createUsbSerialDevice(device, connection);
if (serialPort != null) {
  boolean ret = spi.connectSPI();
  if(ret){
    spi.selectSlave(0); //select slave 0
    spi.setClock(CP2130SpiDevice.CLOCK_3MHz);
    spi.setMISOCallback(misoCallback);
  }
}

// Write and read operations
spi.writeMOSI("Hola!".getBytes()); // Write "Hola!" to the selected slave through MOSI (MASTER OUTPUT SLAVE INPUT)
spi.readMISO(5); // Read 5 bytes from the MISO (MASTER INPUT SLAVE OUTPUT) line. Data will be received through UsbMISOCallback
spi.writeRead("Hola!".getBytes(), 15); // Write "Hola!" and read 15 bytes synchronously

//Close spi device
spi.closeSPI();

As I previously said, it is still in beta state and only CP2103 chipset is supported. I would really appreciate any feedback you could give me about this feature to further improvements.

Happy hacking!

Advertisement

CH340/341 improved support in UsbSerial 4.2

UsbSerial supports the problematic CH340/341 chipset from 3.0 but the implementation lacked of some necessary features. Finally I managed to get some time to improve CH340 and I gladly announce that CH340/341 support has taken a very long leap!

Current Features
– Broader range of baud rates supported: 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
– Flow control support for RTS/CTS and DTR/DSR.
– All parity modes.

With this new driver the support for the Arduino clones should be better.

More information about UsbSerial

Happy hacking!

Apps and projects using UsbSerial

When I started UsbSerial to fill my own needs I never really expected it would be used for other programmers to their own apps and project but eventually it happened! These are some of the projects where it is used besides DroidTerm and DroidTerm PRO. If you want your project listed here just contact me 🙂

Aprsdroid
APRSdroid is an Android application for Amateur Radio operators that allows to report your position to the APRS (Automatic Packet Reporting System). UsbSerial is used to connect TNC’s (Terminal node controllers) to the Android device. I am not sure if this feature is in release mode but there are interesting developments in the APRSDroid github.


Mouse4All
The guys from BLECentral have developed a project to allow people with severe movement limitations use an Android tablet. I personally saw it and it is an awesome example of how technology can enhance people’s life! UsbSerial is used to connect the special mouse to the tablet.

Akvo Caddisfly, the water quality testing kit
Akvo is a not-for-profit foundation that creates open source, internet and mobile software and sensors. One of their projects is Akvo Caddisfly, an Android application to test water quality in a very low cost way. UsbSerial is used to communicate with the sensors to test water’s conductivity. The app is open-source and its code is hosted on Github

Musethereal

MuseTheReal is an amazing project that shows how science, engineering and fashion can be put together to create something amazing. MuseTheReal showcases the relationship between music and consciousness. This project performs in the MakeFashion gala in Calgary, Canada. A gala where fashion and wearables is combined with fashion. The code is available on Github.

Check out this interview with Angie, Ksenia and the rest of the team!

A DIY Android Auto

This is a great example of what a hacker really is. Dennys from Ukraine is developing a DIY Android Auto for his 2009 Kia Ceed. A great DIY example.

A UsbSerial wrapper for Delphi

A UsbSerial wrapper for the Basic4Android development

UsbSerial now allows using flow control signals RTS/CTS and DTR/DTS!

Finally I’ve managed to get some time to implement hardware flow control in UsbSerial! It is probably the most trickiest part of treating with these chipsets because every single one handles this in a very particular manner.

– CP210x devices check the modem status through a usb control transfer, so I was forced to perform a polling in a new thread every X time to check for changes.
– FTDI devices has a more clever way. Every 40 ms a 2-byte package is sent to the host which contains information about the modem signals and the flag errors.
– PL2303 (not implemented yet) have a USB INT endpoint which it is the obvious candidate where modem status data will be received, no really sure yet though.
– CH340/341 also have a USB INT endpoint but polling is also possible and, because some Android inner bugs, the best way to poll the lines state.

Let’s see how it works with an example

UsbDevice device;
UsbDeviceConnection usbConnection;
...
UsbSerialDevice serial = UsbSerialDevice.createUsbSerialDevice(device, usbConnection);
serial.open();
serial.setBaudRate(115200);
serial.setDataBits(UsbSerialInterface.DATA_BITS_8);
serial.setParity(UsbSerialInterface.PARITY_NONE);
serial.setFlowControl(UsbSerialInterface.FLOW_CONTROL_RTS_CTS);

This is basically the same as the previous versions but now the setFlowControl is meaningful. Now that We have our connection configured to pay attention to the RTS and CTS lines let’s define our callback to receive or status changes.

private UsbSerialInterface.UsbCTSCallback ctsCallback = new UsbSerialInterface.UsbCTSCallback() {
        @Override
        public void onCTSChanged(boolean state) {
            //Your code goes here!
        }
    };

And pass the reference to the UsbSerialDevice object

serial.getCTS(ctsCallback);

Now We know when the status of the line change. I will be also executed in the beginning to know what is the status of the line. If you need to raise the RTS or the DST lines jut write these lines.

serial.setRTS(true); // Raised
serial.setRTS(false); // Not Raised
serial.setDTR(true); // Raised
serial.setDTR(false); // Not Raised

PL2303, CH340/341 and CDC still lack of this feature. If you find something wrong just let me know. Happy crafting! 🙂

DroidTerm 6.5 now supports for Arduino clones using CH340/CH341 chipsets

Last information about the current state of DroidTerm. Please check it out

As title says, I’ve managed to add support in DroidTerm and UsbSerial for those cheap Arduino clones which use the Ch34x family usb to serial chipsets (thanks Paul for providing me with one).
Screenshot_2015-05-09-19-39-32
Besides of that. I fixed two bugs:
– A bug that appeared when a CDC device connection was disconnected and reconnected without unplugging.
– Now DroidTerm keeps logging data (if a profile was created) when the screen is turned off.

I hope DroidTerm is still useful for you guys and I hope I could start to add some new features soon.

Farewell!

A dirty and quick example of serial port communication in Android

Since I released UsbSerial I have received good feedback about its performance but I also have received messages with some legit doubts about how to use it. I finally overcame laziness and I have publish a little example of how to use UsbSerial correctly in a real app. Here it is the source code, just two java source code files 🙂

Disclaimer: If you are looking for a serial terminal for Android, DroidTerm is what you need, this is just a quick example

An overall description of how it works:
– UsbService.java contains a Service to isolate all Usb operations. Interesting things happens there. If you need to change baud rate, stop bits and son those lines are located there. It is a good pattern to implement open connections in a Service so I encourage you to do it this way.

– When App starts, UsbService is created and will try to connect with an attached usb device. if there is one device compatible it will connect with it and data will be able to be sent and received. If no devices are attached, it will inform the user through a toast. The app will be still listening for new usb attached devices.

– Data received will appear in the white box, Send button will send the data wrote into the EditText.

Screenshot_2015-01-09-17-52-48

Although pretty simple and it does not have any particular goal (besides being an explanatory app), it could have some bugs so If you find something just let me know opening an issue on github.

Happy coding!

UsbSerial: A serial port driver library for Android v4.5

Although I talked some months ago about this library and I even use it for DroidTerm, DroidTerm PRO and other professional projects, the post I wrote no reflects the truly current state of this work. It is fairly stable and has been used So here it is, a more formal and helpful description of UsbSerial for Android.

A brief list of Apps and wrappers using UsbSerial. Contact with me if you have a nice project to show 🙂

UsbSerial repository

If UsbSerial fits your needs and have help you with your project, please consider donating via PayPal to boost further improvements

If you are relatively new using Android, or just Usb Android api just checkout this simple app using UsbSerial with full source code available on Github

Or you can check out this amazing post about using the USB android API by the guys of BLECentral. The previous steps to use UsbSerial are very well explained there.

Another awesome tutorial by Hariharan Mathavan from All About Circuits

How to add UsbSerial to your project
Thanks to StephaneBg UsbSerial can be easily added to your Android Studio project via Jitpack. First add the jitpack repo into your project build.gradle.

allprojects {
    repositories {
        jcenter()
        maven { url "https://jitpack.io" }
    }
}

And then add the dependency to your module’s build.gradle

compile 'com.github.felHR85:UsbSerial:4.5'

Devices supported
Currently UsbSerial supports three of the most used USB to serial chipsets:
FTDI FT232 (I am not going to brick your device, trust me 🙂)
Silicon Labs CP210x
Prolific PL2303HX (at least HX version)
CH340/CH341
A new feature added here is a CDC generic driver, so it should be possible to connect devices which fits into Communications Device Class. I am open to suggestions about new supported chipsets.

UsbSerial internals: A brief description
– Internally UsbSerial works as a Producer-Consumer handler,  what you write is put into a buffer and it will be consumed by a Consumer thread when previous data is sent.

– Write operations can be queued from multiple threads without problems

– Received data is received through a callback, there is no need to be polling.

– Two 16kb internal buffers for Write and Read operations.

– Android 4.2.1  or greater devices rely on Asynchronous USB api for read operations. Prior versions (Android 3.1 oldest version supported) use synchronous api due to some Android bugs. Write operations use always synchronous USB api. UsbSerial handles all of this so there is no need to worry.

– PL2303, FT232 and CP210x drivers use a list of known vid and pids to identify a correct device.

– CDC driver can be loaded automatically for a device if it has a CDC interface.

How to use it
First of all you need both UsbDevice and UsbDeviceConnection objects correctly initialized.

// This snippet will open the first usb device connected, excluding usb root hubs
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbDevice device;
UsbDeviceConnection connection;
HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
if(!usbDevices.isEmpty())
{
    boolean keep = true;
    for(Map.Entry<String, UsbDevice> entry : usbDevices.entrySet())
    {
        device = entry.getValue()
        int deviceVID = device.getVendorId()
        int devicePID = device.getProductId()
        if(deviceVID != 0x1d6b || (devicePID != 0x0001 || devicePID != 0x0002 || devicePID != 0x0003))
       {
          // We are supposing here there is only one device connected and it is our serial device
          connection = usbManager.openDevice(device);
          keep = false;
       }else
       {
          connection = null;
          device = null;
       }

       if(!keep)
           break;
    }
}

With those objects correctly initialized it is easy to start


// A callback for received data must be defined
private UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback()
{
    @Override
    public void onReceivedData(byte[] arg0)
    {
        // Code here
    }
};

//...
//...
UsbSerialDevice serialPort = UsbSerialDevice.createUsbSerialDevice(device, mConnection);
if(serialPort != null)
{
    if(serialPort.open())
    {
        // Devices are opened with default values, Usually 9600,8,1,None,OFF
        // CDC driver default values 115200,8,1,None,OFF
        serialPort.setBaudRate(115200);
        serialPort.setDataBits(UsbSerialInterface.DATA_BITS_8);
        serialPort.setStopBits(UsbSerialInterface.STOP_BITS_1);
        serialPort.setParity(UsbSerialInterface.PARITY_NONE);
        serialPort.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
        serialPort.read(mCallback);
    }else
    {
        // Serial port could not be opened, maybe an I/O error or it CDC driver was chosen it does not really fit
    }
}else
{
    // No driver for given device, even generic CDC driver could not be loaded
}

And write what you want to send through serial port!

serialPort.write("Hola!".getBytes());

If you need to use flow control signals just check out this post

UsbSerial now allows USB to SPI bridges

Download the jar file here Actually it is better to add UsbSerial using gradle as described above!!

Happy coding and reach me if you do something nice with UsbSerial! 🙂

Update (03/07/15):
Thanks to Martin Blom now it is possible to use UsbSerial with multi-interface devices (like this). The best way would be

int iface = 0;
UsbSerialDevice serialPort = UsbSerialDevice.createUsbSerialDevice(device, mConnection, iface);