From 15c8c47de6558d9567df014290119646212ba574 Mon Sep 17 00:00:00 2001 From: Kevin McAleer Date: Fri, 17 Jun 2022 23:05:20 +0100 Subject: [PATCH] renamed chart to pichart, added card and container --- README.md | 2 + card_demo.py | 122 +++++++++++++++++++ chart.py | 317 ------------------------------------------------- dashboard.py | 10 +- dashboard2.py | 2 +- dashboard3.py | 14 +-- dashboard4.py | 13 +- dashboard5.py | 159 +++++++++++++++++++++++++ dashboard6.py | 170 ++++++++++++++++++++++++++ pichart.py | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 796 insertions(+), 336 deletions(-) create mode 100644 card_demo.py delete mode 100644 chart.py create mode 100644 dashboard5.py create mode 100644 dashboard6.py create mode 100644 pichart.py diff --git a/README.md b/README.md index d697bc5..933d2e8 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,5 @@ A tiny dashboard solution for displaying data from an MQTT server ## Notes - This uses the [Pimoroni Pico Display Pack](https://shop.pimoroni.com/products/pico-display-pack?variant=32368664215635) or [Pimoroni Pico Display 2.0"](https://shop.pimoroni.com/products/pico-display-pack-2-0?variant=39374122582099), which use ST7789 display driver. - I'm using the Pimoroni 'batteries included' MicroPython build so if you get a st7789 not found, this is why. Grab that from the Pimoroni website. + +I've also renamed the library from chart.py to pichart.py diff --git a/card_demo.py b/card_demo.py new file mode 100644 index 0000000..f45b994 --- /dev/null +++ b/card_demo.py @@ -0,0 +1,122 @@ +# Pico Dashboard4 - for smaller displays +# Kevin McAleer +# June 2022 + +# import picodisplay2 as display +from pichart import Chart, Card +from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2 +display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2) +# from st7789 import ST7789 +import gc +import machine +from time import sleep +from random import randint +# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 +# WIDTH, HEIGHT = 240, 135 # Pico Display Pack +WIDTH, HEIGHT = display.get_bounds() + +# display = ST7789(WIDTH, HEIGHT, rotate180=False) + +gc.collect() + +# Set the display backlight to 50% +display.set_backlight(1.0) + +data = [10,11,9,13,2,10,12,10,10,9,7,11,10,11,9,13,2,10,12,10,10,9,7,11] + +# Set the theme colour +THEME = {'red': 255, 'green': 171, 'blue': 57} +THEME2 = {'red': 252, 'green': 193, 'blue': 109} +THEME3 = {'red': 151, 'green': 250, 'blue': 121} +WHITE = {'red': 255, 'green': 255, 'blue': 255} +LIGHT_GREY = {'red': 240, 'green': 240, 'blue': 240} +BLUE = {'red': 20, 'green': 155, 'blue': 206} +LIGHT_BLUE = {'red': 55, 'green': 170, 'blue': 213} +GREEN = {'red': 0, 'green': 220, 'blue': 0} +ORANGE = {'red': 255, 'green': 171, 'blue': 57} +LIGHT_ORANGE = {'red': 255, 'green': 179, 'blue': 94} + +# Frame background colour +BACKGROUND = {'red': 255, 'green': 171, 'blue': 57} + +border = 2 +# frame(border,border,width-(border*2),height-(border*2),BACKGROUND) + +card1 = Card(display, title='Hello World') +card1.margin = border + +card1.width = WIDTH //2 +card1.height = HEIGHT //2 + +card1.x = WIDTH //2 - card1.width //2 +card1.y = HEIGHT // 2 - card1.height //2 + +card1.background_colour = WHITE +card1.title_colour = ORANGE + +# These loops adjust the scale of the text until it fits on the screen +card1.text_scale = 20 + +text_colour = display.create_pen(ORANGE['red'], ORANGE['green'], ORANGE['blue']) + +while True: + display.set_font("bitmap8") + name_length = display.measure_text(card1.title, card1.text_scale) + if name_length >= WIDTH - border: + card1.text_scale -= 1 + else: + # comment out this section if you hate drop shadow + print(f'card1.text_scale {card1.text_scale}') + + # display.text(card1.title, int((WIDTH - name_length) / 2 + 10), 10 , WIDTH, title_size) + + # draw name and stop looping + # card1.title_size = title_size +# display.set_pen(text_colour) +# display.text(card1.title, int((WIDTH - name_length) / 2 + 10), 10, WIDTH, title_size) + break + +card1.update() + + +# chart1 = Chart(display, title='Temperature', x_values=data) +# chart1.data_colour = WHITE +# chart1.title_colour = WHITE +# chart1.border_colour = WHITE +# chart1.background_colour = BLUE +# chart1.grid_colour = LIGHT_BLUE +# chart1.border_width = 1 + +# chart1.x = 0 +# chart1.y = 0 +# chart1.width = WIDTH +# chart1.height = HEIGHT +# chart1.grid = True +# chart1.update() +# chart1.show_labels = True +# chart1.show_lines = True +# chart1.show_bars = False +# chart1.show_datapoints = True + +# sensor_temp = machine.ADC(4) +# conversion_factor = 3.3 / (65535) + +# data = [20,34] +# chart1.x_values = data +# chart1.scale_data() +# data = [] +# chart1.x_values = data + +# while True or KeyboardInterrupt: +# reading = sensor_temp.read_u16() * conversion_factor +# temperature = int(27 - (reading - 0.706)/0.001721) +# data.append(temperature) +# print(data) +# if len(data) == 29: +# data.pop(0) + +# chart1.x_values = data + +# print(chart1.x_values)# +# chart1.update() +# sleep(0.5) \ No newline at end of file diff --git a/chart.py b/chart.py deleted file mode 100644 index a452a8d..0000000 --- a/chart.py +++ /dev/null @@ -1,317 +0,0 @@ -class Chart: - __title = '' - __x_label = '' - __y_label = '' - __x_values = [] - __x_scale = 1 - __y_scale = 1 - __x_offset = 0 - __y_offset = 0 - background_colour = {'red': 0, 'green': 0, 'blue': 0} - border_colour = {'red': 0, 'green': 0, 'blue': 0} - grid_colour = {'red':border_colour['red']//4, 'green':border_colour['green']//4,'blue':border_colour['blue']//4} - __title_colour = {'red': 0, 'green': 0, 'blue': 0} - __data_colour = {'red': 0, 'green': 0, 'blue': 0} - __data_point_radius = 2 - __data_point_radius2 = __data_point_radius * 4 - __data_point_width = 10 - __data_point_height = 1 - __data_point_x_offset = 0 - __data_point_y_offset = 0 - x = 0 - y = 0 - __width = 100 - __height = 100 - border_width = 2 - __text_height = 16 - __show_datapoints = False - __show_lines = False - __show_bars = True - __show_labels = False - grid = True - grid_spacing = 10 - - def __init__(self, display, title=None, x_label=None, y_label=None, x_values=None, y_values=None): - self.display = display - if title: self.__title = title - if x_label: self.__x_label = x_label - if y_label: self.__y_label = y_label - if x_values: - # Scale the data - self.__x_values = x_values - self.min_val = min(self.__x_values) # get the minimum value - self.max_val = max(self.__x_values) # get the maximum value - if y_values: self.__y_values = y_values - - @property - def show_bars(self): - """ Get the show_bars value """ - return self.__show_bars - - @show_bars.setter - def show_bars(self, value): - """ Set the show_bars value """ - self.__show_bars = value - - @property - def show_datapoints(self): - """ Get Show the data points status """ - return self.__show_datapoints - - @show_datapoints.setter - def show_datapoints(self, value): - """ Set Show the data points status""" - self.__show_datapoints = value - - @property - def show_lines(self): - """ Get Show the lines status """ - return self.__show_lines - - @show_lines.setter - def show_lines(self, value): - """ Set Show the lines status""" - self.__show_lines = value - - @property - def data_point_radius(self): - """ Get the radius of the data points """ - return self.__data_point_radius - - @data_point_radius.setter - def data_point_radius(self, value): - """ Set the radius of the data points """ - self.__data_point_radius = value - - @property - def show_labels(self): - """ Get the show_labels value """ - return self.__show_labels - - @show_labels.setter - def show_labels(self, value): - """ Set the show_labels value """ - self.__show_labels = value - self.data_point_radius2 = self.data_point_radius - - @property - def width(self): - """ Get the width of the chart """ - return self.__width - - @width.setter - def width(self, width): - """ Set the width of the chart """ - self.__width = width - - @property - def height(self): - """ Get the height of the chart """ - return self.__height - - @height.setter - def height(self, height): - """ Set the height of the chart """ - self.__height = height - - @property - def data_colour(self): - """ Get the data colour """ - return self.__data_colour - - @data_colour.setter - def data_colour(self, colour): - """ Set the data colour """ - self.__data_colour = colour - - @property - def title_colour(self): - """ Get the title colour """ - return self.__title_colour - - @title_colour.setter - def title_colour(self, colour): - """ Set the title colour """ - self.__title_colour = colour - - @property - def x_offset(self): - return self.__x_offset - - @x_offset.setter - def x_offset(self, value): - self.__x_offset = value - - @property - def y_offset(self, value): - self.__y_offset = value - - @y_offset.setter - def y_offset(self, value): - self.__y_offset = value - - @property - def x_values(self): - return self.__x_values - - @x_values.setter - def x_values(self, value): - """ Set the x values """ - self.__x_values = value - - def set_x_axis_colour(self, red, green, blue): - """ Set the x axis colour """ - self.x_axis_colour = {'red': red, 'green': green, 'blue': blue} - - @property - def title(self): - """ Get the title """ - return self.__title - - @title.setter - def title(self, title): - """ Set the title """ - self.__title = title - - @property - def data_point_radius2(self): - """ Get the radius of the data points """ - return self.__data_point_radius2 - - @data_point_radius2.setter - def data_point_radius2(self, value): - """ Set the radius of the data points """ - self.__data_point_radius2 = value - - def draw_border(self): - self.display.set_pen(self.border_colour['red'], self.border_colour['green'], self.border_colour['blue']) - x = self.x - y = self.y - w = self.__width - h = self.__height - x1 = x+w - y1 = y+h - self.display.set_clip(x,y,x1,y1) - - # Draw the 4 border lines - for i in range(0,self.border_width,1): - self.display.line(x+i, y+i, x+i, y1-i) # left - self.display.line(x+i, y+i, x1-i, y+i) # top - self.display.line(x+i, y1-i-1, x1-i, y1-i-1) # bottom - self.display.line(x1-i-1, y+i, x1-i-1, y1) # right - - self.display.set_pen(self.background_colour['red'], self.background_colour['green'], self.background_colour['blue']) - self.display.remove_clip() - - self.display.update() - - - def draw_grid(self): - # self.display.set_pen(self.border_colour['red']//4, self.border_colour['green']//4, self.border_colour['blue']//4) - self.display.set_pen(self.grid_colour['red'], self.grid_colour['green'], self.grid_colour['blue']) - x = self.x - y = self.y - w = self.width - h = self.height - - cols = w // self.grid_spacing - row = h // self.grid_spacing - -# print(f'cols: {cols}, row: {row}') - - for i in range(cols): - self.display.line(x+self.grid_spacing*i, y, x+self.grid_spacing*i, y+h) - # print(f'drawing cols: {i}') - - for j in range(row): - self.display.line(x, y+self.grid_spacing*j, x+w, y+self.grid_spacing*j) - # print(f'drawing row: {j}') - - # Update display - self.display.update() - - def map(self, x, in_min, in_max, out_min, out_max): - """ Map a value from one range to another """ - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min - - def scale_data(self): - - self.min_val = min(self.__x_values) # get the minimum value - self.max_val = max(self.__x_values) # get the maximum value - - # Calculate the scale - if self.max_val - self.min_val == 0: - self.max_val = 2 - self.min_val = 1 - self.__y_scale = (((self.__height - self.__text_height) - self.border_width * 2)) // (self.max_val - self.min_val) -# print(f'Min: {self.min_val} Max: {self.max_val}, Y Scale: {self.__y_scale}') - - def update(self): - """ Update the chart """ - - self.display.set_clip(self.x, self.y, self.x+self.width, self.y+self.height) - self.display.clear() - self.display.remove_clip() - - # Draw the Grid - if self.grid: self.draw_grid() - - gap = 3 - - # display the Title - self.display.set_clip(self.x+gap, self.y+gap, (self.x+self.width)-gap, (self.y+self.height)-gap) - self.display.set_pen(self.__title_colour['red'], self.__title_colour['green'], self.__title_colour['blue']) - self.display.text(self.title, self.x+self.border_width+1, self.y + self.border_width+1,self.__width) - self.display.set_pen(self.__data_colour['red'], self.__data_colour['green'], self.__data_colour['blue']) - self.display.remove_clip() - - # Work out the area offset - self.__x_offset = self.border_width+2 - self.__y_offset = (self.height-self.border_width)-2 - - x_pos = self.x + self.__x_offset - y_pos = self.y + self.__y_offset - - prev_x = x_pos - prev_y = y_pos - - - # The area within the chart - plot_area = (self.height - self.__text_height) - self.border_width*2 - self.display.set_clip(self.x+self.border_width, self.y+self.border_width+self.__text_height, self.x+self.width, (self.y+self.height)-self.border_width) -# self.display.line(self.x, self.y+self.height - plot_area, self.width, self.y+self.height-plot_area) - # print(f'plot area: {plot_area}, height: {self.height}') - for item in self.__x_values: - val = item - item = int(self.map(item, self.min_val, self.max_val,0,plot_area)) # scale the data - - # calculate data visual height - data_height = int(item) - - if self.show_bars: - self.display.rectangle(x_pos, y_pos-item, self.__data_point_width, data_height) - - if self.show_datapoints: - self.display.set_pen(self.__data_colour['red']//4, self.__data_colour['green']//4, self.__data_colour['blue']//4) - self.display.circle(x_pos, y_pos-item, self.__data_point_radius2) - self.display.set_pen(self.__data_colour['red'], self.__data_colour['green'], self.__data_colour['blue']) - self.display.circle(x_pos, y_pos-item, self.__data_point_radius) - - if self.show_lines: - self.display.line(x_pos, y_pos-item, prev_x, prev_y) - - if self.show_labels: -# self.display.set_pen(self.data_colour['red']//2,self.__data_colour['green']//2, self.__data_colour['blue']//2) - self.display.text(str(val), x_pos-4, y_pos-item -10, self.width - y_pos, 1) - - prev_x = x_pos - prev_y = y_pos-item - x_pos += self.__data_point_width+1 - - self.display.remove_clip() - - # Draw the border - self.draw_border() - - self.display.update() - \ No newline at end of file diff --git a/dashboard.py b/dashboard.py index 92bb3a0..bc859b7 100644 --- a/dashboard.py +++ b/dashboard.py @@ -3,7 +3,7 @@ # June 2022 # import picodisplay2 as display -from chart import Chart +from pichart import Chart from st7789 import ST7789 import gc import machine @@ -64,13 +64,13 @@ def draw_data(): chart1.height = HEIGHT//3 chart2.x = 0 -chart2.y = (HEIGHT//3)*1 +chart2.y = HEIGHT//3*1 chart2.width = WIDTH -chart2.height = HEIGHT//3 +chart2.height = HEIGHT//3 chart2.data_point_radius = 2 chart3.x = 0 -chart3.y = (HEIGHT //3)*2 +chart3.y = HEIGHT //3*2 chart3.width = WIDTH chart3.height = HEIGHT//3 @@ -93,7 +93,7 @@ def draw_data(): sensor_temp = machine.ADC(4) conversion_factor = 3.3 / (65535) -data = [25,34] +data = [10,34] chart1.x_values = data chart2.x_values = data chart3.x_values = data diff --git a/dashboard2.py b/dashboard2.py index d0b4327..47984c1 100644 --- a/dashboard2.py +++ b/dashboard2.py @@ -3,7 +3,7 @@ # June 2022 # import picodisplay2 as display -from chart import Chart +from pichart import Chart from st7789 import ST7789 import gc import machine diff --git a/dashboard3.py b/dashboard3.py index d2cbdc6..fa65d94 100644 --- a/dashboard3.py +++ b/dashboard3.py @@ -3,23 +3,23 @@ # June 2022 # import picodisplay2 as display -from chart import Chart -from st7789 import ST7789 +from pichart import Chart +from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2 +display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2) import gc import machine from time import sleep from random import randint -WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 +# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 +WIDTH, HEIGHT = display.get_bounds() -display = ST7789(WIDTH, HEIGHT, rotate180=False) +# display = ST7789(WIDTH, HEIGHT, rotate180=False) gc.collect() # Set the display backlight to 50% display.set_backlight(1.0) - - data = [10,11,9,13,2,10,12,10,10,9,7,11,10,11,9,13,2,10,12,10,10,9,7,11] # Set the theme colour @@ -108,7 +108,7 @@ chart4.show_labels = True -chart1.grid = True +# chart1.grid = True # chart3.grid = False chart1.update() diff --git a/dashboard4.py b/dashboard4.py index 467ff05..e5f813b 100644 --- a/dashboard4.py +++ b/dashboard4.py @@ -3,24 +3,25 @@ # June 2022 # import picodisplay2 as display -from chart import Chart -from st7789 import ST7789 +from pichart import Chart +from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2 +display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2) +# from st7789 import ST7789 import gc import machine from time import sleep from random import randint # WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 -WIDTH, HEIGHT = 240, 135 # Pico Display Pack +# WIDTH, HEIGHT = 240, 135 # Pico Display Pack +WIDTH, HEIGHT = display.get_bounds() -display = ST7789(WIDTH, HEIGHT, rotate180=False) +# display = ST7789(WIDTH, HEIGHT, rotate180=False) gc.collect() # Set the display backlight to 50% display.set_backlight(1.0) - - data = [10,11,9,13,2,10,12,10,10,9,7,11,10,11,9,13,2,10,12,10,10,9,7,11] # Set the theme colour diff --git a/dashboard5.py b/dashboard5.py new file mode 100644 index 0000000..c3a28f5 --- /dev/null +++ b/dashboard5.py @@ -0,0 +1,159 @@ +# Pico Dashboard +# Kevin McAleer +# June 2022 + +# import picodisplay2 as display +from pichart import Chart, Card +from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2 +display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2) +import gc +import machine +from time import sleep +from random import randint +# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 +WIDTH, HEIGHT = display.get_bounds() + +# display = ST7789(WIDTH, HEIGHT, rotate180=False) + +gc.collect() + +# Set the display backlight to 50% +display.set_backlight(1.0) + +data = [10,11,9,13,2,10,12,10,10,9,7,11,10,11,9,13,2,10,12,10,10,9,7,11] + +# Set the theme colour +THEME = {'red': 255, 'green': 171, 'blue': 57} +THEME2 = {'red': 252, 'green': 193, 'blue': 109} +THEME3 = {'red': 151, 'green': 250, 'blue': 121} +WHITE = {'red': 255, 'green': 255, 'blue': 255} +LIGHT_GREY = {'red': 240, 'green': 240, 'blue': 240} +BLUE = {'red': 20, 'green': 155, 'blue': 206} +LIGHT_BLUE = {'red': 55, 'green': 170, 'blue': 213} +GREEN = {'red': 0, 'green': 220, 'blue': 0} +ORANGE = {'red': 255, 'green': 171, 'blue': 57} +LIGHT_ORANGE = {'red': 255, 'green': 195, 'blue': 110} + +# Frame background colour +BACKGROUND = {'red': 255, 'green': 171, 'blue': 57} + +border = 20 +# frame(border,border,width-(border*2),height-(border*2),BACKGROUND) + +chart1 = Chart(display, title='Temperature', x_values=data) +chart2 = Card(display, title='Humidity') +chart3 = Chart(display, title="Pressure", x_values=data) +chart4 = Chart(display, title="Air Quality", x_values=data) + +chart1.data_colour = WHITE +chart3.data_colour = WHITE +chart4.data_colour = WHITE + +chart1.title_colour = WHITE +chart2.title_colour = WHITE +chart3.title_colour = WHITE +chart4.title_colour = WHITE + +chart1.border_colour = WHITE +chart2.border_colour = WHITE +chart3.border_colour = WHITE +chart4.border_colour = WHITE + +chart1.background_colour = BLUE +chart2.background_colour = BLUE +chart3.background_colour = BLUE +chart4.background_colour = ORANGE + +chart1.grid_colour = LIGHT_BLUE +chart2.grid_colour = LIGHT_BLUE +chart3.grid_colour = LIGHT_BLUE +chart4.grid_colour = LIGHT_ORANGE + +chart1.border_width = 1 +chart2.margin = 30 +chart3.border_width = 1 +chart4.border_width = 1 + +chart1.x = 0 +chart1.y = 0 +chart1.width = WIDTH//2 +chart1.height = HEIGHT //2 + +chart2.x = (WIDTH//2 * 1) +chart2.y = 0 +chart2.width = WIDTH//2 +chart2.height = HEIGHT //2 +chart2.data_point_radius = 2 + +chart3.x = 0 +chart3.y = HEIGHT //2 +chart3.width = WIDTH //2 +chart3.height = HEIGHT //2 + +chart4.x = (WIDTH//2 * 1) +chart4.y = HEIGHT //2 +chart4.width = WIDTH //2 +chart4.height = HEIGHT //2 + + +chart2.show_bars = False +chart2.show_lines = True +chart3.data_point_radius = 2 + +chart3.show_datapoints = True +chart3.show_bars = False +chart3.show_labels = True +chart3.show_lines = True + +chart4.show_labels = True + +# chart1.grid = True +# chart3.grid = False + +chart1.update() +chart2.update() +chart3.update() +chart4.update() + +sensor_temp = machine.ADC(4) +conversion_factor = 3.3 / (65535) + +data = [20,34] +chart1.x_values = data +chart2.x_values = data +chart3.x_values = data +chart4.x_values = [0,50] + +chart1.scale_data() +chart3.scale_data() +chart4.scale_data() + +data = [] +air_quality_data = [] + +chart1.x_values = data +chart3.x_values = data +chart4.x_values = air_quality_data + +while True or KeyboardInterrupt: + reading = sensor_temp.read_u16() * conversion_factor + temperature = int(27 - (reading - 0.706)/0.001721) + data.append(temperature) + air_quality_data.append(randint(0, 50)) + print(data) + if len(data) == 15: + data.pop(0) + if len(air_quality_data) == 15: + air_quality_data.pop(0) + chart1.x_values = data + chart2.title = str(data[-1]) + chart3.x_values = data + chart4.x_values = air_quality_data +# print(chart1.x_values)## + chart1.update() + chart2.update() + chart3.update() + chart4.update() + + sleep(0.5) + diff --git a/dashboard6.py b/dashboard6.py new file mode 100644 index 0000000..0c223ef --- /dev/null +++ b/dashboard6.py @@ -0,0 +1,170 @@ +# Pico Dashboard +# Kevin McAleer +# June 2022 + +# import picodisplay2 as display +from pichart import Chart, Card, Container +from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2 +display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2) +import gc +import machine +from time import sleep +from random import randint +# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0 +WIDTH, HEIGHT = display.get_bounds() + +# display = ST7789(WIDTH, HEIGHT, rotate180=False) + +gc.collect() + +# Set the display backlight to 50% +display.set_backlight(1.0) + +data = [10,11,9,13,2,10,12,10,10,9,7,11,10,11,9,13,2,10,12,10,10,9,7,11] + +# Set the theme colour +THEME = {'red': 255, 'green': 171, 'blue': 57} +THEME2 = {'red': 252, 'green': 193, 'blue': 109} +THEME3 = {'red': 151, 'green': 250, 'blue': 121} +WHITE = {'red': 255, 'green': 255, 'blue': 255} +LIGHT_GREY = {'red': 240, 'green': 240, 'blue': 240} +BLUE = {'red': 20, 'green': 155, 'blue': 206} +LIGHT_BLUE = {'red': 55, 'green': 170, 'blue': 213} +GREEN = {'red': 0, 'green': 220, 'blue': 0} +ORANGE = {'red': 255, 'green': 171, 'blue': 57} +LIGHT_ORANGE = {'red': 255, 'green': 195, 'blue': 110} + +# Frame background colour +BACKGROUND = {'red': 255, 'green': 171, 'blue': 57} + +border = 20 +# frame(border,border,width-(border*2),height-(border*2),BACKGROUND) + +chart1 = Chart(display, title='Temperature', x_values=data) +chart2 = Card(display, title='Humidity') +chart3 = Chart(display, title="Pressure", x_values=data) +chart4 = Chart(display, title="Air Quality", x_values=data) + +chart1.data_colour = WHITE +chart3.data_colour = WHITE +chart4.data_colour = WHITE + +chart1.title_colour = WHITE +chart2.title_colour = WHITE +chart3.title_colour = WHITE +chart4.title_colour = WHITE + +chart1.border_colour = WHITE +chart2.border_colour = WHITE +chart3.border_colour = WHITE +chart4.border_colour = WHITE + +chart1.background_colour = BLUE +chart2.background_colour = BLUE +chart3.background_colour = BLUE +chart4.background_colour = ORANGE + +chart1.grid_colour = LIGHT_BLUE +chart2.grid_colour = LIGHT_BLUE +chart3.grid_colour = LIGHT_BLUE +chart4.grid_colour = LIGHT_ORANGE + +chart1.border_width = 1 +chart2.margin = 30 +chart3.border_width = 1 +chart4.border_width = 1 + +container = Container(display) +container.add_chart(chart4) +container.add_chart(chart3) +container.add_chart(chart2) +container.add_chart(chart1) + +container.cols = 2 + +container.update() + +# chart1.x = 0 +# chart1.y = 0 +# chart1.width = WIDTH//2 +# chart1.height = HEIGHT //2 + +# chart2.x = (WIDTH//2 * 1) +# chart2.y = 0 +# chart2.width = WIDTH//2 +# chart2.height = HEIGHT //2 +# chart2.data_point_radius = 2 + +# chart3.x = 0 +# chart3.y = HEIGHT //2 +# chart3.width = WIDTH //2 +# chart3.height = HEIGHT //2 + +# chart4.x = (WIDTH//2 * 1) +# chart4.y = HEIGHT //2 +# chart4.width = WIDTH //2 +# chart4.height = HEIGHT //2 + + +chart2.show_bars = False +chart2.show_lines = True +chart3.data_point_radius = 2 + +chart3.show_datapoints = True +chart3.show_bars = False +chart3.show_labels = True +chart3.show_lines = True + +chart4.show_labels = True + +# chart1.grid = True +# chart3.grid = False + +# chart1.update() +# chart2.update() +# chart3.update() +# chart4.update() + +sensor_temp = machine.ADC(4) +conversion_factor = 3.3 / (65535) + +data = [20,34] +chart1.x_values = data +chart2.x_values = data +chart3.x_values = data +chart4.x_values = [0,50] + +chart1.scale_data() +chart3.scale_data() +chart4.scale_data() + +data = [] +air_quality_data = [] + +chart1.x_values = data +chart3.x_values = data +chart4.x_values = air_quality_data + +while True or KeyboardInterrupt: + reading = sensor_temp.read_u16() * conversion_factor + temperature = int(27 - (reading - 0.706)/0.001721) + data.append(temperature) + air_quality_data.append(randint(0, 50)) + print(data) + if len(data) == 15: + data.pop(0) + if len(air_quality_data) == 15: + air_quality_data.pop(0) + chart1.x_values = data + chart2.title = str(data[-1]) + chart3.x_values = data + chart4.x_values = air_quality_data +# print(chart1.x_values)## + container.update() + # chart1.update() + # chart2.update() + # chart3.update() + # chart4.update() + + sleep(0.5) + diff --git a/pichart.py b/pichart.py new file mode 100644 index 0000000..4b5b967 --- /dev/null +++ b/pichart.py @@ -0,0 +1,323 @@ +class Chart: + title = '' + x_values = [] + __x_offset = 0 + __y_offset = 0 + background_colour = {'red': 0, 'green': 0, 'blue': 0} + border_colour = {'red': 0, 'green': 0, 'blue': 0} + grid_colour = {'red':border_colour['red']//4, 'green':border_colour['green']//4,'blue':border_colour['blue']//4} + title_colour = {'red': 0, 'green': 0, 'blue': 0} + data_colour = {'red': 0, 'green': 0, 'blue': 0} + __data_point_radius = 2 + data_point_radius2 = __data_point_radius * 4 + __data_point_width = 10 + x = 0 + y = 0 + width = 100 + height = 100 + border_width = 2 + __text_height = 16 + show_datapoints = False + show_lines = False + show_bars = True + __show_labels = False + grid = True + grid_spacing = 10 + + def __init__(self, display, title=None, x_label=None, y_label=None, x_values=None, y_values=None): + self.display = display + if title: self.title = title + if x_label: self.__x_label = x_label + if y_label: self.__y_label = y_label + if x_values: + # Scale the data + self.x_values = x_values + self.min_val = min(self.x_values) # get the minimum value + self.max_val = max(self.x_values) # get the maximum value + if y_values: self.__y_values = y_values + + @property + def data_point_radius(self): + """ Get the radius of the data points """ + return self.__data_point_radius + + @data_point_radius.setter + def data_point_radius(self, value): + """ Set the radius of the data points """ + self.__data_point_radius = value + + @property + def show_labels(self): + """ Get the show_labels value """ + return self.__show_labels + + @show_labels.setter + def show_labels(self, value): + """ Set the show_labels value """ + self.__show_labels = value + self.data_point_radius2 = self.data_point_radius + + def draw_border(self): + border_colour = self.display.create_pen(self.border_colour['red'], self.border_colour['green'], self.border_colour['blue']) + background_colour = self.display.create_pen(self.background_colour['red'], self.background_colour['green'], self.background_colour['blue']) + self.display.set_pen(border_colour) + x = self.x + y = self.y + w = self.width + h = self.height + x1 = x+w + y1 = y+h + self.display.set_clip(x,y,x1,y1) + + # Draw the 4 border lines + for i in range(0,self.border_width,1): + self.display.line(x+i, y+i, x+i, y1-i) # left + self.display.line(x+i, y+i, x1-i, y+i) # top + self.display.line(x+i, y1-i-1, x1-i, y1-i-1) # bottom + self.display.line(x1-i-1, y+i, x1-i-1, y1) # right + + self.display.set_pen(background_colour) + self.display.remove_clip() + +# self.display.update() + + + def draw_grid(self): + """ Draw the grid behind the chart """ + + # Set the colour of the grid + grid_colour = self.display.create_pen(self.grid_colour['red'], self.grid_colour['green'], self.grid_colour['blue']) + self.display.set_pen(grid_colour) + + # Create values + x = self.x + y = self.y + w = self.width + h = self.height + + # Calculate columns + cols = w // self.grid_spacing + row = h // self.grid_spacing + + # Draw Grid + for i in range(cols): + self.display.line(x+self.grid_spacing*i, y, x+self.grid_spacing*i, y+h) + + for j in range(row): + self.display.line(x, y+self.grid_spacing*j, x+w, y+self.grid_spacing*j) + + # Update display +# self.display.update() + + def map(self, x, in_min, in_max, out_min, out_max): + """ Map a value from one range to another """ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min + + def scale_data(self): + """ Scale the data to fit the chart """ + + self.min_val = min(self.x_values) # get the minimum value + self.max_val = max(self.x_values) # get the maximum value + + # Calculate the scale + if self.max_val - self.min_val == 0: + self.max_val = 2 + self.min_val = 1 + self.__y_scale = (((self.height - self.__text_height) - self.border_width * 2)) // (self.max_val - self.min_val) + + def update(self): + """ Update the chart """ + + self.display.set_clip(self.x, self.y, self.x+self.width, self.y+self.height) + background_colour = self.display.create_pen(self.background_colour['red'], self.background_colour['green'], self.background_colour['blue']) + self.display.set_pen(background_colour) + self.display.rectangle(self.x, self.y, self.width, self.height) + + self.display.remove_clip() + + # Draw the Grid + if self.grid: + self.draw_grid() + + gap = 3 + + # display the Title + title_colour = self.display.create_pen(self.title_colour['red'], self.title_colour['green'], self.title_colour['blue']) + data_colour = self.display.create_pen(self.data_colour['red'], self.data_colour['green'], self.data_colour['blue']) + data_colour2 = self.display.create_pen(self.data_colour['red']//4, self.data_colour['green']//4, self.data_colour['blue']//4) + + self.display.set_clip(self.x+gap, self.y+gap, (self.x+self.width)-gap, (self.y+self.height)-gap) + self.display.set_pen(title_colour) + self.display.text(self.title, self.x+self.border_width+1, self.y + self.border_width+1,self.width) + self.display.set_pen(data_colour) + self.display.remove_clip() + + # Work out the area offset + self.__x_offset = self.border_width+2 + self.__y_offset = (self.height-self.border_width)-2 + + x_pos = self.x + self.__x_offset + y_pos = self.y + self.__y_offset + + prev_x = x_pos + prev_y = y_pos + + + # The area within the chart + plot_area = (self.height - self.__text_height) - self.border_width*2 + self.display.set_clip(self.x+self.border_width, self.y+self.border_width+self.__text_height, self.x+self.width, (self.y+self.height)-self.border_width) + + for item in self.x_values: + val = item + item = int(self.map(item, self.min_val, self.max_val,0,plot_area)) # scale the data + + # calculate data visual height + data_height = int(item) + + if self.show_bars: + self.display.rectangle(x_pos, y_pos-item, self.__data_point_width, data_height) + + if self.show_datapoints: + self.display.set_pen(data_colour2) + self.display.circle(x_pos, y_pos-item, self.data_point_radius2) + self.display.set_pen(data_colour) + self.display.circle(x_pos, y_pos-item, self.__data_point_radius) + + if self.show_lines: + self.display.line(x_pos, y_pos-item, prev_x, prev_y) + + if self.show_labels: + self.display.text(str(val), x_pos-4, y_pos-item -10, self.width - y_pos, 1) + + prev_x = x_pos + prev_y = y_pos-item + x_pos += self.__data_point_width+1 + + self.display.remove_clip() + + # Draw the border + self.draw_border() + + self.display.update() + + +class Card(Chart): + """ A card class """ + +# display = None +# x = 0 +# y = 0 +# width = 0 +# height = 0 +# title = '' +# background_colour = {'red':0, 'green':0, 'blue':0} +# title_colour = {'red':0, 'green':0, 'blue':0} + text_scale = 20 + margin = 2 + + def __init__(self, display, x=None, y=None, width=None, height=None, title=None): + """ Initialise the card """ + self.display = display + if x: self.x = x + if y: self.y = y + if width: self.width = width + if height: self.height = height + if title: self.title = title + + def scale_text(self): + """ Scale the text """ + + self.text_scale = 20 + + while True: + self.display.set_font("bitmap8") + name_length = self.display.measure_text(self.title, self.text_scale) + if name_length >= self.width - self.margin*2: + self.text_scale -= 1 + else: + break + + if self.text_scale * 8 > self.height: + self.text_scale = self.height // 8 + + def update(self): + """ Update the card """ + + # Set the colours + background_color = self.display.create_pen(self.background_colour['red'], self.background_colour['green'], self.background_colour['blue']) + title_color = self.display.create_pen(self.title_colour['red'], self.title_colour['green'], self.title_colour['blue']) + self.display.set_pen(background_color) + + # Clear the display, without flickering + self.display.rectangle(self.x, self.y, self.width, self.height) + + # Draw the Grid, if its enabled + if self.grid: + self.draw_grid() + + # Draw the border + self.draw_border() + + self.display.set_pen(title_color) + + # Centered title + self.scale_text() + text_length = self.display.measure_text(self.title, self.text_scale) + + + # title_x = ((text_length - self.width) //2) + title_y = (self.y + self.height // 2) - (self.text_scale * 8) // 2 + + # Draw the title + self.display.text(self.title, self.x + ((self.width //2) - (text_length //2)), title_y, text_length, self.text_scale) + + # Update the display + self.display.update() + + +class Container: + """ A container class """ + + display = None + + charts = [] + cols = 1 + + def __init__(self, display, width=None, height=None): + if width: + self.width = width + if height: + self.height = height + else: + self.width, self.height = display.get_bounds() + + def add_chart(self, item): + """ Add an item to the container """ + self.charts.append(item) + + def update(self): + """ Update the container """ + + rows = len(self.charts) // self.cols + + print(f'rows: {rows}, cols: {self.cols}') + + item_count = rows // self.cols + + count = 1 + for col in range(1,self.cols+1,1): + for row in range(1, rows+1, 1): + item_index = count + print(f'index_item: {item_index}') + + item = self.charts[item_index-1] + item.height = self.height // rows + item.y = self.height - (row * item.height) + item.width = self.width // self.cols + item.x = self.width - (self.width // col) + count +=1 + if count > 4: + count = 1 + for item in self.charts: + item.update() +