2. Talk to me

Published on 16 June 2016

This is a post about pi-zero, usb and explorations.

A series of posts exploring the possibilities of using the Pi Zero as a USB MIDI device. The previous post was "play us a tune" and the next is "Two things at once".

At the end of the last post we had a Pi Zero that would send a MIDI file over the USB port to an attached computer causing an audio program, in this Apple Garageband to play out a tune. The computer also provides power over the same USB cable so it's all plug-and-play.

This post focusses on being able to talk to the Pi Zero over a serial connection and get it to behave as a USB MIDI Gadget.

Swapping behaviours

The main issue is that the USB port can be configured to be either an ethernet cable—so it can be controlled, or a MIDI interface—so it can send MIDI commands but not both.

This led to the merry dance of:

  1. booting in ethernet mode and writing some code
  2. setting the Pi Zero to enter MIDI mode on boot
  3. rebooting
  4. seeing if the MIDI commands were working
  5. setting the Pi Zero to boot into Ethernet mode
  6. rebooting
  7. looking at logs written to disk, fixing whatever was wrong with the program
  8. setting the Pi Zero to enter MIDI mode on boot
  9. rebooting
  10. etc etc

This loop is really frustrating so what we want next is a way to control the Pi Zero and be able to control MIDI messages at the same time.

Talking over serial

The Pi's GPIO pins can be used as a serial interface and in fact, when you boot it up it's sending serial data about the boot sequence already. Using a USB-TTL/UART cable you can log into the Pi over the serial GPIO interface whilst the USB interface is pretending to be a MIDI device.

