The Raspberry Pi 3 (and Raspberry Pi Zero!) have support for both serial port connectivity and bluetooth. For this particular project, I wanted to use the Raspberry Pi to scan for low energy bluetooth beacons, and send this information to a third party device via a serial port interface. At first I wasn’t sure if this was possible with the Pi, given that the serial port and bluetooth interfaces touch the GPIO subsystem, and I definitely needed a physical connection to the Pi3. As with all projects that involve hardware interfaces and linux drivers, it was tough to tell if this was going to be a 4 hour project or a 40 hour project.

Fortunately this is pretty well documented in bits and pieces around the internet, and the Raspberry Pi community is world class. As it turns out this is actually a relatively supported configuration once you figure out exactly what needs to be turned on and moved around in linux land. Basically a best case scenario.

By default, the serial port has a login console running so there is some config updates needed to get the serial port working over GPIO. As it turns out, this interferes with the default bluetooth configuration because they both use the same GPIO interfaces. There’s more to it than that, but that’s the gist of it.

Fortunately, you can have your cake and eat it too. Once the serial port is set up correctly, you just have to switch the overlay drivers to use the mini-uart port (which essentially is a second serial port interface with some limitations).

For my purposes, this works out perfectly for both the Raspberry Pi 3 AND the Raspberry Pi zero. Literally the same image that I set up for the Pi3 can be plugged into the PiZero, too.

The following is a brief summary of the final steps I needed to take to get it working, without all the extra crap and dead ends that came up along the way.

(Reference on Stack Overflow)


First, make sure everything is up to date (apt-get update, apt-get upgrade)

Set up the serial port with sudo raspi-config. Turn on serial port, turn off console login

Ensure that /boot/cmdline.txt has no ttyAMA0 cat /boot/cmdline.txt

It should look something like this:

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

Not like this:

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

Disable serial-getty if its still running (it wasnt for me: double check with ps ax | grep serial-getty)

sudo systemctl mask serial-getty@ttyAMA0.service

Ensure UART pins 15,16 are in state ALT0. I think this was the problem for me. They were not in this state originally.

sudo apt-get install wiringpi
gpio readall

In this case 15, 16, were not in the right mode. Run the following to set them into ALT0 mode:

gpio mode 15 ALT0
gpio mode 16 ALT0

Now it looks good:

pi@raspi:~ $ gpio readall
 +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 1 | ALT0 | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | ALT0 | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | IN   | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+

Set enable_uart=1 in /boot/config.txt. Just to be safe, disable bluetooth too:

enable_uart=1
dtoverlay=pi3-disable-bt

Reboot, then you should be able to connect via minicom -s using /dev/ttyAMA0 or /dev/serial0

To turn on bluetooth, go back into /boot/config.txt and use the pi3-miniuart-bt overlay driver instead of the pi3-disable-bt driver. It may be optional, but recommended to lock the core_freq to 250 to prevent any side effects associated with baud rate and dynamic clock speeds.

This seems to do it:

enable_uart=1
dtoverlay=pi3-miniuart-bt
core_freq=250

Now reboot. You should still be able to see your device over the serial port with minicom, and the following should also work:

sudo hcitool lescan --duplicates

I had really good luck using the python library bluepy for beacon scanning.