Don’t Have an HDMI Display For Your Raspberry Pi? Wio Terminal LCD Got You Covered With a USB HMI!

Introduction

When you get your hands on a Raspberry Pi for the very first time, you might want to know how to actually log in to the Raspberry Pi so that you can start interacting with it. In my previous blog, I have already talked about a couple of methods of logging into a Raspberry Pi such as using a CP2102 USB to Serial Converter and using a Seeeduino XIAO.

However, in both the methods, we logged into the Raspberry Pi via a command-line interface. We could interact with the Raspberry Pi through Linux commands, but there was no actual GUI (Graphical User Interface). The only way to interact with a Raspberry Pi via a GUI is by connecting it to an external display via the HDMI connection.

Wio Terminal LCD as Raspberry Pi GUI!

Now I would like to explain a whole new approach to accessing the GUI on the Raspberry Pi without connecting to an external display, and that is by using a Wio Terminal.

Wio Terminal is an ATSAMD51-based microcontroller with wireless connectivity supported by Realtek RTL8720DN and is equipped with a 2.4” LCD Screen, onboard accelerometer (LIS3DHTR), Microphone, Buzzer, micro-SD card slot, Light sensor, and Infrared Emitter(IR 940nm). It is also compatible with both Arduino and MicroPython.

We could use the LCD on the Wio Terminal to display the Raspberry GUI just by connecting it to one of the USB ports on the Raspberry Pi. This becomes the Wio Terminal LCD, an HMI (Human Machine Interface) USB display for the Raspberry Pi. Furthermore, if you have more than one Wio Terminal, you could connect up to 4 Wio Terminals in total at the same time to the 4 USB ports available on the Raspberry Pi and set them to work in different modes such as Extend mode/ Mirror Mode. I will discuss this later in this blog. I also want to mention that this method not only works for Raspberry Pi, but also works for Nvidia Jetson Nano, BeagleBone, and Odyssey X86J4105 as well!

Wio Terminal Setup

Now, let’s dive into actually making the HMI display on the Wio Terminal to work! I will guide you through how to set up the Wio Terminal for the process.

If this is the first time you are using the Wio Terminal, it is better if you visit this page and do the initial setup for the Wio Terminal. After doing so, you can continue to the steps below.

You need to upload an Arduino program to your Wio Terminal and there are 2 methods of doing this.

Method 1 – Arduino IDE upload method

Step 1:
Visit the Seeed_Arduino_USBDISP library repo here.

Step 2:
Go to “code” and select “Download ZIP”

Step 3:
Open Arduino IDE, go to “Sketch > Include Library > Add .ZIP Library” and select the .zip file you just downloaded

Step 4:
Go to “File > Examples > Seeed_Arduino_USBDISP” and select the example you prefer out of the two.

  • If you want a higher screen refresh rate on Wio Terminal, upload NullFunctional example to Wio Terminal.
  • If you want Wio Terminal to also act as a USB Mouse, upload USBDisplayAndMouseControl example to Wio Terminal. Here you can use the 5-way switch and the 3 buttons on the WIo Terminal to act as a mouse.

Method 2 – flash uf2 method

There is a more convenient method and that is flashing the uf2 firmware files including the examples to the Wio Terminal.

Step 1:
Download the uf2 files of:

Step 2:
Enter bootloader mode on the Wio Terminal by sliding the power switch twice quickly.

Step 3:
An external drive named Arduino should appear on your PC. Drag the downloaded uf2 files to the Arduino drive.

Now the Wio Terminal is all set up. It’s time to set up the Raspberry Pi as well.

Raspberry Pi Setup

If this is the first time you are using the Raspberry Pi, it is better if you visit this page and do the initial setup for the Raspberry Pi. After doing so, you can continue to the steps below.

Step 1:
After you have configured Putty or any other SSH client to log in to the Raspberry Pi, open the command line interface inside the Raspberry Pi.

Step 2:
Download package information from all configured sources, by running the following command

sudo apt-get update

Step 3:
Install the necessary packages such as kernel headers, kernels, build-essential, dkms by running the following command in terminal:

sudo apt-get install raspberrypi-kernel-headers raspberrypi-kernel build-essential dkms

Step 4:
Download the display driver on Raspberry Pi

cd ~
git clone https://github.com/Seeed-Studio/seeed-linux-usbdisp

Step 5:
Make and build the driver

cd ~/seeed-linux-usbdisp/drivers/linux-driver
make & sudo make install
sudo reboot

