Raspberry Pi 3B (+), Dual Monitors

Moved under Archived because the newer Raspberry Pi 4B responded to this need with direct hardware support for connecting two HDMI monitors. Large LCD/LED backlit displays have also become much cheaper so you probably don’t need more than two displays for one user. I upgraded to the 4B model for my desktop and kept my 3B+ for other purposes.

Most linux distributions, including Raspbian,  now come with a DisplayLink  driver, so I will be using my HP T100 zero client which contains a DL-125  USB 2.0 to VGA chip (supporting max resolutions of 1280×1024 or 1440×990 wide screen) to drive my secondary monitor. More recently I purchased a used HP NL571AA/AN2464 adapter which contains a DL-165 that supports 1920×1080 and this works well too.

You should plug this USB device cable into the Raspberry Pi before booting. If you plug it in after booting, the USB keyboard and mouse may stop responding. You can fix this by removing and then reinserting the keyboard and mouse cable (or their USB wireless receiver).

At this stage when you boot, your USB connected secondary monitor will initialise as a solid green screen indicating that it is working. You can see your new hardware with a variety of commands.

pi@raspberrypi:~ $ ls /dev/fb*
/dev/fb0 /dev/fb1
pi@raspberrypi:~ $
pi@raspberrypi:~ $ lsusb
Bus 001 Device 008: ID 0d8c:013a C-Media Electronics, Inc.
Bus 001 Device 006: ID 17e9:410e DisplayLink
Bus 001 Device 004: ID 03f0:0032 Hewlett-Packard
Bus 001 Device 005: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 001 Device 007: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
pi@raspberrypi:~ $
pi@raspberrypi:~ $ loginctl seat-status
Sessions: *c1
│ usb:usb1
│ └─/sys/devices/platform/soc/3f980000.usb/usb1/1-1
│ usb:1-1
│ ├─/sys/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.1
│ │ usb:1-1.1
│ │ ├─/sys/devic…C52B.0003/0003:046D:1024.0004/input/input0
│ │ │ input:input0 "Logitech M310"
│ │ └─/sys/devic…C52B.0003/0003:046D:2011.0005/input/input1
│ │ input:input1 "Logitech K520"
│ └─/sys/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3
│ usb:1-1.3
│ ├─/sys/devic…1.3.1:1.1/0003:17E9:410E.0006/input/input2
│ │ input:input2 "DisplayLink HP T150 Dock"
│ ├─/sys/devic…1.3.1:1.2/0003:17E9:410E.0007/input/input3
│ │ input:input3 "DisplayLink HP T150 Dock"
│ ├─/sys/devic…00.usb/usb1/1-1/1-1.3/1-1.3.1/graphics/fb1
│ │ [MASTER] graphics:fb1 "udlfb"
│ ├─/sys/devic…/1-1/1-1.3/1-1.3.2/1-1.3.2:1.0/sound/card1
│ │ sound:card1 "Device"
│ └─/sys/devic…1.3.2:1.3/0003:0D8C:013A.0008/input/input4
│ input:input4 "C-Media Electronics Inc…vice "
│ sound:card0 "ALSA"
[MASTER] graphics:fb0 "BCM2708 FB"
pi@raspberrypi:~ $

By default, the Raspbian full install under Noobs  or BerryBoot does not have support for Xinerama which is required for extending the display across multiple monitors so that application windows can be dragged from monitor to monitor. Fortunately that support is available for installation.

