Electronic Ink and Raspberry Pi
Electronic ink
As a solution to this problem with autonomous power supply, you can try using so-called electronic ink (eInk). The main advantage of screens operating on this principle is that they consume energy only when the state of the pixels on the screen changes. Simply put, if the picture or text on the screen remains static, then practically no current is consumed.
In this article we will focus on the issue of minimizing energy consumption when using screens. Actually, the technology of electronic ink should be well known to everyone from e-books, in which the image remains on the display even when the power is completely turned off. At the same time, the image also has high contrast and is easily readable in daylight. It really looks like printing paper.
A few years ago I built this device based on Raspberry Pi Zero and an Adafruit eInk 2.13 display.
This microcomputer scanned Wi-Fi and tried to connect to the open networks it found. If it was possible to connect, the network name, connection time, IP settings – everything was displayed on a small display. Due to the small form factor of the device, the battery was also not very powerful, about 2000 mAh, which was enough for a couple of hours of operation of the device.
What is there now
Technical progress does not stand still, and if earlier electronic ink was only two-color, now three-color options have appeared.
As an example, in my article I will look at Adafruit products. Their screens can be purchased at a reasonable price on a well-known Chinese site, and some Russian stores also sell them.
Tri-color displays have black and red ink pixels and a white background. The Adafruit line includes a 212 x 104 tri-color display (the older, lower resolution screen) and a 250 x 122 tri-color display (the new, higher resolution screen). The monochrome (black and white) display has a resolution of 250×122 black pixels on a white background.
The 2.13 Monochrome Flexible Display has a lower resolution of 212×104, but is flexible. Such a display can be bent, although how much this is necessary in practice is an open question.
Fly in the ointment
One thing to keep in mind when working with these small e-ink screens is that they are very slow compared to OLED, TFT, or even memory displays. It may take more than a second to completely erase and replace an image. There is also a recommended refresh limit – you should not refresh or change the display more than every 3 minutes (180 seconds).
The developer also does not recommend refreshing the image frequently, but on tri-color displays the large red dots will gradually increase in size and the display background will turn pinkish rather than white. To keep the background color clear and pale, update it once a day
Well, now that we have learned the main advantages and disadvantages of electronic ink, we can safely move on to consider working with these displays.
Pinout
It is proposed to use Raspberry Pi 2/3/4 as the core of the device, although according to the developers, Arduino is also supported. The development of any smart device consists of assembling the hardware part (in our case, connecting the display to a microcomputer) and the software part (in our case, this will be Python code).
Let's start with the connection diagram. According to information from the vendor, presented in the figure below, the display has the following set of pins:
We don't need all the pins. On the Raspberry side we will connect as follows:
Raspberry Pi 3.3 to display VIN
Raspberry Pi GND to display GND
Raspberry Pi SCLK to display SCK
Raspberry Pi MOSI to display MOSI
Raspberry Pi GPIO CE0 to display ECS
Raspberry Pi GPIO 22 to display D/C
Raspberry Pi GPIO 27 to display RST
Raspberry Pi GPIO 17 to display BUSY
Or in more understandable language:
With the hardware, I think everything is clear, now let’s move on to the software.
Preparing the software
You will need to install the Adafruit_Blink library, which provides Circuit Python support in Python. To do this, do the following:
sudo pip3 install adafruit-circuitpython-epd
If pip3 is not installed, then first run:
sudo apt-get install python3-pip
Also, to work with the library you will need a font file. You can download using the command:
wget https://github.com/adafruit/Adafruit_CircuitPython_framebuf/raw/main/examples/font5x8.bin
Before you continue, make sure that the folder you are running the scripts from contains the font 5×8.bin file.
You may also need PIL (Python Imaging Library), a Python imaging library that allows you to use graphics and text with custom fonts. There are several system libraries that PIL relies on, so installing through a package manager is the easiest way to install:
sudo apt-get install python3-pil
Now we can look at some examples. Let's try clearing the screen buffer and drawing some shapes.
At the beginning there is an initialization block:
import digitalio
import busio
import board
from adafruit_epd.epd import Adafruit_EPD
from adafruit_epd.il0373 import Adafruit_IL0373
from adafruit_epd.il91874 import Adafruit_IL91874 # pylint: disable=unused-import
from adafruit_epd.il0398 import Adafruit_IL0398 # pylint: disable=unused-import
from adafruit_epd.ssd1608 import Adafruit_SSD1608 # pylint: disable=unused-import
from adafruit_epd.ssd1675 import Adafruit_SSD1675 # pylint: disable=unused-import
from adafruit_epd.ssd1680 import Adafruit_SSD1680 # pylint: disable=unused-import
from adafruit_epd.ssd1681 import Adafruit_SSD1681 # pylint: disable=unused-import
from adafruit_epd.uc8151d import Adafruit_UC8151D # pylint: disable=unused-import
from adafruit_epd.ek79686 import Adafruit_EK79686 # pylint: disable=unused-import
# здесь мы прописываем нужные пины:
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
ecs = digitalio.DigitalInOut(board.D12)
dc = digitalio.DigitalInOut(board.D11)
srcs = digitalio.DigitalInOut(board.D10) # can be None to use internal memory
rst = digitalio.DigitalInOut(board.D9) # can be None to not use this pin
busy = digitalio.DigitalInOut(board.D5) # can be None to not use this pin
# указываем нужный дисплей
print("Creating display")
# display = Adafruit_SSD1608(200, 200, # 1.54" HD mono display
# display = Adafruit_SSD1675(122, 250, # 2.13" HD mono display
# display = Adafruit_SSD1680(122, 250, # 2.13" HD Tri-color display
# display = Adafruit_SSD1681(200, 200, # 1.54" HD Tri-color display
# display = Adafruit_IL91874(176, 264, # 2.7" Tri-color display
# display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display
# display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display
# display = Adafruit_UC8151D(128, 296, # 2.9" mono flexible display
# display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display
# display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display
display = Adafruit_IL0373(
104,
212, # 2.13" Tri-color display
spi,
cs_pin=ecs,
dc_pin=dc,
sramcs_pin=srcs,
rst_pin=rst,
busy_pin=busy,
)
# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY uncomment these lines!
# display.set_black_buffer(1, False)
# display.set_color_buffer(1, False)
# IF YOU HAVE A 2.9" FLEXIBLE DISPLAY uncomment these lines!
# display.set_black_buffer(1, True)
# display.set_color_buffer(1, True)
display.rotation = 1
# очищаем буфер
print("Clear buffer")
display.fill(Adafruit_EPD.WHITE)
display.pixel(10, 100, Adafruit_EPD.BLACK)
# рисуем различные геометрические фигуры
print("Draw Rectangles")
display.fill_rect(5, 5, 10, 10, Adafruit_EPD.RED)
display.rect(0, 0, 20, 30, Adafruit_EPD.BLACK)
print("Draw lines")
display.line(0, 0, display.width - 1, display.height - 1, Adafruit_EPD.BLACK)
display.line(0, display.height - 1, display.width - 1, 0, Adafruit_EPD.RED)
display.display()
To display text we use the following code:
print("Draw text")
display.text("hello world", 25, 10, Adafruit_EPD.BLACK)
display.display()
Drawing a picture
In principle, based on the above example with graphics, we can draw a lot of interesting things, but if we already have a ready-made picture, then it can also be easily displayed. Please note that any .bmp image you wish to display must be exactly the size of your display. We will use the image below on a 1.54 inch display.
In order not to overload the article with code, I will provide only part of the code. The initial block associated with pins and initialization can be taken from the previous example.
…
display.rotation = 0
FILENAME = "blinka.bmp"
def read_le(s):
# as of this writting, int.from_bytes does not have LE support, DIY!
result = 0
shift = 0
for byte in bytearray(s):
result += byte << shift
shift += 8
return result
class BMPError(Exception):
pass
def display_bitmap(epd, filename): # pylint: disable=too-many-locals, too-many-branches
try:
f = open(filename, "rb") # pylint: disable=consider-using-with
except OSError:
print("Couldn't open file")
return
print("File opened")
try:
if f.read(2) != b"BM": # check signature
raise BMPError("Not BitMap file")
bmpFileSize = read_le(f.read(4))
f.read(4) # Read & ignore creator bytes
bmpImageoffset = read_le(f.read(4)) # Start of image data
headerSize = read_le(f.read(4))
bmpWidth = read_le(f.read(4))
bmpHeight = read_le(f.read(4))
flip = True
print(
"Size: %d\nImage offset: %d\nHeader size: %d"
% (bmpFileSize, bmpImageoffset, headerSize)
)
print("Width: %d\nHeight: %d" % (bmpWidth, bmpHeight))
if read_le(f.read(2)) != 1:
raise BMPError("Not singleplane")
bmpDepth = read_le(f.read(2)) # bits per pixel
print("Bit depth: %d" % (bmpDepth))
if bmpDepth != 24:
raise BMPError("Not 24-bit")
if read_le(f.read(2)) != 0:
raise BMPError("Compressed file")
print("Image OK! Drawing...")
rowSize = (bmpWidth * 3 + 3) & ~3 # 32-bit line boundary
for row in range(bmpHeight): # For each scanline...
if flip: # Bitmap is stored bottom-to-top order (normal BMP)
pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize
else: # Bitmap is stored top-to-bottom
pos = bmpImageoffset + row * rowSize
# print ("seek to %d" % pos)
f.seek(pos)
rowdata = f.read(3 * bmpWidth)
for col in range(bmpWidth):
b, g, r = rowdata[3 * col : 3 * col + 3] # BMP files store RGB in BGR
if r < 0x80 and g < 0x80 and b < 0x80:
epd.pixel(col, row, Adafruit_EPD.BLACK)
elif r >= 0x80 and g >= 0x80 and b >= 0x80:
pass # epd.pixel(row, col, Adafruit_EPD.WHITE)
elif r >= 0x80:
epd.pixel(col, row, Adafruit_EPD.RED)
except OSError:
print("Couldn't read file")
except BMPError as e:
print("Failed to parse BMP: " + e.args[0])
finally:
f.close()
print("Finished drawing")
# clear the buffer
display.fill(Adafruit_EPD.WHITE)
display_bitmap(display, FILENAME)
display.display()
As a result, something like this will appear on the display:
Conclusion
Unfortunately, some of the materials on the vendor's website are only available when connected via a VPN, but the training examples should be available that way. But, in general, I believe that the main issues related to the use of eInk were discussed in sufficient detail in the article.
In conclusion, I’ll tell you about the open lesson “Radio signal filtering”, which will be held as part of the Otus “Electronics and Electrical Engineering” course on June 19. Here you will master the theoretical foundations and practical aspects of the development and use of radio frequency filters. The knowledge gained will help you in modeling and creating effective filtering circuits to improve the performance of radio systems. Sign up using the link.