Step 6:
Move the config files to the system location

sudo cp ~/seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/10-disp.conf /usr/share/X11/xorg.conf.d/

Note:
There are different screen configurations that you can choose from, the default is one Wio Terminal as one screen.

Step 7:
Restart the service

sudo service lightdm restart

Step 8:
Connect the Wio Terminal to Raspberry Pi’s USB port and the Raspberry Pi Desktop GUI will magically appear on the Wio Terminal LCD!

Note: If nothing shows on the display, please follow the steps below

Run the following command in terminal:

sudo raspi-config

Once the Raspberry Pi Software Configuration Tool opens, navigate to the following location

Display Options > Resolution

Select a different resolution except “default”

Note:
This version of the USB display driver does not support hot swapping

How about Jetson Nano, Beaglebone, and Odyssey X86?

I will also walk you through how to setup NVIDIA Jetson Nano, Beaglebone and Odyssey X86 so that the Wio Terminal can act as a display for them!

Step 1:
For Nvidia Jetson Nano and Odyssey X86J4105 (Ubuntu), install the Linux driver as followed, run the following in the terminal:

sudo apt install --reinstall linux-headers-$(uname -r)

Note:
For Odyssey X86J4105, we have only tested for ubuntu and debian OS. Other Linux OS may not work.

Step 2:
Download the display driver

cd ~
git clone https://github.com/Seeed-Studio/seeed-linux-usbdisp

Step 3:
Make and build the driver

cd ~/seeed-linux-usbdisp/drivers/linux-driver
make & sudo make install
sudo reboot

Step 4:
Move the config files to the system location

sudo cp ~/seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/10-disp.conf /usr/share/X11/xorg.conf.d/

Step 5:
Restart the service

sudo service lightdm restart

Step 6:
Connect the Wio Terminal to Jetson Nano, Beaglebone, or Odyssey X86 and you will see the respective GUI displayed on the Wio Terminal LCD!

Got more Wio Terminals? Let’s utilize them all for displays!

If you have multiple Wio Terminals, you could use them to display content with different working modes. For example, when a Raspberry pi is used and if you have 4 Wio Terminals, you could connect all 4 Wio Terminals to the 4 USB ports on the Raspberry Pi and configure the Wio Terminals to display the content in 4 different configurations.

You can do simply do this by editing the 10-disp.conf file.

Setup 1

# Four-screen expansion
# pattern: 1  2  
#        : 3  4

This is the 10-disp-1.conf under seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/, simply copy this file to /usr/share/X11/xorg.conf.d/ and name it 10-disp.conf:

sudo cp ~/seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/10-disp-1.conf /usr/share/X11/xorg.conf.d/10-disp.conf

Setup 2

# Four-screen expansion
# pattern: 1
#        : 2  3  4

This is the 10-disp-2.conf under seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/. simply copy this file to /usr/share/X11/xorg.conf.d/ and name it 10-disp.conf:

sudo cp ~/seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/10-disp-2.conf /usr/share/X11/xorg.conf.d/10-disp.conf

Setup 3

# Four-screen expansion
# pattern: 1  2  3  4

This is the 10-disp-3.conf under seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/. simply copy this file to /usr/share/X11/xorg.conf.d/ and name it 10-disp.conf:

sudo cp ~/seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/10-disp-3.conf /usr/share/X11/xorg.conf.d/10-disp.conf

Setup 4

# Four-screen clone display

This is the 10-disp-4.conf under seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/. simply copy this file to /usr/share/X11/xorg.conf.d/ and name it 10-disp.conf:

sudo cp ~/seeed-linux-usbdisp/drivers/linux-driver/xserver_conf/10-disp-4.conf

Run Python scripts on Raspberry Pi to display GUI on Wio Terminal LCD

You can not only display the Raspberry Pi Desktop GUI on the Wio Terminal LCD, but also can run Python scripts on Raspberry Pi command line which is accessed from PC and display graphics on the Wio Terminal LCD by using a graphics library.

PyQt5

One of the common graphics libraries is PyQt. You can use this library, program a Python code and run on the Raspberry Pi to display interactive graphics on the Wio Terminal LCD. Here we will be using the latest version of PyQt which is PyQt5.

You can install PyQt on the Raspberry Pi by following the steps below

Step 1:
Install dependencies for pyqt5

sudo apt update
sudo apt install python3 python3-distutils python3-pyqt5 

Step 2:
Export macros for the screen

export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb1

Note:
Where fb1 is the first screen, fb2 is second, fb3 is third and so on. You may use echo $QT_QPA_PLATFORM to check the macros.

Basic Example

Here I will show you the basic usage of the PyQt5 library by displaying 3 buttons on the Wio Terminal LCD.

Step 1:
Copy the following codes and make a Python file called buttons.py. Then move this file to the Raspberry Pi

import sys

from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QWidget

app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle('QHBoxLayout')
layout = QHBoxLayout()
layout.addWidget(QPushButton('Left'))
layout.addWidget(QPushButton('Center'))
layout.addWidget(QPushButton('Right'))
window.setLayout(layout)
window.show()
sys.exit(app.exec_())

Step 2:
After that, run this file

python3 buttons.py

You will see 3 buttons displayed on the Wio Terminal LCD

Tetris on PyQt5

You can run the Tetris game on the Raspberry Pi, display on the Wio Terminal and control the blocks using a connected keyboard. This example is made by Jan Bodnar and all credits go to him.

Step 1:
To use this demo, copy the following code to a Python file, name it as tetris.py.

#!/usr/bin/python3

"""
ZetCode PyQt5 tutorial

This is a Tetris game clone.

Author: Jan Bodnar
Website: zetcode.com
"""

import random
import sys

from PyQt5.QtCore import Qt, QBasicTimer, pyqtSignal
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import QMainWindow, QFrame, QDesktopWidget, QApplication


class Tetris(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        """initiates application UI"""

        self.tboard = Board(self)
        self.setCentralWidget(self.tboard)

        self.statusbar = self.statusBar()
        self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)

        self.tboard.start()

        self.resize(180, 380)
        self.center()
        self.setWindowTitle('Tetris')
        self.show()

    def center(self):
        """centers the window on the screen"""

        screen = QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move(int((screen.width() - size.width()) / 2),
                  int((screen.height() - size.height()) / 2))


