Friday, May 15, 2015

USB Passthrough in KVM/libvirt 2: Problems and workarounds on Arduino development in a vm client

We will talk a bit about setting up an Arduino development environment. Originally I thought this was going to be a short article, but that was not the case. The more I worked on this the longer it became. So I got annoyed and put the part about setting USB passthrough and then dealing with waking up a vm client that lost its USB device after it was saved in their own articles. I want to have this article as focused on building an Arduino development environment in a vm guest as possible; we already talked about USB passthrough in general and even accessed a UPS as example.

Last time I did setup an Arduino development environment I used my laptop and my Arduino. That worked fine but required me to have my Arduino with me and the laptop just in case I decided to do something; which I did: I carried the board, cables, and even a USB drive with useful stuff in a little box in my backpack at all times. With time that got old, I now prefer to do remote development as much as I can get away with, so let's see if I can do it with the Arduino.

The Arduino boards I will be using here are the Funduino Uno/YourDuinoRoboRED and a Duemilanove.

The scientific reason I am using those boards is because that is what I have; I bought both of them with my own money. The first one I ever got was the Duemilanove; it is in fact the one I used to carry in my bag. Recently (as in last month) I bought the Funduino board. I decided to start this article by choosing this red board because it comes with a miniUSB port and included a USB cable just the right size so it can be precariously hung from vmhost. Maybe also because it is more colourful; you'll be the judge.

We will be adding an Arduino device to the vm client desktop, which is an Ubuntu Desktop vm in the vmhost called, for reasons the go beyond the topic of this article, vmhost, which runs KVM with libvirt.

Setting the mess up

The title of this article mentions USB passthrough. Main difference between that and PCI passthrough is that the USB one allows hotplugging. And that means we should be able to add the Arduino board to desktop while this vm client is running. At least that is the hope.

Without further ado, let's get busy.

  1. We probably should start this by connecting the arduino board to on of vmhost's USB port. The picture on the right shows the actual board hanging in front of the actual vmhost.
  2. As we did in an earlier article on USB passthrough, we should ask if vmhost knows about the Arduino board:
    [raub@vmhost ~]$ lsusb
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
    Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 006 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
    Bus 002 Device 003: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
    Bus 001 Device 003: ID 2341:0001 Arduino SA Uno (CDC ACM)
    [raub@vmhost ~]$
    The line we are interested in is
    Bus 001 Device 003: ID 2341:0001 Arduino SA Uno (CDC ACM)
    Note that it identifies itself as an Arduino Uno; I do not know if the real Arduino Uno also identifies itself the same way. Also note that
    <vendor id='0x2341' />
    <product id='0x0001' />
  3. With that info we can now tell desktop about the Arduino board. As we have seen before, we can do it live by creating a xml config file containing info about the Arduino board:
    cat > arduino.xml << 'EOF'
        <hostdev mode='subsystem' type='usb' managed='yes'>
          <source>
            <vendor id='0x2341'/>
            <product id='0x0001'/>
          </source>
        </hostdev>
    EOF
  4. Attach it
    [root@vmhost tmp]# virsh attach-device desktop ./arduino.xml
    Device attached successfully
    
    [root@vmhost tmp]#
  5. Check if the vm client reports it as attached
    raub@desktop:~$ lsusb
    Bus 001 Device 004: ID 2341:0001 Arduino SA Uno (CDC ACM)
    Bus 001 Device 003: ID 0409:55aa NEC Corp. Hub
    Bus 001 Device 002: ID 0627:0001 Adomax Technology Co., Ltd
    Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
    raub@desktop:~$
    Don't forget since we are accessing this board as a normal user, we need to be in the group that owns that device. I know that its USB port is shown as /dev/ttyACM0, so we need to find who owns it.
    raub@desktop:~/Downloadz/Arduino$ ls -l /dev/ttyACM0
    crw-rw---- 1 root dialout 166, 0 May  8 09:56 /dev/ttyACM0
    raub@desktop:~/Downloadz/Arduino$ id
    uid=1000(raub) gid=1000(raub) groups=1000(raub),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),107(lpadmin),124(sambashare),127(debian-tor)
    raub@desktop:~/Downloadz/Arduino$
  6. Get and install the Arduino SDK. Since I am doing all of this in a Linux vm, installing the SDK is really uncompressing the file, arduino-1.6.4-linux64.tar.xz at the time I wrote this article, into where you want to run the SDK from; I put it in ~/bin.
    tar xJvf arduino-1.6.4-linux64.tar.xz
  7. And run and configure the SDK. I connected to the vm running the SDK from my laptop, telling ssh to do X11 port forwarding:
    ssh -X desktop
    Once there, I started the Arduino SDK
    ./bin/arduino-1.6.4/arduino
    which popped the usual GUI on my laptop screen thanks to the magic of X Windows. Now, do tell it that you are connected to an Arduino Uno board through port /dev/ttyACM0.
  8. Now we need to test things up. I think one of the best programs to test an Arduino SDK setup is blink, which controls the little LED that a lot of boards have built-in. Here is the original code if you are too lazy to look it up:
    /*
      Blink
      Turns on an LED on for one second, then off for one second, repeatedly.
    
      This example code is in the public domain.
     */
    
    // Pin 13 has an LED connected on most Arduino boards.
    // give it a name:
    int led = 13;
    
    // the setup routine runs once when you press reset:
    void setup() {
      // initialize the digital pin as an output.
      pinMode(led, OUTPUT);
    }
    
    // the loop routine runs over and over again forever:
    void loop() {
      digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
      delay(1000);               // wait for a second
      digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
      delay(1000);               // wait for a second
    }
    Upload the file and see if it blinks the way you told it to do so (1s on, 1s off).