pi@raspberrypi:~ $ sudo apt-get install libxinerama-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
libpthread-stubs0-dev libx11-dev libx11-doc libxau-dev libxcb1-dev
libxdmcp-dev libxext-dev x11proto-core-dev x11proto-input-dev
x11proto-kb-dev x11proto-xext-dev x11proto-xinerama-dev xorg-sgml-doctools
Suggested packages:
libxcb-doc libxext-doc
The following NEW packages will be installed:
libpthread-stubs0-dev libx11-dev libx11-doc libxau-dev libxcb1-dev
libxdmcp-dev libxext-dev libxinerama-dev x11proto-core-dev
x11proto-input-dev x11proto-kb-dev x11proto-xext-dev x11proto-xinerama-dev
xorg-sgml-doctools xtrans-dev
0 upgraded, 15 newly installed, 0 to remove and 148 not upgraded.
Need to get 4,768 kB of archives.
After this operation, 18.9 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf libpthread-stubs0-dev armhf 0.3-4 [4,042 B]
Get:2 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf xorg-sgml-doctools all 1:1.11-1 [21.9 kB]
Get:3 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf x11proto-core-dev all 7.0.31-1 [728 kB]
Get:4 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf libxau-dev armhf 1:1.0.8-1 [23.0 kB]
Get:5 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf libxdmcp-dev armhf 1:1.1.2-3 [40.9 kB]
Get:6 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf x11proto-input-dev all 2.3.2-1 [158 kB]
Get:7 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf x11proto-kb-dev all 1.0.7-1 [233 kB]
Get:8 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf xtrans-dev all 1.3.5-1 [100 kB]
Get:9 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf libxcb1-dev armhf 1.12-1 [165 kB]
Get:10 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf libx11-dev armhf 2:1.6.4-3+deb9u1 [753 kB]
Get:11 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf libx11-doc all 2:1.6.4-3+deb9u1 [2,201 kB]
Get:12 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf x11proto-xext-dev all 7.3.0-1 [212 kB]
Get:13 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf libxext-dev armhf 2:1.3.3-1 [102 kB]
Get:14 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf x11proto-xinerama-dev all 1.2.1-2 [4,938 B]
Get:15 http://mirror.web-ster.com/raspbian/raspbian stretch/main armhf libxinerama-dev armhf 2:1.1.3-1+b1 [18.7 kB]
Fetched 4,768 kB in 10s (462 kB/s)
Selecting previously unselected package libpthread-stubs0-dev:armhf.
(Reading database ... 144873 files and directories currently installed.)
Preparing to unpack .../00-libpthread-stubs0-dev_0.3-4_armhf.deb ...
Unpacking libpthread-stubs0-dev:armhf (0.3-4) ...
Selecting previously unselected package xorg-sgml-doctools.
Preparing to unpack .../01-xorg-sgml-doctools_1%3a1.11-1_all.deb ...
Unpacking xorg-sgml-doctools (1:1.11-1) ...
Selecting previously unselected package x11proto-core-dev.
Preparing to unpack .../02-x11proto-core-dev_7.0.31-1_all.deb ...
Unpacking x11proto-core-dev (7.0.31-1) ...
Selecting previously unselected package libxau-dev:armhf.
Preparing to unpack .../03-libxau-dev_1%3a1.0.8-1_armhf.deb ...
Unpacking libxau-dev:armhf (1:1.0.8-1) ...
Selecting previously unselected package libxdmcp-dev:armhf.
Preparing to unpack .../04-libxdmcp-dev_1%3a1.1.2-3_armhf.deb ...
Unpacking libxdmcp-dev:armhf (1:1.1.2-3) ...
Selecting previously unselected package x11proto-input-dev.
Preparing to unpack .../05-x11proto-input-dev_2.3.2-1_all.deb ...
Unpacking x11proto-input-dev (2.3.2-1) ...
Selecting previously unselected package x11proto-kb-dev.
Preparing to unpack .../06-x11proto-kb-dev_1.0.7-1_all.deb ...
Unpacking x11proto-kb-dev (1.0.7-1) ...
Selecting previously unselected package xtrans-dev.
Preparing to unpack .../07-xtrans-dev_1.3.5-1_all.deb ...
Unpacking xtrans-dev (1.3.5-1) ...
Selecting previously unselected package libxcb1-dev:armhf.
Preparing to unpack .../08-libxcb1-dev_1.12-1_armhf.deb ...
Unpacking libxcb1-dev:armhf (1.12-1) ...
Selecting previously unselected package libx11-dev:armhf.
Preparing to unpack .../09-libx11-dev_2%3a1.6.4-3+deb9u1_armhf.deb ...
Unpacking libx11-dev:armhf (2:1.6.4-3+deb9u1) ...
Selecting previously unselected package libx11-doc.
Preparing to unpack .../10-libx11-doc_2%3a1.6.4-3+deb9u1_all.deb ...
Unpacking libx11-doc (2:1.6.4-3+deb9u1) ...
Selecting previously unselected package x11proto-xext-dev.
Preparing to unpack .../11-x11proto-xext-dev_7.3.0-1_all.deb ...
Unpacking x11proto-xext-dev (7.3.0-1) ...
Selecting previously unselected package libxext-dev:armhf.
Preparing to unpack .../12-libxext-dev_2%3a1.3.3-1_armhf.deb ...
Unpacking libxext-dev:armhf (2:1.3.3-1) ...
Selecting previously unselected package x11proto-xinerama-dev.
Preparing to unpack .../13-x11proto-xinerama-dev_1.2.1-2_all.deb ...
Unpacking x11proto-xinerama-dev (1.2.1-2) ...
Selecting previously unselected package libxinerama-dev:armhf.
Preparing to unpack .../14-libxinerama-dev_2%3a1.1.3-1+b1_armhf.deb ...
Unpacking libxinerama-dev:armhf (2:1.1.3-1+b1) ...
Setting up libpthread-stubs0-dev:armhf (0.3-4) ...
Setting up xorg-sgml-doctools (1:1.11-1) ...
Setting up x11proto-kb-dev (1.0.7-1) ...
Processing triggers for sgml-base (1.29) ...
Setting up xtrans-dev (1.3.5-1) ...
Setting up x11proto-xinerama-dev (1.2.1-2) ...
Setting up libx11-doc (2:1.6.4-3+deb9u1) ...
Processing triggers for man-db ( ...
Setting up x11proto-core-dev (7.0.31-1) ...
Setting up libxau-dev:armhf (1:1.0.8-1) ...
Setting up libxdmcp-dev:armhf (1:1.1.2-3) ...
Setting up libxcb1-dev:armhf (1.12-1) ...
Setting up x11proto-input-dev (2.3.2-1) ...
Setting up x11proto-xext-dev (7.3.0-1) ...
Setting up libx11-dev:armhf (2:1.6.4-3+deb9u1) ...
Setting up libxext-dev:armhf (2:1.3.3-1) ...
Setting up libxinerama-dev:armhf (2:1.1.3-1+b1) ...
pi@raspberrypi:~ $

A requirement for use of the Xinerama option is that all video devices be configured for the same framebuffer depth. It is important to solve this before configuring an /etc/X11/xorg.conf file as failure to do so will cause the system to hang during the next boot. Let’s see what we have.

pi@raspberrypi:~ $ fbset -v -i
Linux Frame Buffer Device Configuration Version 2.1 (23/06/1999)
(C) Copyright 1995-1999 by Geert Uytterhoeven

Opening frame buffer device `/dev/fb0'
Using current video mode from `/dev/fb0'

mode "1920x1080"
geometry 1920 1080 1920 1080 32
timings 0 0 0 0 0 0 0
rgba 8/16,8/8,8/0,8/24

Getting further frame buffer information
Frame buffer device information:
Name : BCM2708 FB
Address : 0x3e402000
Size : 8294400
XPanStep : 1
YPanStep : 1
YWrapStep : 0
LineLength : 7680
Accelerator : No
pi@raspberrypi:~ $

And for displaylink framebuffer:

pi@raspberrypi:~ $ fbset -i -v -fb /dev/fb1
Linux Frame Buffer Device Configuration Version 2.1 (23/06/1999)
(C) Copyright 1995-1999 by Geert Uytterhoeven

Opening frame buffer device `/dev/fb1'
Using current video mode from `/dev/fb1'

mode "1280x1024-60"
# D: 108.003 MHz, H: 63.983 kHz, V: 60.021 Hz
geometry 1280 1024 1280 1024 16
timings 9259 248 48 38 1 112 3
hsync high
vsync high
rgba 5/11,6/5,5/0,0/0

Getting further frame buffer information
Frame buffer device information:
Name : udlfb
Address : 0xbe1a2000
Size : 2621440
XPanStep : 0
YPanStep : 0
YWrapStep : 0
LineLength : 2560
Accelerator : No
pi@raspberrypi:~ $

Note the output lines I displayed in red. The raspberry pi video currently has a framebuffer pixel depth of 32, while the DisplayLink video has a pixel depth of 16. The xorg.conf file we will be creating has a documented way to change the depth. The Raspberry Pi can also change the depth by an option in its config.txt. With both Noobs and BerryBoot and each OS you can install, there are multiple config.txt so we have to pay attention to inheritance. I haven’t tried all combinations, but it appears that the last config.txt loaded will replace earlier ones as well as overriding the different potential method in xorg.conf. For Rasbian, it would be safest to set the framebuffer depth in its /boot/config.txt by addling the line to the “[all]” section so it will work for any Pi hardware model you insert this microSD card into and won’t be inherited by any other OS you may have added.


Reboot and again run to check result:

pi@raspberrypi:~ $ fbset -i |grep geometry
    geometry 1920 1080 1920 1080 16
pi@raspberrypi:~ $ 

Good, now to create an /etc/X11/xorg.conf file to tell the xserver what to do with this new hardware and framebuffer (you can use any Identifier names  providing you are consistent). There is a minimal version at /usr/share/X11/xorg.conf.d/99-fbturbo.conf for the integrated video which our new /etc/X11/xorg.conf will override.

Section "Device"
Identifier "Raspberry Pi BCM2708"
Driver "fbturbo"
Option "fbdev" "/dev/fb0"
Option "ShadowFB" "off"

Section "Device"
Identifier "displaylink"
Driver "fbturbo"
Option "fbdev" "/dev/fb1"
Option "ShadowFB" "off"

Section "Monitor"
Identifier "HP-23"

Section "Monitor"
Identifier "Dell-19"

Section "Screen"
Identifier "screen0"
Device "Raspberry Pi BCM2708"
Monitor "HP-23"

Section "Screen"
Identifier "screen1"
Device "displaylink"
Monitor "Dell-19"

Section "ServerLayout"
Identifier "default"
Screen 0 "screen0" 0 0
Screen 1 "screen1" RightOf "screen0"
Option "Xinerama" "on"

Here is a picture of Raspian running on Raspberry Pi 3B+  with dual monitors .  On the primary, left hand,  monitor I am using GIMP with its floating tool menus to edit a photo. On the secondary, USB, monitor on the right, I have Chrome browser opened to the GIMP help topics.

Now, the “print screen” key will capture both screens in a single png file. If, as in my case, the secondary screen is of a different height (1024 vs 1080) than the primary screen, the screenshot will show a black horizontal area band for that difference in height (1080-1024 = 56).

Works with UbuntuMATE 16.04 too.

One of my motives for dual displays on the Raspberry Pi was that x2goclient works well on it for remotely viewing/operating  other computers. I wanted x2goclient to open full screen on the displaylink monitor. It has a setting which allows you to choose which display to place a windowed session to display and control a remote computer. You can have multiple concurrent instances of x2goclient and configure them to individually use different monitors on your Raspberry Pi. From a user perspective this is a little like both a multi-seat zero client and a fat client in that you can simultaneously run applications on a remote powerful computer while running less demanding applications locally. The remote computer running x2goserver can accept simultaneous connections from multiple users on different raspbian workstations (like multi-seat zero clients) but without the extra workload of applications that can be run locally (like a fat client) on these same raspbian workstations and without having to setup LTSP or Pinet for PXE loading of thinclients.