class Board(QFrame):
    msg2Statusbar = pyqtSignal(str)

    BoardWidth = 10
    BoardHeight = 22
    Speed = 300

    def __init__(self, parent):
        super().__init__(parent)

        self.initBoard()

    def initBoard(self):
        """initiates board"""

        self.timer = QBasicTimer()
        self.isWaitingAfterLine = False

        self.curX = 0
        self.curY = 0
        self.numLinesRemoved = 0
        self.board = []

        self.setFocusPolicy(Qt.StrongFocus)
        self.isStarted = False
        self.isPaused = False
        self.clearBoard()

    def shapeAt(self, x, y):
        """determines shape at the board position"""

        return self.board[(y * Board.BoardWidth) + x]

    def setShapeAt(self, x, y, shape):
        """sets a shape at the board"""

        self.board[(y * Board.BoardWidth) + x] = shape

    def squareWidth(self):
        """returns the width of one square"""

        return self.contentsRect().width() // Board.BoardWidth

    def squareHeight(self):
        """returns the height of one square"""

        return self.contentsRect().height() // Board.BoardHeight

    def start(self):
        """starts game"""

        if self.isPaused:
            return

        self.isStarted = True
        self.isWaitingAfterLine = False
        self.numLinesRemoved = 0
        self.clearBoard()

        self.msg2Statusbar.emit(str(self.numLinesRemoved))

        self.newPiece()
        self.timer.start(Board.Speed, self)

    def pause(self):
        """pauses game"""

        if not self.isStarted:
            return

        self.isPaused = not self.isPaused

        if self.isPaused:
            self.timer.stop()
            self.msg2Statusbar.emit("paused")

        else:
            self.timer.start(Board.Speed, self)
            self.msg2Statusbar.emit(str(self.numLinesRemoved))

        self.update()

    def paintEvent(self, event):
        """paints all shapes of the game"""

        painter = QPainter(self)
        rect = self.contentsRect()

        boardTop = rect.bottom() - Board.BoardHeight * self.squareHeight()

        for i in range(Board.BoardHeight):
            for j in range(Board.BoardWidth):
                shape = self.shapeAt(j, Board.BoardHeight - i - 1)

                if shape != Tetrominoe.NoShape:
                    self.drawSquare(painter,
                                    rect.left() + j * self.squareWidth(),
                                    boardTop + i * self.squareHeight(), shape)

        if self.curPiece.shape() != Tetrominoe.NoShape:

            for i in range(4):
                x = self.curX + self.curPiece.x(i)
                y = self.curY - self.curPiece.y(i)
                self.drawSquare(painter, rect.left() + x * self.squareWidth(),
                                boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
                                self.curPiece.shape())

    def keyPressEvent(self, event):
        """processes key press events"""

        if not self.isStarted or self.curPiece.shape() == Tetrominoe.NoShape:
            super(Board, self).keyPressEvent(event)
            return

        key = event.key()

        if key == Qt.Key_P:
            self.pause()
            return

        if self.isPaused:
            return

        elif key == Qt.Key_Left:
            self.tryMove(self.curPiece, self.curX - 1, self.curY)

        elif key == Qt.Key_Right:
            self.tryMove(self.curPiece, self.curX + 1, self.curY)

        elif key == Qt.Key_Down:
            self.tryMove(self.curPiece.rotateRight(), self.curX, self.curY)

        elif key == Qt.Key_Up:
            self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY)

        elif key == Qt.Key_Space:
            self.dropDown()

        elif key == Qt.Key_D:
            self.oneLineDown()

        else:
            super(Board, self).keyPressEvent(event)

    def timerEvent(self, event):
        """handles timer event"""

        if event.timerId() == self.timer.timerId():

            if self.isWaitingAfterLine:
                self.isWaitingAfterLine = False
                self.newPiece()
            else:
                self.oneLineDown()

        else:
            super(Board, self).timerEvent(event)

    def clearBoard(self):
        """clears shapes from the board"""

        for i in range(Board.BoardHeight * Board.BoardWidth):
            self.board.append(Tetrominoe.NoShape)

    def dropDown(self):
        """drops down a shape"""

        newY = self.curY

        while newY > 0:

            if not self.tryMove(self.curPiece, self.curX, newY - 1):
                break

            newY -= 1

        self.pieceDropped()

    def oneLineDown(self):
        """goes one line down with a shape"""

        if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
            self.pieceDropped()

    def pieceDropped(self):
        """after dropping shape, remove full lines and create new shape"""

        for i in range(4):
            x = self.curX + self.curPiece.x(i)
            y = self.curY - self.curPiece.y(i)
            self.setShapeAt(x, y, self.curPiece.shape())

        self.removeFullLines()

        if not self.isWaitingAfterLine:
            self.newPiece()

    def removeFullLines(self):
        """removes all full lines from the board"""

        numFullLines = 0
        rowsToRemove = []

        for i in range(Board.BoardHeight):

            n = 0
            for j in range(Board.BoardWidth):
                if not self.shapeAt(j, i) == Tetrominoe.NoShape:
                    n = n + 1

            if n == 10:
                rowsToRemove.append(i)

        rowsToRemove.reverse()

        for m in rowsToRemove:

            for k in range(m, Board.BoardHeight):
                for l in range(Board.BoardWidth):
                    self.setShapeAt(l, k, self.shapeAt(l, k + 1))

        numFullLines = numFullLines + len(rowsToRemove)

        if numFullLines > 0:
            self.numLinesRemoved = self.numLinesRemoved + numFullLines
            self.msg2Statusbar.emit(str(self.numLinesRemoved))

            self.isWaitingAfterLine = True
            self.curPiece.setShape(Tetrominoe.NoShape)
            self.update()

    def newPiece(self):
        """creates a new shape"""

        self.curPiece = Shape()
        self.curPiece.setRandomShape()
        self.curX = Board.BoardWidth // 2 + 1
        self.curY = Board.BoardHeight - 1 + self.curPiece.minY()

        if not self.tryMove(self.curPiece, self.curX, self.curY):
            self.curPiece.setShape(Tetrominoe.NoShape)
            self.timer.stop()
            self.isStarted = False
            self.msg2Statusbar.emit("Game over")

    def tryMove(self, newPiece, newX, newY):
        """tries to move a shape"""

        for i in range(4):

            x = newX + newPiece.x(i)
            y = newY - newPiece.y(i)

            if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
                return False

            if self.shapeAt(x, y) != Tetrominoe.NoShape:
                return False

        self.curPiece = newPiece
        self.curX = newX
        self.curY = newY
        self.update()

        return True

    def drawSquare(self, painter, x, y, shape):
        """draws a square of a shape"""

        colorTable = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
                      0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]

        color = QColor(colorTable[shape])
        painter.fillRect(x + 1, y + 1, self.squareWidth() - 2,
                         self.squareHeight() - 2, color)

        painter.setPen(color.lighter())
        painter.drawLine(x, y + self.squareHeight() - 1, x, y)
        painter.drawLine(x, y, x + self.squareWidth() - 1, y)

        painter.setPen(color.darker())
        painter.drawLine(x + 1, y + self.squareHeight() - 1,
                         x + self.squareWidth() - 1, y + self.squareHeight() - 1)
        painter.drawLine(x + self.squareWidth() - 1,
                         y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1)