And all seems to be nice an peachy; we should end this article here and pat ourselves on the back. But, this is where things start to go wrong.

Things Did Not Happen According to the Plan

  1. Let's say I want to change the blink frequency in the blink program. Maybe I want to have the LE stay on 2s and off 1s. That part of the code would change like this:
    void loop() {
      digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
      delay(2000);               // wait for a second
      digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
      delay(1000);               // wait for a second
    }
    Great! now, let's upload the new code and stare at the LED.

    And no changes.

    We then look at the GUI and find the following messages:

    Sketch uses 1,068 bytes (3%) of program storage space. Maximum is 32,256 bytes.
    Global variables use 11 bytes (0%) of dynamic memory, leaving 2,037 bytes for local variables. Maximum is 2,048 bytes.
    avrdude: stk500_recv(): programmer is not responding
    avrdude: stk500_getsync() attempt 7 of 10: not in sync: resp=0x00
    avrdude: stk500_recv(): programmer is not responding
    avrdude: stk500_getsync() attempt 8 of 10: not in sync: resp=0x00
    avrdude: stk500_recv(): programmer is not responding
    avrdude: stk500_getsync() attempt 9 of 10: not in sync: resp=0x00
    avrdude: stk500_recv(): programmer is not responding
    avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x00
    Problem uploading to board.  See http://www.arduino.cc/en/Guide/Troubleshooting#upload for suggestions.
    Sometimes doing virsh detach-device and then reattaching it will work, but other times it does not, which leads to...
  2. The Funduino board likes to disappear.
    [root@vmhost tmp]# virsh attach-device desktop arduino.xml
    error: Failed to attach device from arduino.xml
    error: internal error Did not find USB device 2341:1
    
    [root@vmhost tmp]#  lsusb
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 006 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
    Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
    Bus 002 Device 003: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
    [root@vmhost tmp]#
    By that I mean I still have not found what triggers that. Sometimes it is connected to vmhost for 3 days doing nothing but always showing on lsusb even if it is not attached to a vm client. And then, it is gone. Just like that. Only workaround I found was to physically unplug the Funduino out of vmhost and then plug it back in.

What happens if you use the Duemilanove?

I have to say I have not been able to replicate any of the above issues with the Duemilanove. It uses a different USB-to-Serial chip, so it is seen by lsusb as

Bus 001 Device 012: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC
I do not know if the chipset makes a difference or there is something in my Funduino board that is boink (maybe the chipset is more picky?). I have another Funduino board, but not an Arduino Uno, so I can only test so much. What I can say is as of now I will use the Duemilanove to do remote development, but when I want to take a board in the field -- say, attach to my car -- I will grab the Funduino one. After all it works fine if I just install new code once.

Update on the Funduino/RoboRED: Success!

So I got home last night, took my second Funduino out of its shrinkwrap, and tried it. It works as well as the Duemilanove:

  1. If I disconnect it physically from vmhost, desktop gracefully reports it as gone. virsh dumpxml still reports an entry
    [root@vmhost tmp]# virsh dumpxml desktop|grep 2341
            
            
    [root@vmhost tmp]# 
    but that could be how KVM does things.
  2. If I unload it using
    [root@vmhost tmp]# virsh detach-device desktop arduino.xml                      
    Device attached successfully
    
    [root@vmhost tmp]#
    it does get removed from desktop's config file. If there is more than one entry, it might take a bit more time but it will come out.
  3. If we did not physically remove the Arduino device as above, instead just using virsh detach-device, it will be removed from destkop's config and running lsusb in that vm client will no longer report it.
  4. Once lsusb in vmhost shows the Arduino board back (say, you physically connected it back to that machine),
    [root@vmhost tmp]# virsh attach-device desktop arduino.xml                      
    Device attached successfully
    
    [root@vmhost tmp]#
    will once again make the device available for desktop.
Since the problem is on the first one; I will see about getting it replaced.

No comments: