Saturday, 20 February 2016

PaPiRus E-ink Display

Pi Gets 'Ink' Done

YotaPhone have already taken on this type of display in mobile technology and with phones only lasting about a day from full charge it's obvious why this market is looking for something new.
The low power (or no power) requirements of the display mean it only needs to change with new information.

The PaPiRus display brings this to the hands of the tinkers and future innovators.





Find the original kickstarter page at

Installation

I have a version 1.8 board which doesn't seem to be listed on the hardware version Excel sheet. I guess this quite new and from reading the message boards the documentation should be out very soon. I'm not a kickstarter backer so I'm surprised that this doesn't come with good user documentation. I hope this helps in the meantime to explain how the display works.

Get over to the git home page at
https://github.com/PiSupply/PaPiRus

This is the latest source for the Pi hat.

Prerequisites

Ensure the SPI is enabled
If you don't have it already get GIT and imaging library for Python

> sudo apt-get install git
> sudo apt-get install python-imaging

Get the fuse library if not already installed

> sudo apt-get install libfuse-dev

If you want to run some of the demos with Gratis and you don't have fonts installed then run

> sudo apt-get install fonts-freefont-ttf

Hardware

All screens connect up in the same way. Just lift the plastic connector and insert the ribbon. Push down the connector to secure in place.

The screen folds over the back of the board so ensure you have it the right way around before inserting





Getting Python code and installing

You will need to download the latest source for the PaPiRus

> git clone https://github.com/PiSupply/PaPiRus.git
> cd PaPiRus
> sudo python setup.py install    
> papirus-setup

Gratis is the beating heart of the setup. If you just want to get straight into the workings of the display then Gratis is all you really need.
https://github.com/repaper/gratis

Get the gratis driver
> git clone https://github.com/repaper/gratis.git

Note that the next lines are for the latest V321 G2 panel, but I guess this will be most new users
> cd gratis/PlatformWithOS
> make PANEL_VERSION=V231_G2 rpi-epd_fuse
> sudo make PANEL_VERSION=V231_G2 rpi-install
> sudo service epd-fuse start

The setup installs libfuse and fonts from fonts-freefont-ttf.
My first thoughts were, why do I need all this extra code baggage. If I'm using a display I usually just directly interface and I'm done.
It's not a tight kernel driver, but it works in user space world and an e-ink isn't going to complain about performance code issues.

Checking everything works

Fix broken code
> sudo nano /usr/local/lib/python2.7/dist-packages/papirus/text.py
Indent line 37 with an extra tab. Save back to original file

> sudo nano /usr/local/bin/papirus-write
Change line 39 to 
font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeMono.ttf', size)

Run 
> sudo papirus-config
Set screen size

The display should be good to go with the code now
> papirus-write "Test"

Should correctly write to e-ink display, if not check previous steps and SPI is enabled.

Run clock test
Fix the clock code by editing
> sudo nano /usr/local/bin/papirus-clock

Change 
from EPD import EPD
to
from papirus import Papirus as EPD

Run clock and you should see the time and date display. This refreshes every 5 seconds
> sudo papirus-clock



Included demos

PiSupply demo files

These were already run in the test above to check things are working. The install of these files puts all copies into /usr/local/bin

> papirus-write "Hello World"
> sudo papirus-clock
> sudo papirus-gol 2.7
> papirus-clear

You'll note that sudo is used on some of these examples. As far as I know this isn't really necessary as the main purpose of the epd_fuse service is to provide an interface in normal user land. Check out the gratis examples for clarity. 

Gratis python demonstration files

Python demonstration files exist in the PlatformWithOS/demo folder. These are further python scripts which can be run (ensure the epd-fuse service is running)

This is a full screen digital clock.
> python Clock27.py

Gratis C demonstration files

These files directly access the hardware and do not use the fuse service. This is actually to test the code in the service so strictly these are not examples or demos, but good to see.

The install doesn't build these by default so a further make command is needed.

Access the PlatformWithOS from within the gratis downloaded GIT repository.
> cd PlatformWithOS

For the 2.7 panel type:
> make PANEL_VERSION=V231_G2 rpi

Other older panel versions are V110_G1 and V230_G2, only use if you know you have these older panels.

Change directory to driver-common
> cd driver-common

Run the test file as root and specify the panel size.
> sudo ./epd_test 2.7

This loops through some test images which are good examples of display.
Root access is needed for this test file because it directly accesses the hardware.










Gratis driver