class Tetrominoe(object):
    NoShape = 0
    ZShape = 1
    SShape = 2
    LineShape = 3
    TShape = 4
    SquareShape = 5
    LShape = 6
    MirroredLShape = 7


class Shape(object):
    coordsTable = (
        ((0, 0), (0, 0), (0, 0), (0, 0)),
        ((0, -1), (0, 0), (-1, 0), (-1, 1)),
        ((0, -1), (0, 0), (1, 0), (1, 1)),
        ((0, -1), (0, 0), (0, 1), (0, 2)),
        ((-1, 0), (0, 0), (1, 0), (0, 1)),
        ((0, 0), (1, 0), (0, 1), (1, 1)),
        ((-1, -1), (0, -1), (0, 0), (0, 1)),
        ((1, -1), (0, -1), (0, 0), (0, 1))
    )

    def __init__(self):

        self.coords = [[0, 0] for i in range(4)]
        self.pieceShape = Tetrominoe.NoShape

        self.setShape(Tetrominoe.NoShape)

    def shape(self):
        """returns shape"""

        return self.pieceShape

    def setShape(self, shape):
        """sets a shape"""

        table = Shape.coordsTable[shape]

        for i in range(4):
            for j in range(2):
                self.coords[i][j] = table[i][j]

        self.pieceShape = shape

    def setRandomShape(self):
        """chooses a random shape"""

        self.setShape(random.randint(1, 7))

    def x(self, index):
        """returns x coordinate"""

        return self.coords[index][0]

    def y(self, index):
        """returns y coordinate"""

        return self.coords[index][1]

    def setX(self, index, x):
        """sets x coordinate"""

        self.coords[index][0] = x

    def setY(self, index, y):
        """sets y coordinate"""

        self.coords[index][1] = y

    def minX(self):
        """returns min x value"""

        m = self.coords[0][0]
        for i in range(4):
            m = min(m, self.coords[i][0])

        return m

    def maxX(self):
        """returns max x value"""

        m = self.coords[0][0]
        for i in range(4):
            m = max(m, self.coords[i][0])

        return m

    def minY(self):
        """returns min y value"""

        m = self.coords[0][1]
        for i in range(4):
            m = min(m, self.coords[i][1])

        return m

    def maxY(self):
        """returns max y value"""

        m = self.coords[0][1]
        for i in range(4):
            m = max(m, self.coords[i][1])

        return m

    def rotateLeft(self):
        """rotates shape to the left"""

        if self.pieceShape == Tetrominoe.SquareShape:
            return self

        result = Shape()
        result.pieceShape = self.pieceShape

        for i in range(4):
            result.setX(i, self.y(i))
            result.setY(i, -self.x(i))

        return result

    def rotateRight(self):
        """rotates shape to the right"""

        if self.pieceShape == Tetrominoe.SquareShape:
            return self

        result = Shape()
        result.pieceShape = self.pieceShape

        for i in range(4):
            result.setX(i, -self.y(i))
            result.setY(i, self.x(i))

        return result


def main():

    app = QApplication([])
    tetris = Tetris()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Step 2:
After that, run this file

python3 tetris.py

Now you can play Tetris, displayed on the Wio Terminal LCD by using the keyboard arrow keys and the spacebar.

Scientific Graphics using PtQtGraph

PyQtGraph is a pure-python graphics and GUI library built on PyQt4 / PySide and numpy. It is intended for use in mathematics / scientific / engineering applications.

Let’s look at an example of displaying a scientific graph.

Step 1:
Install dependencies for PyQtGraph

sudo apt update
sudo apt install python3 python3-distutils python3-pyqt5 python3-pip python3-numpy -y
sudo pip3 install pyqtgraph

Step 2:
Copy the following code to a Python file, name it as graph.py

# -*- coding: utf-8 -*-
"""
Demonstrates common image analysis tools.
Many of the features demonstrated here are already provided by the ImageView
widget, but here we present a lower-level approach that provides finer control
over the user interface.
"""
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
 
 
# Interpret image data as row-major instead of col-major
pg.setConfigOptions(imageAxisOrder='row-major')
 
