Raspberry Pi 3, PiTFT and a modern kernel (bluetooth support!)

Note 2017-02-17: Some slight changes have been made after testing this guide against raspbian 2017-01-11.

Adafruit’s PiTFT is a very neat little addition to the Raspberry Pi. It combines a 2.8″ 320×240 touchscreen (either resistive or capacitive) with 4 buttons hooked up to the Pi’s GPIO. Adafruit even sells a case topper for their standard pi case to fit the screen.

Installation and use of the screen is easily done by following the instructions. The  easy install script takes care of everything and after a reboot the screen works flawlessly.

Unfortunately this will install a fairly old kernel, 4.1, which does not support certain features of the Pi 3. Bluetooth support is one of them and the WiFi driver seems less reliable. There have been multiple forum posts requesting an updated kernel since the Pi 3 was released, but nothing has surfaced yet.

This guide sets up the screen with a standard raspbian install and gets the touchscreen to work for TSLIB by applying a small kernel patch. I’ ll primarily be using the screen with pygame, a simple and light way of getting images up on the screen. pygame relies on TSLIB for it’s touchscreen input, hence the need for the patch.

First of all, my “setup”  is as follows:

  • Raspberry Pi 3
  • Adafruit 2.8″ capacitive PiTFT Plus
  • Adafruit Case and PiTFT case Topper
  • Raspbian jessie lite 2016-05-27 (and 2017-01-11)

Let’s start by everything that’s needed to get the screen to run. I’ll follow up with some more details to what’s what below.

Step by Step

These steps are based on adafruit’s instructions but without using the adafruit kernel. This will probably only work for a  Pi 2 and 3.

Start by having raspbian jessie installed and running on your Pi. Make sure all the software is up to date by running sudo apt-get update && sudo apt-get upgrade

Append the following to /boot/config.txt. If you are using the resistive version use the pitft28-resistive.dtbo overlay instead.


Adafruit’s steps to get the console to show up on boot are still valid. Modify the kernel options by appending fbcon=map:10 fbcon=font:VGA8x8 to /boot/cmdline.txt It should look something like this:

dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait fbcon=map:10 fbcon=font:VGA8x8

After a reboot (not required at this point though) the screen now works! Next is getting the touchscreen to work. This is a bit more complicated and requires a modified kernel to be built.

Start by cloning the raspbian kernel sources and installing required packages:

sudo apt-get install bc git
git clone --depth=1 https://github.com/raspberrypi/linux

Download and apply the ft6236 patchfile:

git clone https://github.com/turboturbonet/ft6236-pitft.git
cd linux
git apply --ignore-space-change --ignore-whitespace ../ft6236-pitft/ft6236.patch

Build and install the modified kernel. Especially the build step will take forever. More information on pi kernel building and also cross compiling can be found here.

make bcm2709_defconfig
make -j4 zImage modules dtbs
sudo make modules_install
sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
sudo scripts/mkknlimg arch/arm/boot/zImage /boot/$KERNEL.img

2016-07-11 21_39_12-pi@raspberrypi_ ~_linux

And reboot to start your fresh kernel:

sudo reboot

As per the adafruit guide, create a udev rule for consistent naming of the device by creating /etc/udev/rules.d/95-ft6236.rules

SUBSYSTEM=="input", ATTRS{name}=="ft6236", ENV{DEVNAME}=="*event*", SYMLINK+="input/touchscreen"

The final step is to set the TSLIB calibration in /etc/pointercal:

320 65536 0 -65536 0 15728640 65536

All that remains is to reload the kernel module and to test the screen!

sudo apt-get install evtest tslib libts-bin 
sudo rmmod ft6236; sudo modprobe ft6236
sudo TSLIB_FBDEVICE=/dev/fb1 TSLIB_TSDEVICE=/dev/input/touchscreen ts_test

ts_test screen

And to be sure that the module is loaded modify /etc/modules and add ft6236 to the list:

# /etc/modules: kernel modules to load at boot time.
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.


dmesg can be used to check if everything is loading:

2016-07-10 20_59_59-pi@raspberrypi_ ~

This shows that both fbtft and ft6236 are loaded and have created their devices.

For decent pygame support libsdl has to be downgraded, here adafruit’s steps can be followed 1:1. Create a file pinsdl.sh and paste in this code:

#enable wheezy package sources
echo "deb http://archive.raspbian.org/raspbian wheezy main
" > /etc/apt/sources.list.d/wheezy.list

#set stable as default package source (currently jessie)
echo "APT::Default-release \"stable\";
" > /etc/apt/apt.conf.d/10defaultRelease