Fuse driver provides device files in /dev/epd directory. These can be seen when the epd service is running
> sudo /etc/init.d/epd-fuse start

The README.md file explains what the files do and if they are read or write

Example
> cat /dev/epd/version
Will give you the driver version

Whilst in the gratis driver-common directory (see example above) the epd_fuse service can also be called with the same test images without needing root. Note that it doesn't matter if you haven't built the epd_test file to do this. Actually any test XBM matching the screen dimensions can be used.

> ./xbm2bin < cat_2_7.xbm > /dev/epd/display
> echo U > /dev/epd/command

This switches to the cat image or any other image chosen in the test folder.

Sum up of the display

E-ink displays cannot be compared directly to other display types as the screen cannot rapidly change like an LCD or OLED. Images displayed are clear and sharp for text displays. Graphics require dithering because there's no grey scale which can be set.

I hope there's further improvements down the road for the driver and display. Rapid changes to the screen are possible but ghosting is a problem until a full screen refresh has been performed. 
Check out the game of life code to see ghosting examples
> sudo papirus-gol

When this has run for a few iterations there's clear ghosting on the screen. This isn't too bad and may be something that some smart folks who now have this display on their Pi can help improve. 

Driving the display is really easy with the existing software. Simple scripts can be used easily to set the screen. 

I can't really figure out what is going on with the 4 optional buttons that came with the display. I ran some simple GPIO code to see if they were just forwarded straight to the Pi pins but this doesn't seem to be the case. 
UPDATE - The buttons connect to GPIO pins 16, 26, 20 and 21. 

The real time clock and wake functions are not clear. I didn't install the pogo pin as my current case doesn't allow the connection through to the Pi board. There's no example code to show how it works so I'm hoping this appears soon.

Finally I'm not quite sure the temperature sensor feature actually works with the Pi. There's only snippets of information in the included documentation, but it seems to indicate that an ADC would be needed for the Pi and since this isn't possible out-of-the-box I can't see how it can be read. Primarily the temperature sensor is for the display to set the PWM. 

Overall it does what it should do. I'm looking forward to some cool ideas from the user community. I think a dedicated case to house the display and Pi would be great for applications.

Sunday, 7 February 2016

Sparkfun micro OLED

Sparkfun Micro OLED 64x48 Display

The 64x48 micro display from Sparkfun is a cheap and cheerful OLED display to add into a project. Adafruit also sell a  128x64 display using exactly the same SDD1306 display driver and gives you a bit more screen if you spend a few extra dollars. There's some excellent information on both sites to get you started, but both are geared towards Arduino libraries.

Micro OLED on Raspberry Pi

There are 3 ways the OLED can be hooked up using parallel, I2C and the default setup using SPI. SPI is used in the examples below.

SPI can be enabled using the raspi-config tool from the command line. If this is enabled then a /dev/spidev0.0 and /dev/spidev0.1 will be available. 

A entire post can be written on how to hookup and use SPI but this is well documented on many sites. Sparkfun has a good tutorial for SPI and I2C.

Micro OLED in action

A video to demonstrate output to the OLED display can be seen below. If a small fixed width font is used then a good number of text rows can be displayed. XBM images provide a quick format to import directly into a binary image as resources (C/C++).


SPI libraries

For Python there's an SPIDEV driver library, which is by far the easiest way to get connected up to the OLED. 
Run 
> sudo apt-get install python-spidev
to get 2.0 SPIDEV through Jessie or download the later version direct from the link above.

The SPIDEV library wraps code to talk to the kernel driver and provides a comprehensive set of functions to manage the connection. 
A simple set of attributes are exposed by the Python library to read/write settings
  • bits_per_word 
  • cshigh
  • lsbfirst
  • max_speed_hz
  • mode
Defaults should work fine but if you want you can adjust the max_speed_hz for your project. 500kHz seems to be the default speed on the Pi 2 and Pi Zero. This seems to be fast enough to run the display.

Connecting to the OLED

Example setup to connect to the OLED:

import RPi.GPIO as GPIO
import spidev
import time

spi = spidev.SpiDev()
spi.open(0,0)

GPIO.setmode(GPIO.BCM)

dc_pin    = 17 # data command pin
reset_pin = 27 # screen reset pin

GPIO.setup(dc_pin, GPIO.OUT)
GPIO.setup(reset_pin, GPIO.OUT)

This opens SPI and uses CE0 pin to address the display. Two additional GPIO pins are needed for the command type (DC) and reset pins. 17 and 27 are used in the example above, but you can use anything you like for these pins. 

Initializing the display

