Introduction: Connect the Raspberry Pi to Network Using UART

    In this tutorial I will show you how to connect your Raspberry Pi to the Internet just by using its serial line which is accessible on GPIO. This can be very helpful if there is no other means of connection available like ethernet or wifi dongle. The UART serial port is not directly suitable for TCP/IP traffic so we are going to use a PPP protocol that will encapsulate the serial link and give us an IP interface. Please note that the UART is designed to work in kilobits speed range (115.2kbit by default), that's sufficient for ssh and small files transfers, but it will take ages to transfer large files.

    The operating principle is very simple, but it may be tricky to setup things properly, especially if your communication is limited to UART only. What you'll need:

    • Raspberry PI (any model) with Raspbian
    • USB <-> UART convertor + cable with 3.3v logic levels
    • A linux computer that will act as a second serial link endpoint and a network gateway
    • Optionally a monitor / keyboard / ethernet / wifi that you can plug to your Raspberry

    This method is particularly suitable for the model A as it lacks an ethernet port, but it will work with any Raspberry model. I have the model B rev1.0 :-) The convertor can be of any type, I'm using a cheap convertor with a CP2102 chip. As a second endpoint, I'm using my Ubuntu laptop. You don't need anything else to plug in to your Raspberry if you setup things correctly, but having a monitor + keyboard and ethernet connection will make the setup easier and can greatly help in case something doesn't work as expected. I needed to connect my Raspberry to the network and I had only a monitor, so it was a lot of trial & error for me to get it working.

    Step 1: Identify USB UART Convertor RX / TX Lines

    You'll need to be sure which pins on the converter are the RX / TX serial lines. Don't rely on markings on the convertor PCB as they may not be correct! You can skip this step if you know which lines are TX and RX.

    If you are not sure which line is TX, you can attach a resistor and LED in series to the suspected pin, the LED should blink when you write something to the UART.

    Assuming the /dev/ttyUSB0 is the convertor:

    # This will make a LED attached to the TX blink once
    stty -F /dev/ttyUSB0 9600 raw
    dd if=/dev/zero of=/dev/ttyUSB0 bs=1000 count=1
    

    Once you have identified the TX pin, you can connect it to the suspected RX pin on the converter to make an echo loop. You should be able to receive what you write to it.

    screen /dev/ttyUSB0 115200

    When you type characters to the screen session, the screen should echo them when TX-RX are connected.

    Step 2: Wire Your Computer to the Raspberry Pi

    Connect your USB <-> UART convertor to the Raspberry Pi. You need to connect TX, RX and GND lines. TX stands for "transmitter" and RX stands for "receiver". When connecting two UART devices, you must connect them in a way that TX and RX form a crossover. That means that the output from a TX pin on one device goes to an RX input pin on the other device and vice versa. This is sometimes called a null-modem.

    UART Convertor             Raspberry PI GPIO
    GND ---------------------- GND
    TX output pin ------------ RXD pin 10 on GPIO header
    RX input pin ------------- TXD pin 8  on GPIO header
    

    Make sure that your convertor uses 3.3v logic levels output! The Raspberry uses 3.3v logic on its GPIO and higher voltage may damage the GPIO! If your convertor uses 5v logic levels you must use some logic level convertor.

    Step 3: Test the Connection Using a Console

    By default, the Raspbian uses the built-in UART for linux console access and kernel debugging. Now, we will take advantage of this to test our wiring. Later we will disable this feature and use the UART for networking exclusively.

    The Raspi's UART is accessible via /dev/ttyAMA0 special device.

    First, make sure that the serial console is enabled. In cmdline.txt, you should see something like:

    dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

    The most important part is console=ttyAMA0,115200 this will tell the kernel to assume the UART as a system console.

    Edit the file manually or use a raspi-config (Advanced options) to enable the console on the serial port.

    Second, on your linux computer, type:

    screen /dev/ttyUSB0 115200

    This will connect to the Raspberry Pi console. You should see a login prompt and be able to login. If you don't see anything, try pressing Enter. Also you should see kernel messages while the Raspi boots.

    You can use the the serial console to connect to the Raspberry Pi if you don't have a monitor and a keyboard. Just make sure that the console is enabled in the cmdline.txt

    Once you've checked that the serial connection works, you can proceed to the next step.

    Step 4: Install Necessary Tools

    You will need the 'pppd' tool to manage the connection. It is a part of the 'ppp' package.

    Install the ppp package both to your linux host and the Raspberry Pi:

    sudo apt-get install ppp

    If you don't have an access to the Internet already from your Raspberry Pi (a chicken-egg problem) you can manually download the .deb packages, place them on the sd card or a flash drive and manually install them. Just make sure you download and install all the dependencies.

    sudo dpkg -i xyz.deb

    Step 5: Take Over the Raspberry Pi's UART

    Now, when the serial link reliably works. We can disable the console feature and use the UART exclusively for networking.

    First, remove all ttyAMA0 references from cmdline.txt, refer to the previous step if you don't know how:

    dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

    On next reboot, you'll be able to use ttyAMA0 for whatever you like.

    In our case we will configure the UART for some reasonable behavior and start PPP Daemon to establish a connection over the wire.

    These commands will basically do what we need:

    stty -F /dev/ttyAMA0 raw
    pppd /dev/ttyAMA0 115200 10.0.5.2:10.0.5.1 noauth local debug dump defaultroute nocrtscts

    The IP addresses 10.0.5.2 and 10.0.5.1 denote local:remote addresses being used for the connection. You can chose any IP addresses, just make sure the network range is different from what you are already using. In our case the 10.0.5.2 will be the address of our Pi, the 10.0.5.1 will be address of our linux host.

    As I don't have any access to the RaspberryPi apart from the serial port, I tweaked the pppd parameters a little bit and added them to the /etc/rc.local script so they execute every time the Raspberry boots and the pppd keeps running all the time so I can connect & disconnect the Raspberry at will.

    My rc.local:

    echo "Starting pppd..."
    stty -F /dev/ttyAMA0 raw
    stty -F /dev/ttyAMA0 -a
    pppd /dev/ttyAMA0 115200 10.0.5.2:10.0.5.1 noauth local debug dump defaultroute nocrtscts persist maxfail 0 holdoff 1

    The 'persist maxfail 0 holdoff 1' parameters will make the pppd attempt connect forever.

    When you plug a monitor to your Raspberry, you should see an output of the startup script on the screen.

    Step 6: Run Pppd on Host

    Start the ppp daemon on your computer:

    sudo stty -F /dev/ttyUSB0 raw
    sudo pppd /dev/ttyUSB0 115200 10.0.5.1:10.0.5.2 proxyarp local noauth debug nodetach dump nocrtscts passive persist maxfail 0 holdoff 1

    When pppd is running on both devices, they should handshake and establish a link. The output should like this:

    using channel 10
    Using interface ppp0
    Connect: ppp0 <--> /dev/ttyUSB0
    sent [LCP ConfReq id=0x5 <asyncmap 0x0> <magic 0xb4bad9a9> <pcomp> <accomp>]
    rcvd [LCP ConfReq id=0x2 <asyncmap 0x0> <magic 0x263f80e1> <pcomp> <accomp>]
    sent [LCP ConfAck id=0x2 <asyncmap 0x0> <magic 0x263f80e1> <pcomp> <accomp>]
    rcvd [LCP ConfAck id=0x5 <asyncmap 0x0> <magic 0xb4bad9a9> <pcomp> <accomp>]
    sent [LCP EchoReq id=0x0 magic=0xb4bad9a9]
    sent [CCP ConfReq id=0x4 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
    sent [IPCP ConfReq id=0x4 <compress VJ 0f 01> <addr 10.0.5.1>]
    rcvd [LCP EchoReq id=0x0 magic=0x263f80e1]
    sent [LCP EchoRep id=0x0 magic=0xb4bad9a9]
    rcvd [LCP EchoRep id=0x0 magic=0x263f80e1]
    rcvd [CCP ConfReq id=0x2 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
    sent [CCP ConfAck id=0x2 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
    rcvd [IPCP ConfReq id=0x2 <compress VJ 0f 01> <addr 10.0.5.2>]
    sent [IPCP ConfAck id=0x2 <compress VJ 0f 01> <addr 10.0.5.2>]
    rcvd [CCP ConfAck id=0x4 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
    Deflate (15) compression enabled
    rcvd [IPCP ConfAck id=0x4 <compress VJ 0f 01> <addr 10.0.5.1>]
    Cannot determine ethernet address for proxy ARP
    local  IP address 10.0.5.1
    remote IP address 10.0.5.2
    Script /etc/ppp/ip-up started (pid 3826)
    Script /etc/ppp/ip-up finished (pid 3826), status = 0x0

    Now, from you linux host, you should be able to ping or ssh the RaspberryPi

    ping 10.0.5.2

    If the ping works, you can proceed to the next step.

    Step 7: Setup Routing and NAT

    Once we've established an IP link between our computer and the Raspberry Pi, we can configure the computer to route traffic from the Raspi to the Internet.

    First, enable the traffic forwarding:

    sudo sysctl -w net.ipv4.ip_forward=1

    Next, enable the NAT masquerade to translate Raspi's internal address to the computer's address. Assuming the 'eth0' is an interface on your computer that you use to connect to the outside world:

    sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

    Now, you should be able to ssh to the Raspberry and ping some internet server :-)

    ssh pi@10.0.5.2

    Finished!