#set the priority for libsdl from wheezy higher then the jessie package
echo "Package: libsdl1.2debian
Pin: release n=jessie
Pin-Priority: -10
Package: libsdl1.2debian
Pin: release n=wheezy
Pin-Priority: 900
" > /etc/apt/preferences.d/libsdl

apt-get update
apt-get -y --force-yes install libsdl1.2debian/wheezy

Execute the file by issuing these commands:

sudo chmod +x pinsdl.sh
sudo ./pindsdl.sh

Your Pi is now ready to use pygame based and touchscreen input!

Detailed Reasoning

Adafruit does supply a set of patches, but the file’s datestamps indicate that the patch was created in 2013. This is even more outdated than adafruits kernel sources. and will not help patching the newer kernel. It does however give some insight in what Adafruit has modified and which modules are used.

Time to do some digging!


In the adafruit kernel the module that creates the framebuffer device /dev/fb1 is called fbtft. This module also exists in the raspbian sources, so it should also support the pitft. Lets dig a little deeper! In the instructions adafruit modifies the /boot/config.txt file to add an overlay and this initializes the module with the right parameters. The exact same overlay does not exist but seems to have been renamed to pitft28-capacitive.dtbo or pitft28-resistive.dtbo. This overlay can be used and the screen works immediately, that was easy! Adafruit’s other tweaks to get the console to show work without any change too.

I think X should also work as the framebuffer device is created similar to the adafruit kernel.


The touchscreen required a little more work. Adafruit’s kernel contains the module ft6x06_ts  but it does not exist in the raspbian sources. However it does contain a module ft6236, might this work? Adafruit’s udev rule does create the /dev/input/touchscreen symlink and running evtest shows incoming coordinates when the screen is touched. So Succes? Unfortunately not, when running a TSLIB test it outputs “Error Message is: selected device is not a touchscreen I understand”. A bit of googling led me to a post on 0xf8 where Pieter Hollants ran into the same problems. He does a great job of identifying why TSLIB does not understand the event output: Basically, TSLIB requires a touch pressure value to function. Unfortunately he does not have a solution for the problem.

This made me dig a little more in the differences between the sources of the two modules. Turns out the ft6x06_ts does not read the actual force data from the controller but just uses a defined constant! The kernel patch implements exactly the same for the newer ft6236 module and bingo! it works!

Loose Ends

Obviously there are some loose ends because of my hardware and because I simply haven’t tried everything yet:

  • PWM Backlight control
  • GPIO Power Switch: Module rpi_power_switch does not exist.
  • I have not tried to get X to run, as I’ll be using Pygame/SDL for the graphics. It will probably work fine without the kernel modifications as these were only needed to get LIBTS to work.

Patch File

From d6d82948a7a06668a72e1e130ae0ac27f8a6ee7a Mon Sep 17 00:00:00 2001
From: Rost <rost@turboturbo.net>
Date: Sun, 10 Jul 2016 03:39:39 +0000
Subject: [PATCH] Added fixed pressure reporting for TSLIB compatability

 drivers/input/touchscreen/ft6236.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/input/touchscreen/ft6236.c b/drivers/input/touchscreen/ft6236.c
index d240d2e..1cf6430 100644
--- a/drivers/input/touchscreen/ft6236.c
+++ b/drivers/input/touchscreen/ft6236.c
@@ -23,6 +23,7 @@
 #include <linux/property.h>

 #define FT6236_MAX_TOUCH_POINTS                2
+#define FT6236_MAX_PRESSURE            0xFF

 #define FT6236_REG_TH_GROUP            0x80
 #define FT6236_REG_PERIODACTIVE                0x88
@@ -38,6 +39,8 @@
 #define FT6236_EVENT_CONTACT           2
 #define FT6236_EVENT_NO_EVENT          3

+#define FT6236_PRESSURE                        0x7F
 struct ft6236_data {
        struct i2c_client *client;
        struct input_dev *input;
@@ -140,6 +143,7 @@ static irqreturn_t ft6236_interrupt(int irq, void *dev_id)
                        input_report_abs(input, ABS_MT_POSITION_X, x);
                        input_report_abs(input, ABS_MT_POSITION_Y, y);
+               input_report_abs(input, ABS_MT_PRESSURE, FT6236_PRESSURE);

@@ -273,6 +277,7 @@ static int ft6236_probe(struct i2c_client *client,
                input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
                                     ft6236->max_y, fuzz_y, 0);
+       input_set_abs_params(input, ABS_MT_PRESSURE,0, FT6236_MAX_PRESSURE,0,0);

        error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
                                    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);