Before we get to plotting various system status parameters, let's use a stand alone test to make sure all the essential software pieces are working. We'll also use this example as a way to explain how the code works. If you wanted to adapt this to plot something else, this would be a good starting point.
Here's the code:
# SPDX-FileCopyrightText: 2019 Carter Nelson for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
from collections import deque
import random
import math
# Blinka CircuitPython
import board
import digitalio
import adafruit_rgb_display.ili9341 as ili9341
# Matplotlib
import matplotlib.pyplot as plt
# Python Imaging Library
from PIL import Image
#pylint: disable=bad-continuation
#==| User Config |========================================================
REFRESH_RATE = 0.5
HIST_SIZE = 61
PLOT_CONFIG = (
#--------------------
# PLOT 1 (upper plot)
#--------------------
{
'line_config' : (
{'color' : '#FF0000', 'width' : 2, 'style' : '--'},
{ },
)
},
#--------------------
# PLOT 2 (lower plot)
#--------------------
{
'title' : 'sin()',
'ylim' : (-1.5, 1.5),
'line_config' : (
{'color' : '#00FF00', 'width' : 4},
)
}
)
def update_data():
''' Do whatever to update your data here. General form is:
y_data[plot][line].append(new_data_point)
'''
# upper plot data
for data in y_data[0]:
data.append(random.random())
# lower plot data
y_data[1][0].append(math.sin(0.5 * time.monotonic()))
#==| User Config |========================================================
#pylint: enable=bad-continuation
# Setup X data storage
x_time = [x * REFRESH_RATE for x in range(HIST_SIZE)]
x_time.reverse()
# Setup Y data storage
y_data = [ [deque([None] * HIST_SIZE, maxlen=HIST_SIZE) for _ in plot['line_config']]
for plot in PLOT_CONFIG
]
# Setup display
disp = ili9341.ILI9341(board.SPI(), baudrate = 24000000,
cs = digitalio.DigitalInOut(board.D4),
dc = digitalio.DigitalInOut(board.D5),
rst = digitalio.DigitalInOut(board.D6))
# Setup plot figure
plt.style.use('dark_background')
fig, ax = plt.subplots(2, 1, figsize=(disp.width / 100, disp.height / 100))
# Setup plot axis
ax[0].xaxis.set_ticklabels([])
for plot, a in enumerate(ax):
# add grid to all plots
a.grid(True, linestyle=':')
# limit and invert x time axis
a.set_xlim(min(x_time), max(x_time))
a.invert_xaxis()
# custom settings
if 'title' in PLOT_CONFIG[plot]:
a.set_title(PLOT_CONFIG[plot]['title'], position=(0.5, 0.8))
if 'ylim' in PLOT_CONFIG[plot]:
a.set_ylim(PLOT_CONFIG[plot]['ylim'])
# Setup plot lines
#pylint: disable=redefined-outer-name
plot_lines = []
for plot, config in enumerate(PLOT_CONFIG):
lines = []
for index, line_config in enumerate(config['line_config']):
# create line
line, = ax[plot].plot(x_time, y_data[plot][index])
# custom settings
if 'color' in line_config:
line.set_color(line_config['color'])
if 'width' in line_config:
line.set_linewidth(line_config['width'])
if 'style' in line_config:
line.set_linestyle(line_config['style'])
# add line to list
lines.append(line)
plot_lines.append(lines)
def update_plot():
# update lines with latest data
for plot, lines in enumerate(plot_lines):
for index, line in enumerate(lines):
line.set_ydata(y_data[plot][index])
# autoscale if not specified
if 'ylim' not in PLOT_CONFIG[plot].keys():
ax[plot].relim()
ax[plot].autoscale_view()
# draw the plots
canvas = plt.get_current_fig_manager().canvas
plt.tight_layout()
canvas.draw()
# transfer into PIL image and display
image = Image.frombytes('RGB', canvas.get_width_height(),
canvas.tostring_rgb())
disp.image(image)
print("looping")
while True:
update_data()
update_plot()
time.sleep(REFRESH_RATE)
And then to run it do the following. If you've already set the BLINKA_FT232H environment variable, you don't need to do that again.
$ export BLINKA_FT232H=1 $ python3 tft_sidekick_basic.py
You should get something like this on the display.
The top plot is two lines of random numbers. The bottom line is a sine curve. In the next section we'll go through this code and talk about how it works.
Page last edited January 22, 2025
Text editor powered by tinymce.