diff --git a/DISPLAYS.md b/DISPLAYS.md index dc60420..494b377 100644 --- a/DISPLAYS.md +++ b/DISPLAYS.md @@ -13,32 +13,33 @@ owing to their long update time. Size is diagonal in inches. C/M/GS color/1-bit monochrome/greyscale. Width and height are pixels. -| Size | Width | Height | Tech | Driver | Description | Notes | -|:------:|:-----:|:------:|:------|:--------------|:----------------------------|:------| -| 0.96C | 94 | 64 | OLED | [SSD1331][1d] | [Adafruit 684][1m] | | -| 1.12GS | 96 | 96 | OLED | [SSD1327][11d]| [Seeed 104030011][21m] | Obsolescent. | -| 1.27C | 128 | 96 | OLED | [SSD1351][2d] | [Adafruit 1673][2m] | | -| 1.5C | 128 | 128 | OLED | [SSD1351][2d] | [Adafruit 1431][3m] | | -| 1.44C | 128 | 128 | TFT | [ST7735R][4d] | [Adafruit 2088][5m] | | -| 1.5C | 160 | 128 | TFT | [ST7735R][4d] | [Adafruit 358][6m] | | -| 1.3C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 4313][7m] | | -| 1.5GS | 128 | 128 | OLED | [SSD1327][11d]| [Waveshare 13992][20m] | | -| 2.0C | 320 | 240 | TFT | [ST7789][5d] | [Waveshare Pico LCD 2][18m] | For Pi Pico. | -| 1.54C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 3787][8m] | | -| 1.14C | 240 | 135 | TFT | [ST7789][5d] | [T-Display][9m] | ESP32 with attached display. | -| 2.8C | 320 | 240 | TFT | [ST7789][5d] | [Waveshare pico 2.8][10m] | Display for Pi Pico. | -| 1.14C | 240 | 135 | TFT | [ST7789][5d] | [Waveshare pico 1.14][11m] | For Pi Pico. Buttons good for micro-gui. | -| 1.14C | 240 | 135 | TFT | [ST7789][5d] | [Pimoroni pico 1.14][23m] | For Pi Pico. Buttons good for micro-gui. | -| 3.2C | 320 | 240 | TFT | [ILI9341][6d] | [Adafruit 1743][12m] | Big display. eBay equivalents work here. | -| 3.5C | 480 | 320 | TFT | [ILI9486][12d]| [Waveshare Rpi 3.5 LCD][22m]| Pi HAT. Many pixels. Needs plenty of RAM. | -| 3.5C | 480 | 320 | TFT | [ILI9486][12d]| [Adafruit 3.5 LCD][24m] | 3.5" HX8357D display, notes as above. | -| 3.5C | 480 | 320 | TFT | [ILI9486][12d]| [Waveshere RPI 3.5 LCD][25m]| 3.5" ILI9488 display, notes as above. | -| 2.9M | 296 | 128 | eInk | [UC8151D][7d] | [Adafruit 4262][13m] | Flexible ePaper display. | -| 2.9M | 296 | 128 | eInk | [UC8151D][7d] | [Adafruit 4777][15m] | FeatherWing ePaper display. | -| 4.2M | 400 | 300 | eInk | [WS][10d] | [Waveshare pico 4.2][19m] | Pico, Pico W plug in. Other hosts via cable. | -| 2.7M | 274 | 176 | eInk | [HAT][8d] | [Waveshare HAT][14m] | HAT designed for Raspberry Pi, repurposed. | -| 2.7M | 400 | 240 | Sharp | [Sharp][9d] | [Adafruit 4694][16m] | Micropower monochrome display. | -| 1.3M | 168 | 144 | Sharp | [Sharp][9d] | [Adafruit 3502][17m] | Ditto. | +| Size | Width | Height | Tech | Driver | Description | Notes | +|:------:|:-----:|:------:|:------|:---------------|:-----------------------------|:---------------------------------------------| +| 0.96C | 94 | 64 | OLED | [SSD1331][1d] | [Adafruit 684][1m] | | +| 1.12GS | 96 | 96 | OLED | [SSD1327][11d] | [Seeed 104030011][21m] | Obsolescent. | +| 1.27C | 128 | 96 | OLED | [SSD1351][2d] | [Adafruit 1673][2m] | | +| 1.5C | 128 | 128 | OLED | [SSD1351][2d] | [Adafruit 1431][3m] | | +| 1.44C | 128 | 128 | TFT | [ST7735R][4d] | [Adafruit 2088][5m] | | +| 1.5C | 160 | 128 | TFT | [ST7735R][4d] | [Adafruit 358][6m] | | +| 1.3C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 4313][7m] | | +| 1.5GS | 128 | 128 | OLED | [SSD1327][11d] | [Waveshare 13992][20m] | | +| 2.0C | 320 | 240 | TFT | [ST7789][5d] | [Waveshare Pico LCD 2][18m] | For Pi Pico. | +| 1.54C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 3787][8m] | | +| 1.14C | 240 | 135 | TFT | [ST7789][5d] | [T-Display][9m] | ESP32 with attached display. | +| 2.8C | 320 | 240 | TFT | [ST7789][5d] | [Waveshare pico 2.8][10m] | Display for Pi Pico. | +| 1.14C | 240 | 135 | TFT | [ST7789][5d] | [Waveshare pico 1.14][11m] | For Pi Pico. Buttons good for micro-gui. | +| 1.14C | 240 | 135 | TFT | [ST7789][5d] | [Pimoroni pico 1.14][23m] | For Pi Pico. Buttons good for micro-gui. | +| 3.2C | 320 | 240 | TFT | [ILI9341][6d] | [Adafruit 1743][12m] | Big display. eBay equivalents work here. | +| 3.5C | 480 | 320 | TFT | [ILI9486][12d] | [Waveshare Rpi 3.5 LCD][22m] | Pi HAT. Many pixels. Needs plenty of RAM. | +| 3.5C | 480 | 320 | TFT | [ILI9486][12d] | [Adafruit 3.5 LCD][24m] | 3.5" HX8357D display, notes as above. | +| 3.5C | 480 | 320 | TFT | [ILI9486][12d] | [Waveshere RPI 3.5 LCD][25m] | 3.5" ILI9488 display, notes as above. | +| 2.9M | 296 | 128 | eInk | [UC8151D][7d] | [Adafruit 4262][13m] | Flexible ePaper display. | +| 2.9M | 296 | 128 | eInk | [UC8151D][7d] | [Adafruit 4777][15m] | FeatherWing ePaper display. | +| 2.9M | 296 | 128 | eInk | [SSD1680][15d] | [WeAct Studio ePaper][26m] | WeAct Studio ePaper display. | +| 4.2M | 400 | 300 | eInk | [WS][10d] | [Waveshare pico 4.2][19m] | Pico, Pico W plug in. Other hosts via cable. | +| 2.7M | 274 | 176 | eInk | [HAT][8d] | [Waveshare HAT][14m] | HAT designed for Raspberry Pi, repurposed. | +| 2.7M | 400 | 240 | Sharp | [Sharp][9d] | [Adafruit 4694][16m] | Micropower monochrome display. | +| 1.3M | 168 | 144 | Sharp | [Sharp][9d] | [Adafruit 3502][17m] | Ditto. | ## Displays using compatible drivers @@ -105,6 +106,7 @@ simple. See [this doc](./DRIVERS.md#7-writing-device-drivers) for details. [12d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#34-driver-for-ili9486 [13d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/drivers/sh1106/sh1106.py [14d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/drivers/ssd1306/ssd1306.py +[15d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#54-weact-studio-ssd1680-eink-displays [1m]: https://www.adafruit.com/product/684 [2m]: https://www.adafruit.com/product/1673 @@ -131,4 +133,5 @@ simple. See [this doc](./DRIVERS.md#7-writing-device-drivers) for details. [23m]: https://shop.pimoroni.com/products/pico-display-pack?variant=32368664215635 [24m]: https://www.adafruit.com/product/2050 [25m]: https://www.waveshare.com/wiki/Pico-ResTouch-LCD-3.5 +[26m]: https://aliexpress.com/item/1005004644515880.html diff --git a/DRIVERS.md b/DRIVERS.md index 144d689..20c3dce 100644 --- a/DRIVERS.md +++ b/DRIVERS.md @@ -1374,6 +1374,20 @@ Color values of 0 (white) to 3 (black) can explicitly be specified. ###### [Contents](./DRIVERS.md#contents) +## 5.4 WeAct Studio SSD1680 eInk Displays + +The driver supports the WeAct Studio SSD1680 2.9 inch 296*128 pixel +[display](https://github.com/WeActStudio/WeActStudio.EpaperModule) that uses the +[SSD1680 driver](https://github.com/WeActStudio/WeActStudio.EpaperModule/blob/master/Doc/SSD1680.pdf). + +This display lacks many features when compared to the ones from Waveshare, +two important examples are fast refresh and partial refresh. The big pro however is the price, +it costs half the money of the Waveshare 2.9in alternative. + +The driver is cross platform and supports landscape or portrait mode. To keep +the buffer size down (to 4736 bytes) there is no greyscale support. It should +be noted that WeAct Studio product page suggests to not update the display more frequently than every 180s. + # 6. EPD Asynchronous support The following applies to nano-gui. Under micro-gui the update mechanism is diff --git a/drivers/ssd1680/epd29_ssd1680.py b/drivers/ssd1680/epd29_ssd1680.py new file mode 100644 index 0000000..ad711e0 --- /dev/null +++ b/drivers/ssd1680/epd29_ssd1680.py @@ -0,0 +1,200 @@ +# Nanogui driver for WeAct Studio 2.9" Black and White ePaper display. +# This driver can be used with the 2.13" EPD from WeAct Studio or with +# other displays with the same driver however, on my tests changing the resolution +# may rise exception, but I manage to successfully use this driver with the smaller +# display without changing the resolution. +# [Display](https://github.com/WeActStudio/WeActStudio.EpaperModule) + +# EPD is subclassed from framebuf.FrameBuffer for use with Writer class and nanogui. + +# Released under the MIT license see LICENSE + +# Based on the following sources: +# https://github.com/peterhinch/micropython-nano-gui/blob/master/drivers/epaper/epd29.py +# https://github.com/hfwang132/ssd1680-micropython-drivers + +# You can run a demo for this driver by executing the demo script "epd29_sync.py" +# or "epd29_weactstudio.py" after changing landscape=False when creating "ssd" on color_setup.py + +import framebuf +import uasyncio as asyncio +from micropython import const +from time import sleep_ms, sleep_us, ticks_ms, ticks_us, ticks_diff +from gui.drivers.boolpalette import BoolPalette +from machine import lightsleep, Pin + + +class TimeoutError(Exception): + def __init__(self, msg): + super().__init__(msg) + + +class EPD(framebuf.FrameBuffer): + # A monochrome approach should be used for coding this. The rgb method ensures + # nothing breaks if users specify colors. + @staticmethod + def rgb(r, g, b): + return int((r > 127) or (g > 127) or (b > 127)) + + # Discard asyn: autodetect + def __init__(self, spi, cs, dc, rst_pin, busy, landscape=True): + self._spi = spi + self._cs = cs # Pins + self._dc = dc + self._rst = Pin(rst_pin, Pin.OUT, value=1) + self._rst_pin = rst_pin + self._busy = busy # Pin High if Busy + self._lsc = landscape + # ._as_busy is set immediately on start of task. Cleared + # when busy pin is physically 0. + self._as_busy = False + self.updated = asyncio.Event() + self.complete = asyncio.Event() + # Public bound variables required by nanogui. + # Dimensions in pixels as seen by nanogui (landscape mode). + self.width = 296 if landscape else 128 + self.height = 128 if landscape else 296 + # Other public bound variable. + # Special mode enables demos written for generic displays to run. + self.demo_mode = False + + self._buffer = bytearray(self.height * self.width // 8) + self._mvb = memoryview(self._buffer) + mode = framebuf.MONO_VLSB if landscape else framebuf.MONO_HLSB + self.palette = BoolPalette(mode) + super().__init__(self._buffer, self.width, self.height, mode) + self.init() + + def _command(self, command, data=None): + self._dc(0) + self._spi.write(command) + self._dc(1) + if data is not None: + self._data(data) + + def _data(self, data, buf1=bytearray(1)): + for b in data: + buf1[0] = b + self._spi.write(buf1) + + def hw_reset(self): + self._rst(1) + sleep_ms(200) + self._rst(0) + sleep_ms(200) + self._rst(1) + self.wait_until_ready() + # print("hardware reset successful") + + def init(self): + # Hardware reset + self.hw_reset() + + self._dc(1) + self._cs(0) + + # Initialisation + cmd = self._command + + # Software Reset + # print("software resetting...") + # cmd(b'\x12') + # self.wait_until_ready() + # print("software reset successful") + + # deriver output control + cmd(b'\x01', b'\x27\x01\x01') + # data entry mode + cmd(b'\x11', b'\x01') + # set ram-x addr start/end pos + cmd(b'\x44', b'\x00\x0F') + # set ram-y addr start/end pos + cmd(b'\x45', b'\x27\x01\x00\x00') + # border waveform + cmd(b'\x3c', b'\x05') + # display update control + cmd(b'\x21', b'\x00\x80') + # set ram-x addr cnt to 0 + cmd(b'\x4e', b'\x00') + # set ram-y addr cnt to 0x127 + cmd(b'\x4F', b'\x27\x01') + + # set to use internal temperature sensor + cmd(b'\x18', b'\x80') + + ''' + # read from internal temperature sensor + self._dc(0) + self._spi.write(b'\x1B') + print(self._spi.read(2)) + self._dc(1) + print(self._spi.read(2)) + ''' + + self.wait_until_ready() + # print('Init Done.') + + # For use in synchronous code: blocking wait on ready state. + def wait_until_ready(self): + sleep_ms(50) + while not self.ready(): + sleep_ms(100) + + # Return immediate status. Pin state: 1 == busy. + def ready(self): + return not (self._as_busy or (self._busy() == 1)) + + # draw the current frame memory. + def show(self, buf1=bytearray(1), + fast_refresh=False, # USELESS for this driver, doesn't work well at all + deepsleep_after_refresh=False, + lightsleep_while_waiting_for_refresh=False): + if not self.ready(): + # Hardware reset to exit deep sleep mode + self.hw_reset() + + mvb = self._mvb + cmd = self._command + dat = self._data + + cmd(b'\x24') + if self._lsc: # Landscape mode + wid = self.width + tbc = self.height // 8 # Vertical bytes per column + iidx = wid * (tbc - 1) # Initial index + idx = iidx # Index into framebuf + vbc = 0 # Current vertical byte count + hpc = 0 # Horizontal pixel count + for _ in range(len(mvb)): + buf1[0] = ~mvb[idx] + dat(buf1) + idx -= wid + vbc += 1 + vbc %= tbc + if not vbc: + hpc += 1 + idx = iidx + hpc + else: + for b in mvb: + buf1[0] = ~b + dat(buf1) + + if fast_refresh: + cmd(b'\x22', b'\xFF') + else: + cmd(b'\x22', b'\xF7') + sleep_us(20) + cmd(b'\x20') # DISPLAY_REFRESH + + if lightsleep_while_waiting_for_refresh: + # set Pin hold=True is needed before entering lightsleep and after you must revert it back to hold=False + # without this, entering lightsleep results in a low state on the reset pin, and this resets the driver + self._rst = Pin(self._rst_pin, Pin.OUT, value=1, hold=True) + lightsleep(3000) # can be used to lowering consumption on ESP32 + self._rst = Pin(self._rst_pin, Pin.OUT, value=1, hold=False) + + self.wait_until_ready() + if deepsleep_after_refresh: + cmd(b'\x10', b'\x01') + else: + cmd(b'\x10', b'\x00') diff --git a/gui/demos/epd29_weactstudio.py b/gui/demos/epd29_weactstudio.py new file mode 100644 index 0000000..d1aef37 --- /dev/null +++ b/gui/demos/epd29_weactstudio.py @@ -0,0 +1,271 @@ +import utime +from time import time +from math import exp +# import uos +from color_setup import ssd +# On a monochrome display Writer is more efficient than CWriter. +from gui.core.writer import Writer +from gui.core.nanogui import refresh, circle +from gui.widgets.label import Label + +# Fonts +import gui.fonts.arial35 as font_big +import gui.fonts.freesans20 as font_mid +import gui.fonts.arial10 as font_small + +energy_meter_data_topic = "EnergyMeter" +weather_out_data_topic = "WeatherOut" +alert_thereshold = 30 # in minutes + +prev_data_from_bridge = {energy_meter_data_topic: {"data": None, "timestamp": None}, + weather_out_data_topic: {"data": None, "timestamp": None}} + +in_data = {"co2": 689, "temperature": 25.5, "humidity": 54.12} + +prev_data_from_bridge[energy_meter_data_topic]["data"] = {"power": 7.212, "voltage": 209.2} +prev_data_from_bridge[weather_out_data_topic]["data"] = {"temperature": 31.545, "humidity": 72.45} + +in_temp = in_data["temperature"] +in_temp_int = str(int(in_temp)) +in_temp_float = round(float(in_temp), 1) +in_temp_float = f'{in_temp_float:.1f}' +in_hum = str(int(in_data["humidity"])) +co2_val = str(int(in_data["co2"])) +pos = len(co2_val) - 1 +sfasamento = 10 * pos + +sfasamento_power = 10 +power_unit = "W" +power_kw = -1 + +if prev_data_from_bridge[energy_meter_data_topic]["data"]: + try: + power_kw = round(prev_data_from_bridge[energy_meter_data_topic]["data"]["power"], 2) + volts = str(int(prev_data_from_bridge[energy_meter_data_topic]["data"]["voltage"])) + if prev_data_from_bridge[energy_meter_data_topic]["data"]["power"] < 1: + power = str(int(prev_data_from_bridge[energy_meter_data_topic]["data"]["power"] * 1000)) + power_unit = "W" + else: + power = f'{power_kw:.2f}' + power_unit = "kW" + sfasamento_power = 0 + except: + power = "- - -" + volts = "- - -" +else: + power = "- - -" + volts = "- - -" + +if prev_data_from_bridge[weather_out_data_topic]["data"]: + try: + out_temp = round(float(prev_data_from_bridge[weather_out_data_topic]["data"]["temperature"]), 1) + out_temp = f'{out_temp:.1f}' + out_hum = str(int(prev_data_from_bridge[weather_out_data_topic]["data"]["humidity"])) + except: + out_temp = " - - -" + out_hum = "- -" +else: + out_temp = " - - -" + out_hum = "- -" + +out_old_data = False +if prev_data_from_bridge[energy_meter_data_topic]["timestamp"] and \ + time() - prev_data_from_bridge[energy_meter_data_topic]["timestamp"] > 60 * alert_thereshold: + out_old_data = True + +energy_old_data = False +if prev_data_from_bridge[weather_out_data_topic]["timestamp"] and \ + time() - prev_data_from_bridge[weather_out_data_topic]["timestamp"] > 60 * alert_thereshold: + energy_old_data = True + +# --------- OUT TEMPERATURE AND HUMIDITY --------- + +out_line_sep_pos = -1 +ssd.line(0, out_line_sep_pos, ssd.width, out_line_sep_pos, 1) + +label_out_pos = [out_line_sep_pos + 7, 5] +out_temp_pos = [out_line_sep_pos + 15, ssd.width - 85] +out_hum_pos = [out_temp_pos[0] - 1, ssd.width - 28] + +# ssd.text(str(co2_val), 49+sfasamento, v_align_center, 1) +wri = Writer(ssd, font_small, verbose=False) +Label(wri, label_out_pos[0], label_out_pos[1], 'OUT', bdcolor=None) + +if out_old_data: + ssd.fill_rect(label_out_pos[1] + 23, label_out_pos[0] - 2, 11, 14, 1) + ssd.fill_rect(label_out_pos[1] + 27, label_out_pos[0], 2, 6, 0) + ssd.fill_rect(label_out_pos[1] + 27, label_out_pos[0] + 8, 2, 2, 0) + +Writer.set_textpos(ssd, out_hum_pos[0], out_hum_pos[1]) +wri.printstring(out_hum + " %") + +Writer.set_textpos(ssd, out_temp_pos[0] + 8, out_temp_pos[1] + 43) +wri.printstring("C") +# gradi +circle(ssd, out_temp_pos[1] + 41, out_temp_pos[0] + 8, 1, 1) + +wri = Writer(ssd, font_mid, verbose=False) +Writer.set_textpos(ssd, out_temp_pos[0], out_temp_pos[1]) +wri.printstring(out_temp) + +# --------- IN TEMPERATURE AND HUMIDITY --------- + +in_line_sep_pos = out_line_sep_pos + 43 +ssd.line(0, in_line_sep_pos, ssd.width, in_line_sep_pos, 1) + +# label_in_pos = [label_out_pos[0]+58, 5] +label_in_pos = [in_line_sep_pos + 6, 5] +in_temp_pos = [label_in_pos[0] + 18, ssd.width - 103] +in_hum_pos = [label_in_pos[0] + 1, ssd.width - 35] + +# ssd.text(str(co2_val), 49+sfasamento, v_align_center, 1) +wri = Writer(ssd, font_small, verbose=False) +Label(wri, label_in_pos[0], label_in_pos[1], ' IN ', bdcolor=None) + +Writer.set_textpos(ssd, in_hum_pos[0] + 8, in_hum_pos[1] + 22) +wri.printstring("%") + +wri = Writer(ssd, font_big, verbose=False) +Writer.set_textpos(ssd, in_temp_pos[0], in_temp_pos[1]) +wri.printstring(in_temp_int) + +wri = Writer(ssd, font_mid, verbose=False) +Writer.set_textpos(ssd, in_hum_pos[0], in_hum_pos[1]) +wri.printstring(in_hum) + +Writer.set_textpos(ssd, in_temp_pos[0] + 11, in_temp_pos[1] + 42) +wri.printstring("." + in_temp_float.split(".")[1] + " C") +# gradi +circle(ssd, in_temp_pos[1] + 64, in_temp_pos[0] + 13, 2, 1) + +# --------- IN CO2 --------- +co2_rect_pos = [5, in_temp_pos[0] + 38] +co2_rect_height = 105 +v_align_co2_center = co2_rect_pos[1] + int(co2_rect_height / 2) - 1 + +ssd.rect(co2_rect_pos[0], co2_rect_pos[1], ssd.width - (co2_rect_pos[0] * 2), co2_rect_height, 1) + +# ssd.line(co2_rect_pos[0], v_align_co2_center, ssd.width-co2_rect_pos[0], v_align_co2_center, 1) + +co2_pos = [v_align_co2_center - int(co2_rect_height / 2) + 7, int(ssd.width / 2) - 18] +Writer.set_textpos(ssd, co2_pos[0], co2_pos[1]) +wri.printstring("CO") +wri = Writer(ssd, font_small, verbose=False) +Writer.set_textpos(ssd, co2_pos[0] + 10, co2_pos[1] + 30) +wri.printstring("2") + +Writer.set_textpos(ssd, v_align_co2_center + 28, int(ssd.width / 2) - 9) +wri.printstring("ppm") + +wri = Writer(ssd, font_big, verbose=False) +Writer.set_textpos(ssd, v_align_co2_center - 13, int(ssd.width / 2) - 10 - sfasamento) +wri.printstring(co2_val) + +# ssd.line(0, label_in_pos[0]+53, ssd.width, label_in_pos[0]+53, 1) + +co2_scale_height = 10 +co2_scale_borders = 5 +co2_scale_pos = [co2_rect_pos[0], co2_rect_pos[1] + co2_rect_height - co2_scale_height] +co2_scale_width = ssd.width - (co2_rect_pos[0] * 2) - (co2_scale_borders * 2) + +ssd.rect(co2_scale_pos[0] + co2_scale_borders, co2_scale_pos[1], co2_scale_width, co2_scale_height, 1) + +co2_line_mid = 6 / 10 +co2_line_high = 9 / 10 + +co2_x_scale_width = 0 +co2_val = int(co2_val) +if 400 <= co2_val <= 1500: + co2_x_scale = (co2_val - 400) / 1100 + co2_x_scale_width = int((co2_scale_width * (co2_line_mid)) * co2_x_scale) + ssd.fill_rect(co2_scale_pos[0] + co2_scale_borders, co2_scale_pos[1] + 3, co2_x_scale_width, co2_scale_height - 6, + 1) +else: + co2_x_scale = ((co2_val - 1500) / 1350) + co2_x_scale = 1 - exp(-co2_x_scale) + co2_x_scale_width = int(co2_scale_width * (co2_line_mid)) + int( + (co2_scale_width * (1 - co2_line_mid)) * co2_x_scale) + ssd.fill_rect(co2_scale_pos[0] + co2_scale_borders, co2_scale_pos[1] + 3, co2_x_scale_width, co2_scale_height - 6, + 1) + +co2_scale_perc = co2_x_scale_width / co2_scale_width +if co2_scale_perc < co2_line_mid: + sep_co2_line_mid = co2_scale_height - 8 + sep_co2_line_high = co2_scale_height - 6 + sep_co2_line_mid_z = - 4 + sep_co2_line_high_z = - 3 + +elif co2_line_mid <= co2_scale_perc < co2_line_high: + sep_co2_line_mid = co2_scale_height - 4 + sep_co2_line_high = co2_scale_height - 6 + sep_co2_line_mid_z = - 2 + sep_co2_line_high_z = - 3 + +elif co2_scale_perc >= co2_line_high: + sep_co2_line_mid = co2_scale_height - 4 + sep_co2_line_high = sep_co2_line_mid + sep_co2_line_mid_z = - 2 + sep_co2_line_high_z = - 2 + +ssd.fill_rect(co2_rect_pos[0] + co2_scale_borders + int(co2_scale_width * (co2_line_mid)), + co2_rect_pos[1] + co2_rect_height - sep_co2_line_mid + sep_co2_line_mid_z, 1, sep_co2_line_mid, 1) + +ssd.fill_rect(co2_rect_pos[0] + co2_scale_borders + int(co2_scale_width * (co2_line_high)), + co2_rect_pos[1] + co2_rect_height - sep_co2_line_high + sep_co2_line_high_z, 1, sep_co2_line_high, 1) + +# --------- ENERGY METER --------- + +energy_line_sep_pos = co2_scale_pos[1] + co2_scale_height + 12 +ssd.line(0, energy_line_sep_pos, ssd.width, energy_line_sep_pos, 1) + +label_energy_pos = [energy_line_sep_pos + 7, 5] +power_pos = [label_energy_pos[0] + 22, co2_rect_pos[0] + 6] +volts_pos = [label_energy_pos[0], ssd.width - 48] + +# ssd.text(str(co2_val), 49+sfasamento, v_align_center, 1) +wri = Writer(ssd, font_small, verbose=False) +Label(wri, label_energy_pos[0], label_energy_pos[1], 'Energy', bdcolor=None) + +if energy_old_data: + ssd.fill_rect(label_energy_pos[1] + 36, label_energy_pos[0] - 2, 11, 14, 1) + ssd.fill_rect(label_energy_pos[1] + 40, label_energy_pos[0], 2, 6, 0) + ssd.fill_rect(label_energy_pos[1] + 40, label_energy_pos[0] + 8, 2, 2, 0) + +Writer.set_textpos(ssd, volts_pos[0] + 8, volts_pos[1] + 35) +wri.printstring("V") + +wri = Writer(ssd, font_big, verbose=False) +Writer.set_textpos(ssd, power_pos[0], power_pos[1] + sfasamento_power) +wri.printstring(power) + +wri = Writer(ssd, font_mid, verbose=False) +Writer.set_textpos(ssd, volts_pos[0], volts_pos[1]) +wri.printstring(volts) + +Writer.set_textpos(ssd, power_pos[0] + 11, power_pos[1] + 75) +wri.printstring(power_unit) + +# power slider +energy_scale_height = 9 +energy_scale_borders = 5 +energy_scale_pos = [co2_rect_pos[0], ssd.height - energy_scale_height] +energy_scale_width = co2_scale_width +# energy_scale_width = ssd.width - (energy_scale_borders*2) + +ssd.rect(energy_scale_pos[0] + energy_scale_borders, energy_scale_pos[1], energy_scale_width, energy_scale_height, 1) + +if power_kw != -1: + if power_kw >= 7.7: + enery_x_scale = 1 + else: + enery_x_scale = power_kw / 7.7 + enery_x_scale_width = int(energy_scale_width * enery_x_scale) + ssd.fill_rect(energy_scale_pos[0] + co2_scale_borders, energy_scale_pos[1] + 3, enery_x_scale_width, + energy_scale_height - 6, 1) + +# ssd.line(int(ssd.width/2), 0, int(ssd.width/2), ssd.height, 1) +# ssd.line(0, int(ssd.height/2), ssd.width, int(ssd.height/2), 1) + +ssd.rect(0, 0, ssd.width, ssd.height, 1) + +ssd.show() diff --git a/setup_examples/ssd1680_esp32.py b/setup_examples/ssd1680_esp32.py new file mode 100644 index 0000000..f9ad68e --- /dev/null +++ b/setup_examples/ssd1680_esp32.py @@ -0,0 +1,22 @@ +# WIRING +# 15 BUSY +# 4 DC +# 2 Rst +# 5 CS +# 23 SDA +# 18 SCL + +from machine import Pin, SPI +import gc + +# *** Choose your color display driver here *** +from drivers.ssd1680.epd29_ssd1680 import EPD as SSD + +dc = Pin(4, Pin.OUT, value=0) +rst_pin = 2 +cs = Pin(5, Pin.OUT, value=1) +busy = Pin(15, Pin.IN) + +spi = SPI(1, baudrate=10000000, sck=Pin(18), mosi=Pin(23)) +gc.collect() # Precaution before instantiating framebuf +ssd = SSD(spi, cs, dc, rst_pin, busy, landscape=True)