diff --git a/PiicoDev_SSD1306.py b/PiicoDev_SSD1306.py index 32695f3..34856ff 100644 --- a/PiicoDev_SSD1306.py +++ b/PiicoDev_SSD1306.py @@ -58,14 +58,14 @@ def _set_pos(self, col=0, page=0): c1, c2 = col * 2 & 0x0F, col >> 3 self.write_cmd(0x00 | c1) # lower start column address self.write_cmd(0x10 | c2) # upper start column address - + def fill(self, c=0): for i in range(0, 1024): if c > 0: self.buffer[i] = 0xFF else: self.buffer[i] = 0x00 - + def pixel(self, x, y, color): x = x & (WIDTH - 1) y = y & (HEIGHT - 1) @@ -78,17 +78,17 @@ def pixel(self, x, y, color): def line(self, x1, y1, x2, y2, c): # bresenham steep = abs(y2-y1) > abs(x2-x1) - + if steep: # Swap x/y tmp = x1 x1 = y1 y1 = tmp - + tmp = y2 y2 = x2 x2 = tmp - + if x1 > x2: # Swap start/end tmp = x1 @@ -97,12 +97,12 @@ def line(self, x1, y1, x2, y2, c): tmp = y1 y1 = y2 y2 = tmp - + dx = x2 - x1; dy = abs(y2-y1) - + err = dx/2 - + if(y1 < y2): ystep = 1 else: @@ -118,23 +118,23 @@ def line(self, x1, y1, x2, y2, c): y1 += ystep err += dx x1 += 1 - + def hline(self, x, y, l, c): self.line(x, y, x + l, y, c) - + def vline(self, x, y, h, c): self.line(x, y, x, y + h, c) - + def rect(self, x, y, w, h, c): self.hline(x, y, w, c) self.hline(x, y+h, w, c) self.vline(x, y, h, c) self.vline(x+w, y, h, c) - + def fill_rect(self, x, y, w, h, c): for i in range(y, y + h): self.hline(x, i, w, c) - + def text(self, text, x, y, c=1): fontFile = open("font-pet-me-128.dat", "rb") font = bytearray(fontFile.read()) @@ -149,8 +149,8 @@ def text(self, text, x, y, c=1): y_coordinate = y+i if x_coordinate < WIDTH and y_coordinate < HEIGHT: self.pixel(x_coordinate, y_coordinate, c) - - + + class PiicoDev_SSD1306(framebuf.FrameBuffer): def init_display(self): self.width = WIDTH @@ -192,6 +192,7 @@ def init_display(self): _SET_DISP | 0x01, # display on ): # on self.write_cmd(cmd) + self._init_print_text = False def poweroff(self): self.write_cmd(_SET_DISP) @@ -220,7 +221,7 @@ def show(self): self.write_cmd(0) self.write_cmd(self.pages - 1) self.write_data(self.buffer) - + def write_cmd(self, cmd): try: self.i2c.writeto_mem(self.addr, int.from_bytes(b'\x80','big'), bytes([cmd])) @@ -228,7 +229,7 @@ def write_cmd(self, cmd): except: print(i2c_err_str.format(self.addr)) self.comms_err = True - + def write_data(self, buf): try: self.write_list[1] = buf @@ -237,7 +238,69 @@ def write_data(self, buf): except: print(i2c_err_str.format(self.addr)) self.comms_err = True - + + @property + def displaySize(self): + return [self.width,self.height] + + def _init_print_text_func(self,spacing,font_size): + self.line_cnt = (HEIGHT-spacing[1])//(8+spacing[2]) + self.disp_lst = [['',1]]* self.line_cnt + self.temp_display_lst = [['',1]]* self.line_cnt + self.font_height = font_size[1] + self.line_y_coord = [None]* self.line_cnt + self.font_size = font_size + for i in range(self.line_cnt): + self.line_y_coord[i] = spacing[1] + (font_size[1] + spacing[2])*i # Reduces processor burden + self._init_print_text = True + return self.line_y_coord + + def draw_print_text(self,font_size_y,spacing,blanking): + if blanking: + self.fill(0) + for i in range(1,self.line_cnt+1): + self.text(self.temp_display_lst[-i][0],spacing[0],self.line_y_coord[self.line_cnt-i],self.temp_display_lst[-i][1]) + if blanking: + self.show() + self.temp_display_lst = [['',1]]* self.line_cnt + self.disp_lst = self.disp_lst[-self.line_cnt:] + + def trunctate_text(self,txt,delim,c): + if len(txt) > 15: + find_val= txt.rfind(' ',delim,15) + if delim and find_val is not -1: + [disp,to_disp] = [txt[0:find_val],txt[(find_val):].strip()] + else: + [disp,to_disp] = [txt[0:15],txt[15:]] + self.disp_lst.append([disp,c]) + self.trunctate_text(to_disp,delim,c) + else: + self.disp_lst.append([txt,c]) + + def print(self,txt='',c=1,line_num=None,blanking=True,delim=True,font_size=[8,8],spacing=[0,0,0]): + ''' Prints up to 15 characters on a new line of the OLED, more characters will flow on to a new line, repeated calls will increment the line counter''' + # line num - optional argument to manually specify the line number to be printed on, takes prio over auto incremented lines (allows them to be blanked, does not overwrite the text buffer) + # auto_scroll - does the text auto-scroll + # Delim, will a delimiting character move that 'word' to the next line + # Font size - [x,y] dimensions of the characters + #Spacing is formatted as [starting-x,starting-y, y-spacing(bottom to top of char), ] + + if not self._init_print_text: + self._init_print_text_func(spacing,font_size) + if isinstance(txt,float) or isinstance(txt,int): + txt = str(txt) + if delim and not line_num: + if len(txt) > 15: + self.trunctate_text(txt,delim,c) + else: + self.disp_lst.append([txt,c]) + self.temp_display_lst = self.disp_lst + if line_num is not None and (0< line_num <= self.line_cnt): + self.temp_display_lst[line_num-1] = [txt,c] + elif line_num is not None and not (0< line_num <= self.line_cnt): + print('line_num out of range, max is {}'.format(self.line_cnt)) + self.draw_print_text(font_size[1],spacing,blanking) + def circ(self,x,y,r,t=1,c=1): for i in range(x-r,x+r+1): for j in range(y-r,y+r+1): @@ -247,14 +310,14 @@ def circ(self,x,y,r,t=1,c=1): else: if((i-x)**2 + (j-y)**2 < r**2) and ((i-x)**2 + (j-y)**2 >= (r-r*t-1)**2): self.pixel(i,j,c) - + def arc(self,x,y,r,stAng,enAng,t=0,c=1): for i in range(r*(1-t)-1,r): for ta in range(stAng,enAng,1): X = int(i*cos(radians(ta))+ x) Y = int(i*sin(radians(ta))+ y) self.pixel(X,Y,c) - + def load_pbm(self, filename, c): with open(filename, 'rb') as f: line = f.readline() @@ -272,7 +335,7 @@ def load_pbm(self, filename, c): y_coordinate = byte * 8 // WIDTH if x_coordinate < WIDTH and y_coordinate < HEIGHT: self.pixel(x_coordinate, y_coordinate, c) - + class graph2D: def __init__(self, originX = 0, originY = HEIGHT-1, width = WIDTH, height = HEIGHT, minValue=0, maxValue=255, c = 1, bars = False): self.minValue = minValue @@ -314,7 +377,7 @@ def __init__(self, bus=None, freq=None, sda=None, scl=None, addr=0x3C): super().__init__(self.buffer, WIDTH, HEIGHT, framebuf.MONO_VLSB) self.fill(0) self.show() - + class PiicoDev_SSD1306_MicroBit(PiicoDev_SSD1306): def __init__(self, bus=None, freq=None, sda=None, scl=None, addr=0x3C): self.i2c = create_unified_i2c(bus=bus, freq=freq, sda=sda, scl=scl) @@ -334,7 +397,7 @@ def __init__(self, bus=None, freq=None, sda=None, scl=None, addr=0x3C): self.init_display() self.fill(0) self.show() - + def create_PiicoDev_SSD1306(address=0x3C,bus=None, freq=None, sda=None, scl=None, asw=None): if asw == 0: _a = 0x3C elif asw == 1: _a = 0x3D @@ -352,4 +415,4 @@ def create_PiicoDev_SSD1306(address=0x3C,bus=None, freq=None, sda=None, scl=None display = PiicoDev_SSD1306_Linux(addr=_a, freq=freq) else: display = PiicoDev_SSD1306_MicroPython(addr=_a, bus=bus, freq=freq, sda=sda, scl=scl) - return display + return display \ No newline at end of file diff --git a/README.md b/README.md index 10c44e2..f2c4252 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,20 @@ x2 | int | 0 - 127 | X2 coordinate y2 | int | 0 - 63 | Y2 coordinate c | int | 0 - 1 | Set the line to the given color (0: Black, 1: White) +### `PiicoDev_SSD1306.print(txt, c, line_num=None, auto_scroll=True, delim=True, font_size=[8,8], spacing=[0,0,0])` +Print text on the OLED, each subsequent call will increment the line number from the bottom of the display. + +Parameter | Type | Range | Default | Description +--- | --- | --- | --- | --- +txt | string | Dependent | | Text to display. +c | int | 0 - 1 | 1 | The colour of the printed text +line_num | int | 0 - 8 (Dependent) | 0 | Optional: Prints on the requested line, default scrolls the text upwards +blanking | bool | True/False | True | When enabled each print() call redraws the display +delim | int | 0 - 16 | 1 | Set = 0/False to disable Delimiting +font_size | list of Integers | [1 - Font Width,1 - Font Height] | [8,8] | Used in calculations for the line spacing +spacing | list of Integers | [X-Starting Point, Y-Starting Point, Y-Spacing] | [0,0,0] | [X Starting Coordinate for all strings, Y Starting Coordinate for the first text write, Vertical spacing between Characters ] + + ### `PiicoDev_SSD1306.rect(x, y, w, h, c)` Draw a rectangle at the given location, size and color. Draws only a 1 pixel outline. diff --git a/example/print.py b/example/print.py new file mode 100644 index 0000000..d932729 --- /dev/null +++ b/example/print.py @@ -0,0 +1,85 @@ +from PiicoDev_SSD1306 import * +from PiicoDev_Unified import sleep_ms + +import utime + +display = create_PiicoDev_SSD1306() + +delay = 3000 + +# Basic print statements +example_number = 210.2 + +display.print('Hello world') +display.print() +display.print(example_number) # Emulating printing sensor values +display.print() +display.print("Number: {}".format(example_number)) + +# Clear screen and internal lists +sleep_ms(delay) +for _ in range(8): + display.print() + +# Demonstrate printing text at specific lines (with background boxes) + +display.print('Line 2 text',line_num=2) +display.print('Line 4 text',line_num=4) +display.fill_rect(0,40,128,8,1) +display.print('Spooky line', line_num=6,c=0,blanking=False) +display.show() + + +# Clear screen and internal lists +sleep_ms(delay) +for _ in range(8): + display.print() + +display.print('Show overflowing lines') + +sleep_ms(int(delay/2)) + + + + + + +# Clear screen and internal lists +sleep_ms(delay) +for _ in range(8): + display.print() + +# Demonstrating the delimiting text functionality +show_delim = True +show_no_delim = False + +if show_delim: + display.print('this string should overflow a couple of times into the next line on the OLED',delim=True) # Default + +if show_no_delim: + display.print('this string should overflow a couple of times into the next line on the OLED',delim=False) # Raggedy + +# Clear screen and internal lists +sleep_ms(delay) +for _ in range(8): + display.print() + +# Running a benchmark for printing lines +display.print('show how fast it takes to print lots of lines') + +sleep_ms(int(delay/2)) + +start = utime.ticks_us() +for i in range(100): + display.print('some text' + str(i)) + + +display.print() +display.print() + +outcome_str = str(round(utime.ticks_diff(utime.ticks_us(), start)/(100000000),2)) + ' seconds/print' +display.print(outcome_str) +print(outcome_str) + + + diff --git a/example/print_menu.py b/example/print_menu.py new file mode 100644 index 0000000..c92e437 --- /dev/null +++ b/example/print_menu.py @@ -0,0 +1,40 @@ +from PiicoDev_SSD1306 import * +from PiicoDev_Unified import sleep_ms + +import utime + +display = create_PiicoDev_SSD1306() + +textYCoords = display._init_print_text_func(font_size=[8,8],spacing=[0,0,0]) + +def menuSelect(lineNum,display,textYCoords,font_height=8): + dispParams = display.displaySize + display.fill_rect(0,textYCoords[lineNum-1],dispParams[0],font_height,1) + +def showMenu(mn_opt,hv_ln): + for i, item in enumerate(mn_opt): + if i == hv_ln-1: + display.print(item,line_num=i+1,c=0,blanking=False) + else: + display.print(item,line_num=i+1,c=1,blanking=False) + +hover_line = 1 + +menu_options = ['option1','option2','option3','option4','option5'] + + +for i in range(1,len(menu_options)): + display.fill(0) + menuSelect(hover_line,display,textYCoords) + showMenu(menu_options,hover_line) + hover_line = hover_line + 1 + display.show() + sleep_ms(700) + +for i in range(len(menu_options)+1,1,-1): + display.fill(0) + menuSelect(hover_line,display,textYCoords) + showMenu(menu_options,hover_line) + hover_line = hover_line-1 + display.show() + sleep_ms(700)