├── README.md
├── doc
├── plot_gif.gif
└── single_joystick.png
├── joystick_readings
└── joystick_readings.ino
└── real_time_plot.py
/README.md:
--------------------------------------------------------------------------------
1 | # Real-time plot of analog joystick readings using pyqtgraph
2 |
3 | In this project, the analog signals of a joystick module are plotted in real-time using the Python's pyqtgraph library. The joystick signals are processed using an Arduino Microcontroller.
4 |
5 | ## Table of contents
6 |
7 | * [Arduino set-up](#arduino-set-up)
8 | * [Real-time plotting using pyqtgraph](#real-time-plotting-using-pyqtgraph)
9 |
10 |
11 | ## Arduino set-up
12 |
13 | The following image shows the Arduino schmetics to work with a commonly available [joystick](https://www.amazon.de/AZDelivery-Joystick-KY-023-Arduino-gratis/dp/B07CKCBHF4/ref=sr_1_1_sspa?ie=UTF8&qid=1538845699&sr=8-1-spons&keywords=joystick+arduino&psc=1) module. These joysticks basically consist of two [potentiometers](https://en.wikipedia.org/wiki/Potentiometer) (for x- and y-axis) as well as a single button (z-direction).
14 |
15 |
16 | The five pins of the joysticks are connected as follows:
17 |
18 | |Arduino schematics|Pin connections|
19 | |--|--|
20 | ||
Pins Joystick | Pins Arduino |
---|
GND | GND |
+5V | 5V |
VRx | A1 |
VRy | A0 |
SW | 11 |
|
21 |
22 | The corresponding Arduino [code](joystick_readings.ino) simply reads the analog values of the joystick and prints them on the serial port to be accessable by Python. In this example, the analog readings are published with 200 Hz. We can now use Python to access the serial port and plot the streaming data in real-time.
23 |
24 | ## Real-time plotting using pyqtgraph
25 |
26 | Although matplolib is the common plotting library for Python and allows creating beautiful graphics, pyqtgraph comes with the possibility to update plots much faster, which allows real-time data plots with high frequencies. The script [real_time_plot.py](real_time_plot.py) creates a real-time plot of the analog output (0-1023) for y over x. Note that we are not plotting single signal pairs but arrays of the last 20 points. The gif below shows how the real-time plot might look like.
27 | 
28 |
29 |
30 | I also created an explaining youtube video on this project. Just click below.
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/doc/plot_gif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JaFeKl/joystick_real_time_plot_with_pyqtgraph/c8e975ad42f444cbc838b41148d9c52942bbfbe7/doc/plot_gif.gif
--------------------------------------------------------------------------------
/doc/single_joystick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JaFeKl/joystick_real_time_plot_with_pyqtgraph/c8e975ad42f444cbc838b41148d9c52942bbfbe7/doc/single_joystick.png
--------------------------------------------------------------------------------
/joystick_readings/joystick_readings.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Simple Joystick analog reading
3 |
4 | This simple arduino code reads the analog x and y values of a Joystick modules
5 | and print them using the serial port
6 |
7 | List of components:
8 | * Arduino board + serial cabel
9 | * Joystick module (e.g. KY-023)
10 | * 5 x Jumper Wires M/F
11 |
12 | Last modified 06/10/18
13 | By Jan-Felix Klein
14 |
15 | For the full project description, please refer to:
16 | https://github.com/JaFeKl/Joystick-analog-readings-real-time-plot-with-pyqtgraph/blob/master/README.md
17 |
18 | */
19 |
20 | const int switchPin = 11; //SW Pin is connected to pin 11
21 | const int yAxisPin = A1; //VRy pin is connected to analog pin A1
22 | const int xAxisPin = A0; //VRx pin is connected to analog pin A0
23 |
24 |
25 | void setup() {
26 | Serial.begin(115200); //Turn on the serial port with desired baud rate
27 | pinMode(switchPin, INPUT); //Set the switchPin to be an input
28 | pinMode(xAxisPin, INPUT); //Set the xAxisPin to be an input
29 | pinMode(yAxisPin, INPUT); //Set the yAxisPin to be an input
30 | digitalWrite(switchPin, HIGH); //write HIGH to switchPin
31 | }
32 |
33 |
34 | void loop() {
35 | // For the purpose of this project, we only need to read the x and y value from the joystick
36 | Serial.print(analogRead(xAxisPin));
37 | Serial.print(",");
38 | Serial.println(analogRead(yAxisPin));
39 |
40 | delay(5); //5 ms delay --> reading with 200Hz
41 | }
42 |
--------------------------------------------------------------------------------
/real_time_plot.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import time
3 | from pyqtgraph.Qt import QtCore, QtGui
4 | import numpy as np
5 | import pyqtgraph as pg
6 | import pyqtgraph.exporters
7 | import serial
8 |
9 |
10 | class joystickPlot(QtGui.QMainWindow):
11 | def __init__(self, parent=None):
12 | super(joystickPlot, self).__init__(parent)
13 |
14 | # Set up GUI configuration
15 | self.mainbox = QtGui.QWidget()
16 | self.setCentralWidget(self.mainbox)
17 | self.mainbox.setLayout(QtGui.QVBoxLayout())
18 |
19 | self.canvas = pg.GraphicsLayoutWidget() # create GrpahicsLayoutWidget obejct
20 | self.mainbox.layout().addWidget(self.canvas)
21 |
22 | self.label = QtGui.QLabel() # placeholder Qlabel object to display framerate
23 | self.mainbox.layout().addWidget(self.label)
24 |
25 |
26 | # Set up plot
27 | self.analogPlot = self.canvas.addPlot(title='Real-time joystick analog signal plot')
28 | self.analogPlot.setYRange(-100,1123) # set axis range
29 | self.analogPlot.setXRange(-100,1123)
30 | self.analogPlot.showGrid(x=True, y=True, alpha=0.5)
31 | x_axis = self.analogPlot.getAxis('bottom')
32 | y_axis = self.analogPlot.getAxis('left')
33 |
34 | x_axis.setLabel(text='x-axis reading') # set axis labels
35 | y_axis.setLabel(text='y-axis reading')
36 |
37 | ticks = [[(0,'0'),(1023,'1023')],[(200,'200'),(400,'400'),(600,'600'),(800,'800')]]
38 | x_axis.setTicks(ticks) # set ticks manually (first major-ticks than minor-ticks)
39 | y_axis.setTicks(ticks)
40 |
41 | self.drawplot = self.analogPlot.plot(pen='y') # yellow line plot
42 |
43 |
44 | # initialize sensor data variables
45 | self.numPoints = 20 # number of points that should be plottet at the same time stamp
46 | self.x = np.array([], dtype=int) # create empty numpy array to store xAxis data
47 | self.y = np.array([], dtype=int) # create empty numpy array to store yAxis data
48 |
49 |
50 | # initialize frame counter variables
51 | self.counter = 0
52 | self.fps = 0.
53 | self.lastupdate = time.time()
54 |
55 |
56 | # set up image exporter (necessary to be able to export images)
57 | QtGui.QApplication.processEvents()
58 | self.exporter=pg.exporters.ImageExporter(self.canvas.scene())
59 | self.image_counter = 1
60 |
61 |
62 | # start updating
63 | self._update()
64 |
65 |
66 |
67 | def _framerate(self):
68 | now = time.time() # current time since the epoch in seconds
69 | dt = (now-self.lastupdate)
70 | if dt <= 0:
71 | dt = 0.000000000001
72 | fps2 = 1.0 / dt
73 | self.lastupdate = now
74 | self.fps = self.fps * 0.9 + fps2 * 0.1
75 | tx = 'Mean Frame Rate: {fps:.3f} FPS'.format(fps=self.fps )
76 | self.label.setText(tx)
77 | QtCore.QTimer.singleShot(1, self._update)
78 | self.counter += 1
79 |
80 |
81 |
82 | def _save_image(self):
83 | filename = 'img'+("%04d" % self.image_counter)+'.png'
84 | self.exporter.export(filename)
85 | self.image_counter += 1
86 |
87 |
88 | def _update(self):
89 | while (arduinoData.inWaiting()==0): #wait until there is data available
90 | pass # do nothing
91 | arduinoString = arduinoData.readline() #read the text line from serial port
92 | if sys.version_info >= (3, 0):
93 | arduinoString = arduinoString.decode('utf-8', 'backslashreplace')
94 | dataArray = arduinoString.split(',') #split it into an array
95 |
96 | xAxis = int(dataArray[1]) #convert first element to an integer
97 | yAxis = 1023 - int(dataArray[0]) #convert and flip yAxis signal
98 |
99 | self.x = np.append(self.x, xAxis) # append new data point to the array
100 | if self.x.size >= self.numPoints: # make sure that the size of the array includes the most recent data points
101 | self.x = np.append(self.x[1:self.numPoints],xAxis)
102 | else:
103 | self.x = np.append(self.x,xAxis)
104 |
105 | self.y = np.append(self.y, yAxis) # append new data point to the array
106 | if self.y.size >= self.numPoints: # make sure that the size of the array includes the most recent data points
107 | self.y = np.append(self.y[1:self.numPoints],yAxis)
108 | else:
109 | self.y = np.append(self.y,yAxis)
110 |
111 | self.drawplot.setData(self.x, self.y) # draw current data set
112 |
113 | self._framerate() # update framerate, see corresponding function
114 |
115 | # self._save_image() # uncomment this to save each frame as an .png image in your current directory. Note that the framerate drops significantly by doing so
116 |
117 |
118 |
119 | if __name__ == '__main__':
120 |
121 | arduinoData = serial.Serial("com3", 115200); # Creating our serial object named arduinoData, make sure that port and baud rate is set up according to your arduino data stream
122 | app = QtGui.QApplication(sys.argv)
123 | plot = joystickPlot()
124 | plot.show()
125 | sys.exit(app.exec_())
126 |
--------------------------------------------------------------------------------