These steps are really important to get the display to do anything. If you read the SDD1306 spec you will see the steps are outlined as a reference, which is harder to understand how to implement for the 64x48 display. This is because the SDD1306 is actually a full 128x64 driver whereas the micro OLED is a cut down display. 

1. Pull DC high and set the reset pin low to formally reset the display. There needs to be a time period to reset the chip
GPIO.output(dc_pin, 1)
GPIO.output(reset_pin, 0)
time.sleep(0.1)

2. Enable the display, ensure command mode is set, then pull down and back up reset pin to get into a state for commands across SPI
GPIO.output(reset_pin, 1) # enable display
GPIO.output(dc_pin,0) # set to command mode
time.sleep(0.1) # wait 100ms (spec is for 5ms min)
GPIO.output(reset_pin, 0) # disable display
time.sleep(0.1) # wait 100ms (spec is for 10ms min)
GPIO.output(reset_pin, 1) # enable display

3. Display is set to initially off. Set the timing and multiplex. These are specific to the 64x48 display. 
spi.xfer([0xAE]) # display off command
spi.xfer([0xD5]) # display clock div
spi.xfer([0x80]) # set timing
spi.xfer([0xA8]) # set muliplex, 
spi.xfer([0x2F]) # note that this is specific to 64x48 displays

4. Set the display offsets. The Sparkfun is aligned to zero offsets in memory. Note this may not be the case for other implementations which use the same display driver.
spi.xfer([0xD3]) # set display offset
spi.xfer([0x0]) # com offset set to 0
spi.xfer([0x40 | 0x00]) # set startline offset to zero

5. Kick off the display
spi.xfer([0x8D]) # charge pump
spi.xfer([0x14]) # enable pump (disable with 0x00)
spi.xfer([0xA6]) # normal display
spi.xfer([0xA4]) # display all on resume

6. Next steps are for configuration. The examples here work with page addressing
spi.xfer([0x20]) # memory mode
spi.xfer([0x10]) # page address mode (10b for page, 01b for vertical)
spi.xfer([0xA0 | 0x01]) # seg remap 127 to SEG0. Rows are left to right from display ribbon
spi.xfer([0xC)]) # com scan descend. Basically flips the display from display connector ribbon
spi.xfer([0xC0]) # com scan ascend from display connector
spi.xfer([0xDA]) # set com pins
spi.xfer([0x12]) # A4 is 0 so sequential COM pin. A5 is 1 so enable com left/right remap
spi.xfer([0x81]) # set contrast
spi.xfer([0xCF]) # 00h to FFh contrast values.
spi.xfer([0xD9]) # set precharge
spi.xfer([0xF1]) # Phase 1: 1 dclk (max 15), Phase 2: 15 dclks (max 15)
spi.xfer([0xDB]) # set com deselect
spi.xfer([0x40]) # above 0.83 x Vcc for regulator output.
spi.xfer([0xAF]) # display on
time.sleep(0.1)

7. The display is now on and can be written to over SPI by setting DC high
GPIO.output(dc_pin, 1)

Drawing to the OLED

This isn't a particularly efficient method, but gives an example to show how the paged memory works and will allow you to write to the OLED memory buffer. There are 3 helper functions shown to make the code a bit more readable.

def WriteCommand(c):
        GPIO.output(dc_pin, 0)
        spi.xfer([c])
#end def

def WriteData(d):
        GPIO.output(dc_pin, 1)
        spi.xfer([d])
#end def

def SetColumnAddress(add):
        WriteCommand ((0x10 | add >> 4) + 0x02)
        WriteCommand (0x0F & add)
#end def

def DrawOLED(cx,cy):
        # 8 pixels are written on each SPI byte.
        # This is performed over 6 pages
        out = 0x00
        for y in range(0,6):
                SetColumnAddress(0)
                WriteCommand(0xB0 | y)
                for x in range(0, 64):
                        out = 0x00
                        if cx == x:
                                if cy >= y*8 and cy < (y+1)*8:
                                        out = 0x01 << (cy - (y*8))
                        WriteData(out)

Taking this into real projects

All the code shown are just examples and should be enough to expand for use in real projects. Code libraries can be built in C/C++ using the same underlying spidev calls via ioctl to the kernel driver.


My own code is a C++ library implementation making almost identical calls seen here. The main differences consist of an improved method using internal buffer in the code, which is written out to the OLED in a single full write of the display. This matches the Arduino code approach from Sparkfun. I may publish this at a later date as a useful Pi library for C++ development.