Jump to content

E-Paper Bricklet Wert mit PIL ImageFont ausgeben


Recommended Posts

Hallo Zusammen

Ich hab echt den Filmriss. Wie kann ich auf dem E-Paper einen Wert mit einem TrueType Font Zeichnen?

Ich hab da mal das alte Oled Servo Beispiel angeschaut. Diese API hat allerdings noch eine Funktion new_window... darum gehts nicht zu adaptieren.

Beim neuen Oled Blicklet 2.0 gibt es das Servo Beispiel leider nicht mehr. Dort wurde auch die API eher in die Richtung des E-Ink Bricklet entwickelt.

Hier mal eine Codevariante die natürlich nicht läuft weil ich die Daten nicht in den E-Ink Buffer bekomme.

Vielleicht kann mir jemand helfen. Natürlich müss später noch die Aktualisierung gelöst werden. z.b schwarz überschreiben.. aber das kommt später.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

HOST = "localhost"
PORT = 4223
UID = "Lww" # Change XYZ to the UID of your E-Paper 296x128 Bricklet

WIDTH = 296 # Columns
HEIGHT = 128 # Rows

from tinkerforge.ip_connection import IPConnection
from tinkerforge.bricklet_e_paper_296x128 import BrickletEPaper296x128
from PIL import Image, ImageDraw, ImageFont

import math
import time

