Waveshare Python Library

in utopian-io •  7 years ago  (edited)

hello.png

This post covers changes beginning with revision #5888c7a to revision #f2d510fd.

Github Project Page.

New Features

  • added commands for drawing triangles
  • added commands for drawing filled triangles
  • added commands for drawing images
  • added unit tests for all of the new features (and went back and added a unit test for the text display command)
  • added an example script that displays ip addresses on the e-ink display
  • added license
  • added README

ip.py

I have a raspberry pi3 that sits on my network, but it doesn't always get the same IP, so I now have this display hooked up to it so that when it's IP changes I can read it for the new value. The ip.py script was added just for this.

Because this library is specific to the Pi3, I didn't feel it was necessary to make the ip.py script work under Windows. So when it fetches the IP addresses, it does so using the ip command. But let's back up a bit.

First, the script opens the device using a with clause and sends a handshake. In testing, the handshake doesn't seem to be necessary after the device has been reset, but depending on the state the device was left in the last time it was used, it's not a bad practice.

Early on, I didn't use a context manager and it was quite annoying. Successive calls to the device would behave erratically if the GPIO cleanup wasn't getting called, so I jammed that into the architecture pretty early on. In my Roadmap section below, I make reference to not handling responses from the device very well.

In this script, I made a method called wait_for_paper that is quite representative of how I use the display in my other scripts. The documentation doesn't say (or I haven't read far enough) whether it is even necessary to read data coming back from the device, so I generally just discared the replies unless I'm querying the device for information explicitly.

Next, the script gets a list of interfaces and related information via a get_ip_addresses method, I'll explain that here as there doesn't seem to be a standard python way of fetching such information:

def get_ip_addresses():
    '''
    Calls `ip -o -4 a` to get interface addresses.
    Returns a dictionary of { 'iface_num': num, 'iface_name': name, 'iface_addr': address }
    '''

    command_string = 'ip -o -4 a'
    command_object = Popen(command_string.split(), stdout=PIPE)
    results = command_object.stdout.read()
    pattern = r'\W*([0-9]+)\W*:\W*([a-z0-9]+)\W*inet\W*(([0-9]+\.?){4}/[0-9]+)'
    iface_num = 0 # offset 0 into the pattern is the interface number
    iface_name = 1 # offset 1 into the pattern is the interface name
    ip = 2 # offset 2 into the pattern is the interface address
    output = []

    for line in results.split('\n'):
        matches = re.match(pattern, line)
        if matches:
            groups = matches.groups()
            output.append(
                {
                    IFACE_NUM_KEY: groups[iface_num],
                    IFACE_NAME_KEY: groups[iface_name],
                    IFACE_ADDR_KEY: groups[ip]
                }
            )

    return output

This method uses Popen to run the ip command. The arguments passed are to keep each interface's information on one line and display only ipv4 addresses.

The method then loops over the command output and uses a regular expression to extract the interface number, the interface name, and the ip address. These details are then placed into a dictionary so that when the method returns it returns a list of dictionaries of the information that I care about.

Then the script loops over the results from the get_ip_addresses method and just calls the DisplayText command for each interface. The resulting output looks like this:

ip script

Once the writing to the screen is finished the with clause takes care of invoking the cleanup and the script exits.

New Project

This is a project that I originally started back when Utopian was getting started, so I had not mentioned it here on Utopian previously, and as I've recently made a bunch of improvements to it, it seemed like a good time to post on it.

When I purchased the waveshare e-ink display, I couldn't find other projects that already existed for working with it on RPi, so I started one. But it's also a somewhat notable implementation because I made the commands into objects so that they could be more easily queued, passed around, and even replayed. (As opposed to being methods off the paper object.)

Technology Stack

This is a project for controlling a physical display connected to a Raspberry Pi3, so it uses that physical display, a raspberry pi, and python to do all the heavy lifting.

The library makes use of the GPIO library under the covers for actually reading and writing the display. To make cleanup easier, the library provides an EPaper class that can be used with a context manager so that the GPIO libraries are closed appropriately when the object is disposed of. For example, in the script example ip.py, the display is opened using a with clause:

with EPaper('/dev/ttyAMA0') as paper:
    paper.send(Handshake())
    ...

Also, for convenience, the library supports using the command pattern, so any instructions that may be sent to the display can actually be stored in a list and fed to the display. For example:

commands = [ DrawCircle(100,100,100), DrawCircle(50,50,25), FillCircle(50,50,3), DrawCircle(150,50,25), FillCircle(150,50,3), DrawTriangle(100,100,110,110,90,110), FillCircle(100,150,10), RefreshAndUpdate() ]

with EPaper('/dev/ttyAMA0') as paper:
    for c in commands:
        paper.send(c)

result of snippet

All the commands that can be sent to the EPaper object extend a common base class. Some of the new commands are so simple, the only content the child needs to provide is the updated command identifier.

For example, the ClearScreen command implementation:

class ClearScreen(Command):
    '''
    From the wiki:
    Clear the screen with the background color.
    '''
    COMMAND = b'\x2e'

The base class does require that children handle formatting any arguments they need to send, for example, DrawTriangle:

class DrawTriangle(Command):
    '''
    From the wiki:
    Draw a tri-angle according to three given point coordinates.
    '''
    COMMAND = b'\x28'
    def __init__(self, x1, y1, x2, y2, x3, y3):
        super(DrawTriangle, self).__init__(self.COMMAND, struct.pack(">HHHHHH", x1, y1, x2, y2, x3, y3))

But then related child classes are still easy to implement, for example, the FillTriangle command:

class FillTriangle(DrawTriangle):
    '''
    From the wiki:
    Fill a tri-angle according to three given point coordinates.
    '''
    COMMAND = b'\x29'

Roadmap

It's not quite finished, but it's close! There are just a few more API calls to be added, for drawing lines and rectangles. There's also a bit of an issue with reading results from the display. I haven't found the best practices for pulling replies after telling the display to do something. So I generally just do a read when I've finished using the display and once the replies have been read off, I close it.

If anyone is familiar with packaging up python libraries for inclusion in things like PIP or you want to jump in and contribute, let me know! You can contact me on the steem network, the easiest way being to just reply to this post.

TODO:

  • implement remaining commands
  • package up for easy install or possibly PIP
  • methods that return a result need better handling for reading the result
  • documentation!
  • others?



Posted on Utopian.io - Rewarding Open Source Contributors

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Hey @vladimir-simovic, I just gave you a tip for your hard work on moderation. Upvote this comment to support the utopian moderators and increase your future rewards!

@not-a-bird, Upvote is the only thing I can support you.

Hey @not-a-bird I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x