{{#srcset 'pi-zero-gadget/2-usb-ttl-cable${size}.jpg' sizes="320,768"}}{{/srcset}}
USB on one side, wires for connecting to the GPIO pins on the other

Adafruit sell this one and the one I used looks more like this.

The USB-TTL convertor has the a few wires but only 3 of them need to be connected.

This table shows how:

UARTPi Zero GPIO
GNDGND
TX output pinRXD pin 10 on GPIO header
RX input pinTXD pin 8 on GPIO header

Make sure that TX on the Pi goes to RX on the cable and vice vera. I lost a lot of time wondering why it didn't work because I'd connected TX to TX and RX to RX.

With the cable connected to the Pi Zero's GPIO pins as above and the other end plugged into a USB port on my laptop I used the following terminal command:

screen /dev/tty.SLAB_USBtoUART 115200

At first I saw a blank screen, but hitting enter displayed the familiar login prompt for the Pi. Entering the password will give you the usual terminal access to the Pi.

If the Pi Zero is set to g_midi and the USB cable is plugged in to the computer, running the following will start playing MIDI from the previous post:

aplaymidi --port 16:0 /boot/test.mid

Ctl-C will stop the MIDI playing.

This is quite powerful as it shortens the write-test-debug loop and it lets me see any log messages and make changes as the code is running.

Some useful commands when using screen as Ctl-A d (hold control, press-A, release control, then press d) will detach the terminal from the session but will keep the connection open in the background. screen -r will re-attach to the session. Ctl-A c will close the session completely.

Talking to the outside world

This approach has one major drawback. The Pi Zero can talk to your computer but not the network. The trick of Internet Sharing in the previous post doesn't work because communication is happening over serial and not the virtual ethernet bridge.

There is a way to do it but it took me hours to get it working and felt like travelling back in time to the internet past.

The solution is to use Point-to-Point Protocol, that thing we used to connect modems to our ISP, to bridge the Pi Zero serial connection to the network.

There were some good articles written about this that I used as a starting point:

On the Pi Zero:

These steps need the internet, so I enabled g_ether and connected to the Pi Zero with Internet Sharing.

1. Install PPP

sudo apt-get install ppp -y

2. Create PPP config

I created a config file sudo nano /etc/ppp/options.ttyAMA0 with the contents:

noauth
nocrtscts
xonxoff
passive
local
maxfail 0
defaultroute
persist
nodetach
192.168.0.101:192.168.0.100

This would create a PPP connection giving the Pi Zero the IP 192.168.0.101 and connecting to the laptop which will be 192.168.0.100.

3. Start PPP on boot

cd /etc/systemd/system/
sudo nano rpippp.service

I put the following in rpippp.service:

Description=PPP
[Service]
Type=idle
ExecStart=/usr/sbin/pppd -d /dev/ttyAMA0 1000000
Restart=always
[Install]
WantedBy=multi-user.target
Alias=ppp.service

4. Configure a DNS server

Add the following line in /etc/resolvconf.conf to use Google's public DNS server:

name_servers=8.8.8.8

5. Enable the service

sudo systemctl daemon-reload
sudo systemctl enable rpippp.service

6. Reboot the Pi Zero

That's the Pi Zero configured. pppd would now start on boot and try to connect over serial to another computer.

On the Mac

I'm using a Mac with OS X 10.11 El Capitan. This may be different on other versions.

1. Install drivers

The USB-TTL cable may need specific drivers. You should install these and then make sure that the correct device appears when you plug the cable in.

In my case /dev/tty.SLAB_USBtoUART appears.

2. Enable IP forwarding

On the Mac, you need to get the firewall to let you forward IP packets:

    sudo sysctl -w net.inet.ip.forwarding=1

3. Connect the network interfaces

Create a file at /etc/pf.anchors/customNATRules with the contents:

nat on en0 from 192.168.0.100/24 to any -> (en0)
pass from {lo0, 192.168.0.100/24} to any keep state

This is telling the Mac to pass packets from our end of the PPP connection through to our network connection on en0 which for me was my wifi connection. It might be different if you use an ethernet connection or a different wifi card.

4. Link to the new config

Add to very top of /etc/pf.anchors/com.apple:

nat-anchor "100.customNATRules/*"
rdr-anchor "100.customNATRules/*"
load anchor "100.customNATRules" from "/etc/pf.anchors/customNATRules"

5. Activate the network settings

You restart pfctl to activate the new config:

sudo pfctl -d
sudo pfctl -f /etc/pf.conf
sudo pfctl -e

6. Start PPP on the Mac to connect the Pi Zero's serial connection

/dev/tty.SLAB_USBtoUART is the address on the Mac of the USB to TTL converter cable. Depending on which drivers are installed, this could be different.

Running the following long-winded command will connect the serial connection via the USB-TTL cable and assign the other end the IP address 192.168.0.101.

sudo pppd /dev/tty.SLAB_USBtoUART 115200 192.168.0.100:192.168.0.101 proxyarp local noauth debug nodetach dump nocrtscts xonxoff passive nopersist maxfail 0

On a successful connection you should see something like:

Fri Jun 17 12:09:25 2016 : rcvd [CCP ConfAck id=0x1]

Setting nopersist above will cause pppd to exit when you unplug the USB cable. Set to persist if you want it to stay running and to try to reconnect automatically. Although, if you close the terminal window or restart the Mac then it will exit.

7. SSH into the Pi Zero

I could ssh into the Pi Zero on the static IP I had given it via the PPP link i.e.

ssh pi@192.168.0.101

Now you should be able to ping an external IP from the Pi Zero e.g.

pi@raspberrypi:~ $ ping 212.58.246.78
PING 212.58.246.78 (212.58.246.78) 56(84) bytes of data.
64 bytes from 212.58.246.78: icmp_seq=1 ttl=45 time=82.5 ms
64 bytes from 212.58.246.78: icmp_seq=2 ttl=45 time=100 ms
64 bytes from 212.58.246.78: icmp_seq=3 ttl=46 time=147 ms

That confirms that the Pi Zero has a connection to the outside world.

Next, test the DNS is working by pinging a hostname:

pi@raspberrypi:~ $ ping bbc.co.uk
PING bbc.co.uk (212.58.244.22) 56(84) bytes of data.
64 bytes from 212.58.244.22: icmp_seq=1 ttl=47 time=75.4 ms
64 bytes from 212.58.244.22: icmp_seq=2 ttl=47 time=59.3 ms
64 bytes from 212.58.244.22: icmp_seq=3 ttl=47 time=100 ms

Success 🎉!

Conclusion

{{#srcset 'pi-zero-gadget/2-usb-cables${size}.jpg' sizes="320,768"}}{{/srcset}}
USB-TTL cable connected to the Mac. The blue USB cable is connected to the other Mac USB port.

Although there are quite a few moving parts, having PPP providing a network bridge between the serial port of the Pi Zero and the Mac's network means we can interactively control the Pi Zero whilst it's in gadget mode. This should be really useful for building the types of gadgets that rely on user interaction and are non-obvious in how they behave.

Having another cable isn't ideal though. It would be great if the Pi Zero could behave like several gadgets over the same USB cable at the same time.

Interestingly enough, this blog post suggests that's possible and that'll be the subject for the next post.