pg.mkQApp()
win = pg.GraphicsLayoutWidget()
win.setWindowTitle('pyqtgraph example: Image Analysis')
 
# A plot area (ViewBox + axes) for displaying the image
p1 = win.addPlot(title="")
 
# Item for displaying image data
img = pg.ImageItem()
p1.addItem(img)
 
# Custom ROI for selecting an image region
roi = pg.ROI([-8, 14], [6, 5])
roi.addScaleHandle([0.5, 1], [0.5, 0.5])
roi.addScaleHandle([0, 0.5], [0.5, 0.5])
p1.addItem(roi)
roi.setZValue(10)  # make sure ROI is drawn above image
 
# Isocurve drawing
iso = pg.IsocurveItem(level=0.8, pen='g')
iso.setParentItem(img)
iso.setZValue(5)
 
# Contrast/color control
hist = pg.HistogramLUTItem()
hist.setImageItem(img)
win.addItem(hist)
 
# Draggable line for setting isocurve level
isoLine = pg.InfiniteLine(angle=0, movable=True, pen='g')
hist.vb.addItem(isoLine)
hist.vb.setMouseEnabled(y=False) # makes user interaction a little easier
isoLine.setValue(0.8)
isoLine.setZValue(1000) # bring iso line above contrast controls
 
# Another plot area for displaying ROI data
win.nextRow()
p2 = win.addPlot(colspan=2)
p2.setMaximumHeight(250)
win.resize(800, 800)
win.show()
 
 
# Generate image data
data = np.random.normal(size=(200, 100))
data[20:80, 20:80] += 2.
data = pg.gaussianFilter(data, (3, 3))
data += np.random.normal(size=(200, 100)) * 0.1
img.setImage(data)
hist.setLevels(data.min(), data.max())
 
# build isocurves from smoothed data
iso.setData(pg.gaussianFilter(data, (2, 2)))
 
# set position and scale of image
img.scale(0.2, 0.2)
img.translate(-50, 0)
 
# zoom to fit imageo
p1.autoRange()  
 
 
# Callbacks for handling user interaction
def updatePlot():
    global img, roi, data, p2
    selected = roi.getArrayRegion(data, img)
    p2.plot(selected.mean(axis=0), clear=True)
 
roi.sigRegionChanged.connect(updatePlot)
updatePlot()
 
def updateIsocurve():
    global isoLine, iso
    iso.setLevel(isoLine.value())
 
isoLine.sigDragged.connect(updateIsocurve)
 
def imageHoverEvent(event):
    """Show the position, pixel, and value under the mouse cursor.
    """
    if event.isExit():
        p1.setTitle("")
        return
    pos = event.pos()
    i, j = pos.y(), pos.x()
    i = int(np.clip(i, 0, data.shape[0] - 1))
    j = int(np.clip(j, 0, data.shape[1] - 1))
    val = data[i, j]
    ppos = img.mapToParent(pos)
    x, y = ppos.x(), ppos.y()
    p1.setTitle("pos: (%0.1f, %0.1f)  pixel: (%d, %d)  value: %g" % (x, y, i, j, val))
 
# Monkey-patch the image to use our custom hover function. 
# This is generally discouraged (you should subclass ImageItem instead),
# but it works for a very simple use like this. 
img.hoverEvent = imageHoverEvent
 
 
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

Step 3:
After that, run this file

python3 graph.py

Now you will see different scientific graphs displayed on the Wio Terminal!

If you experienced that Mouse is crashing with PyQtGraph’s script, you can simply disable raspberry pi’s desktop using sudo raspi-config -> Boot options -> Desktop/CLI -> Console Autologin.

You can also run different PyQtGraph scripts on different screens as follows

Usermod SDK

Apart from displaying your Raspberry Pi Desktop GUI on the Wio Terminal LCD, you could display your PC on the Wio Terminal LCD as well. Click here to check the wiki for more information.

Conclusion

That’s all about it for this blog. I hope you clearly understood how to setup the Wio Terminal so that you could use it as a display with your Raspberry Pi, Jetson Nano, BeagleBone, and ODYSSEY-86! Now you can start implementing it in your next projects!

About Author

Calendar

November 2020
M T W T F S S
 1
2345678
9101112131415
16171819202122
23242526272829
30