def draw_matrix(pixels):
    column_index = 0
    column = []

    for i in range(HEIGHT//8 - 1): # We use last 8 pixels for text
        for j in range(WIDTH):
            page = 0
            for k in range(8):
                if pixels[i*8 + k][j] == 1:
                    page |= 1 << k

            if len(column) <= column_index:
                column.append([])

            column[column_index].append(page)
            if len(column[column_index]) == HEIGHT:
                column_index += 1

        epaper.write_black_white(0, 0, WIDTH-1, HEIGHT-1, pixels)
        

    


if __name__ == "__main__":
    ipcon = IPConnection() # Create IP connection
    epaper = BrickletEPaper296x128(UID, ipcon) # Create device object

    ipcon.connect(HOST, PORT) # Connect to brickd
   
    while True:
        img = Image.new('1', (WIDTH, HEIGHT), 0)
        draw = ImageDraw.Draw(img)
        font = ImageFont.truetype("DejaVuSans.ttf", 25)
        draw.text((70, 22), "test", font=font, fill=1)
        data = img.load()
        pixel_matrix = [[False]*WIDTH for i in range(HEIGHT)]
        for x in range(WIDTH):
            for y in range(HEIGHT):
                pixel_matrix[y][x] = data[x, y] == 1

        draw_matrix(pixel_matrix)
        
        epaper.draw()

        time.sleep(0.04)

hier ein lauffähiger Code, falls jemand ein ähnliches Problem hat.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time
import logging as log
log.basicConfig(level=log.INFO)

from PIL import Image, ImageDraw, ImageFont
from tinkerforge.ip_connection import IPConnection
from tinkerforge.ip_connection import Error
from tinkerforge.bricklet_e_paper_296x128 import BrickletEPaper296x128
from tinkerforge.bricklet_air_quality import BrickletAirQuality

class AirQuality:
    HOST = "localhost"
    PORT = 4223

    ipcon = None
    ink = None
    air_quality = None

    def __init__(self):
        self.ipcon = IPConnection()

        self.t = 0
        self.h = 0
        self.p = 0
        self.ii = 0
        self.ia = ""
        self.i = -7
        self.init = True
        self.image = 0
        self.WIDTH = 296 # Columns
        self.HEIGHT = 128 # Rows
        self.color = 0

        while True:
            try:
                self.ipcon.connect(AirQuality.HOST, AirQuality.PORT)
                break
            except Error as e:
                log.error('Connection Error: ' + str(e.description))
                time.sleep(1)
            except socket.error as e:
                log.error('Socket error: ' + str(e))
                time.sleep(1)

        self.ipcon.register_callback(IPConnection.CALLBACK_ENUMERATE,
                                     self.cb_enumerate)
        self.ipcon.register_callback(IPConnection.CALLBACK_CONNECTED,
                                     self.cb_connected)

        # Enumerate Bricks and Bricklets (retry if not possible)
        while True:
            try:
                self.ipcon.enumerate()
                break
            except Error as e:
                log.error('Enumerate Error: ' + str(e.description))
                time.sleep(1)

    def draw_image(self, ink, start_column, start_row, column_count, row_count, image):
        image_data = image.load()
        pixels = []

        # Convert image pixels into 8bit pages
        for row in range(row_count):
            for column in range(column_count):
                pixels.append(image_data[column, row] != 0)

        self.ink.write_black_white(0, 0, self.WIDTH-1, self.HEIGHT-1, pixels)

    def cb_all_values(self, iaq_index, iaq_index_accuracy, temperature, humidity, air_pressure):
        if self.init == True:
            self.color = 1
            self.image = Image.new("1", (self.WIDTH, self.HEIGHT), 0)
            self.draw = ImageDraw.Draw(self.image)
            self.font = ImageFont.truetype("DejaVuSans.ttf", 60)
            self.ink_refresh()
            self.init = False
        elif self.color == 1:
            self.t = temperature/100
        
        self.draw.text((70, 22), str(self.t), font=self.font, fill=self.color)    
        self.draw_image(self.ink, 0, 0, self.WIDTH, self.HEIGHT, self.image)
        
        if self.ink.get_draw_status() == self.ink.DRAW_STATUS_IDLE:
            self.ink.draw()

        if self.color == 1:
            self.color = 0
        elif self.color == 0:
            self.color = 1
            
        
    def ink_refresh(self):
        self.ink.set_update_mode(self.ink.UPDATE_MODE_DEFAULT)
        self.ink.fill_display(self.ink.COLOR_BLACK)
        self.ink.draw()
        time.sleep(5)
        self.ink.set_update_mode(self.ink.UPDATE_MODE_DELTA)


    def cb_enumerate(self, uid, connected_uid, position, hardware_version,
                     firmware_version, device_identifier, enumeration_type):
        if enumeration_type == IPConnection.ENUMERATION_TYPE_CONNECTED or \
           enumeration_type == IPConnection.ENUMERATION_TYPE_AVAILABLE:
            if device_identifier == BrickletEPaper296x128.DEVICE_IDENTIFIER:
                try:    
                    self.ink = BrickletEPaper296x128(uid, self.ipcon)
                    log.info('Ink Display initialized')
                except Error as e:
                    log.error('Ink Display init failed: ' + str(e.description))
                    self.ink = None
            elif device_identifier == BrickletAirQuality.DEVICE_IDENTIFIER:
                try:
                    self.air_quality = BrickletAirQuality(uid, self.ipcon)
                    self.air_quality.set_all_values_callback_configuration(500, False)
                    self.air_quality.register_callback(self.air_quality.CALLBACK_ALL_VALUES,
                                                       self.cb_all_values)
                    log.info('Air Quality initialized')
                except Error as e:
                    log.error('Air Quality init failed: ' + str(e.description))
                    self.air_quality = None

    def cb_connected(self, connected_reason):
        # Eumerate again after auto-reconnect
        if connected_reason == IPConnection.CONNECT_REASON_AUTO_RECONNECT:
            log.info('Auto Reconnect')

            while True:
                try:
                    self.ipcon.enumerate()
                    self.ink_refresh()
                    break
                except Error as e:
                    log.error('Enumerate Error: ' + str(e.description))
                    time.sleep(1)

if __name__ == "__main__":
    print ("Start")
    refresh = 1000
    x = 18
    z = 7
    y0 = 8
    y1 = 128
    y = range(y0, y1, int(round((y1-y0)/z,0)))


    w = AirQuality()
    time.sleep(0.1)
    
    while True:
        time.sleep(0.1)

 

Edited by techniker
Link to comment
Share on other sites

Moin,

Die API des E-Paper-Bricklets erwartet nicht, dass du die Pixel in ints packst. Du kannst direkt ein Array von boolschen Werten übergeben, den Rest machen die Bindings. So sollte es funktionieren:

pixel_matrix = [False] * WIDTH * HEIGHT
for x in range(WIDTH):
  for y in range(HEIGHT):
    pixel_matrix[y * WIDTH + x] = data[x, y] == 1

epaper.write_black_white(0, 0, WIDTH - 1, HEIGHT - 1, pixel_matrix)
eppaper.draw()

Gruß,
Erik

Link to comment
Share on other sites

Abend,

Vielen Dank für das Code Beispiel. Obwohl ich bei der Formel [y * WIDTH + x] nicht recht verstehe was die macht.

Gibt es en Vorteil zu der append Funktion? Evtl. die Geschwindigkeit? Ich hab beide versucht und kein offensichtlicher unterschied feststellen können.

z.B. dies hier:

def draw_image(self, WIDTH, HEIGHT, image):
        data = image.load()
        pixels_matrix = []

        for x in range(HEIGHT):
            for y in range(WIDTH):
                pixels_matrix.append(data[y, x] != 0)

        self.ink.write_black_white(0, 0, WIDTH-1, HEIGHT-1, pixels_matrix)

 

Link to comment
Share on other sites

Hier mal noch ein Zwischenstand. Das AirQuality Bricklet dient nur mal als Datenlieferant. Denkbar sind diverse Anwendungen um Werte oder ein Balkendiagramm anzuzeigen.

Der Timer kann auf 1000 gesetzt werden. Sobald dieser erreicht ist wird ein Refresh ausgelöst. default mode > fill.black > draw > delta mode, evtl. auch die Farbe rot möglich je nach Einbrenn-Problematik was sich noch zeigen wird.

Beste Grüsse

ink.jpg

Link to comment
Share on other sites

Moin,

12 hours ago, techniker said:

Obwohl ich bei der Formel [y * WIDTH + x] nicht recht verstehe was die macht.

Gibt es en Vorteil zu der append Funktion? Evtl. die Geschwindigkeit? Ich hab beide versucht und kein offensichtlicher unterschied feststellen können.

Da geht es tatsächlich nur um die Geschwindigkeit, wenn du die Liste leer anlegst und immer neue Daten appendest, muss Python ziemlich oft intern eine neue (größere) Liste anlegen und die Daten kopieren. Das ist aber bei der relativ kleinen Problemgröße (und der Tatsache, dass auf das E-Paper zeichnen im Vergleich dazu ewig dauert) vermutlich egal.

Die Formel benutze ich zur Umrechnung von (x,y)-Koordinaten in den eindimensionalen Index. Das hilft immer sich es als Bild vorzustellen, so wie hier zum Beispiel (Stride und Width sind bei unserem Bildformat identisch). Im Endeffekt überspringe ich y vollständige Bildzeilen mit y * WIDTH, dann bin ich bei der aktuelle Bildzeile, das + x ist dann in der aktuellen Zeile die Spalte die ich schreiben will.

Weil mir das gerade noch aufgefallen ist: Du kannst die etwas verwirrende data[y,x] Vertauschung loswerden, wenn du die for-Schleifen-Indices umbenennst, x ist ja eigentlich die Spaltenkoordinate, y die Zeilenkoordinate.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...