Skip to content

Commit e08660e

Browse files
authored
Merge pull request #807 from cogliano/patch-3
Added code.py
2 parents 1b40859 + 081c793 commit e08660e

1 file changed

Lines changed: 356 additions & 0 deletions

File tree

EInk_Autostereograms/code.py

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
import os
2+
import time
3+
import json
4+
import digitalio
5+
import busio
6+
import board
7+
import displayio
8+
import adafruit_imageload
9+
from analogio import AnalogIn
10+
from adafruit_epd.epd import Adafruit_EPD
11+
from adafruit_epd.il91874 import Adafruit_IL91874
12+
13+
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
14+
ecs = digitalio.DigitalInOut(board.D10)
15+
dc = digitalio.DigitalInOut(board.D9)
16+
srcs = digitalio.DigitalInOut(board.D8) # can be None to use internal memory
17+
led = digitalio.DigitalInOut(board.D13)
18+
led.direction = digitalio.Direction.OUTPUT
19+
20+
print("Creating display")
21+
display = Adafruit_IL91874(
22+
176,
23+
264,
24+
spi, # 2.7" Tri-color display
25+
cs_pin=ecs,
26+
dc_pin=dc,
27+
sramcs_pin=srcs,
28+
rst_pin=None,
29+
busy_pin=None,
30+
)
31+
32+
# read buttons from ePaper shield
33+
def read_buttons():
34+
btn = 1
35+
with AnalogIn(board.A3) as ain:
36+
reading = ain.value / 65535
37+
if reading > 0.75:
38+
btn = None
39+
if reading > 0.4:
40+
btn = 4
41+
if reading > 0.25:
42+
btn = 3
43+
if reading > 0.13:
44+
btn = 2
45+
btn = 1
46+
return btn
47+
48+
#pylint: disable-msg=too-many-nested-blocks
49+
#pylint: disable-msg=too-many-branches
50+
#pylint: disable-msg=too-many-statements
51+
# display bitmap file
52+
def display_bitmap(epd, filename):
53+
try:
54+
f = open("/" + filename, "rb")
55+
print("File opened")
56+
if f.read(2) != b"BM": # check signature
57+
raise BMPError("Not BitMap file")
58+
read_le(f.read(4)) # File size
59+
f.read(4) # Read & ignore creator bytes
60+
bmpImageoffset = read_le(f.read(4)) # Start of image data
61+
headerSize = read_le(f.read(4))
62+
bmpWidth = read_le(f.read(4))
63+
# convert width to 8 pixels per byte width
64+
bmpWidth = (bmpWidth + 7) // 8
65+
bmpHeight = read_le(f.read(4))
66+
# convert unsigned int to signed int in case there is a negative height
67+
if bmpHeight > 0x7FFFFFFF:
68+
bmpHeight = bmpHeight - 4294967296
69+
flip = True
70+
if bmpHeight < 0:
71+
bmpHeight = abs(bmpHeight)
72+
flip = False
73+
print(
74+
"Image offset: %d\nHeader size: %d"
75+
% (bmpImageoffset, headerSize)
76+
)
77+
print("Width: %d\nHeight: %d" % (bmpWidth, bmpHeight))
78+
if read_le(f.read(2)) != 1:
79+
raise BMPError("Not singleplane")
80+
if read_le(f.read(2)) != 1: # bits per pixel
81+
raise BMPError("Not 1-bit")
82+
if read_le(f.read(4)) != 0:
83+
raise BMPError("Compressed file not supported")
84+
read_le(4) # SizeImage
85+
read_le(4) # biXPelsPerMeter
86+
read_le(4) # biYPelsPerMeter
87+
read_le(4) # biClrUsed
88+
read_le(4) # biClrImportant
89+
blkpixel = 1
90+
if read_le(4) != 0:
91+
blkpixel = 0
92+
print("black pixel is ", blkpixel)
93+
print("Image OK! Drawing...")
94+
rowSize = (bmpWidth + 3) & ~3 # 32-bit line boundary
95+
for row in range(bmpHeight): # For each scanline...
96+
# blink the LED
97+
if row % 2 == 0:
98+
led.value = False
99+
else:
100+
led.value = True
101+
if flip: # Bitmap is stored bottom-to-top order (normal BMP)
102+
f.seek(bmpImageoffset + (bmpHeight - 1 - row) * rowSize)
103+
else: # Bitmap is stored top-to-bottom
104+
f.seek(bmpImageoffset + row * rowSize)
105+
106+
rowdata = f.read(bmpWidth)
107+
for col in range(bmpWidth):
108+
for b in range(8):
109+
if (rowdata[col] & (0x80 >> b) != 0 and blkpixel == 0) or (
110+
rowdata[col] & (0x80 >> b) == 0 and blkpixel == 1):
111+
epd.pixel(col * 8 + b, row, Adafruit_EPD.BLACK)
112+
except (ValueError) as e:
113+
display_message("Error: " + e.args[0])
114+
except BMPError:
115+
display_message("Error: unsupported BMP file " + filename)
116+
except OSError:
117+
display_message("Error: Couldn't open file " + filename)
118+
finally:
119+
f.close()
120+
print("Finished drawing")
121+
return
122+
123+
124+
def read_le(s):
125+
result = 0
126+
shift = 0
127+
for byte in bytearray(s):
128+
result += byte << shift
129+
shift += 8
130+
return result
131+
132+
133+
class BMPError(Exception):
134+
pass
135+
136+
137+
# alternate bitmap display method using imageload library
138+
def display_bitmap_alternate(epd, filename):
139+
image, _ = adafruit_imageload.load(
140+
filename, bitmap=displayio.Bitmap, palette=displayio.Palette
141+
)
142+
for y in range(display.height):
143+
# blink the LED
144+
if y % 2 == 0:
145+
led.value = True
146+
else:
147+
led.value = False
148+
for x in range(display.width):
149+
if image[x, y] == 0:
150+
epd.pixel(x, y, Adafruit_EPD.BLACK)
151+
return
152+
153+
154+
# display message both on the screen and the serial port
155+
def display_message(message):
156+
print(message)
157+
display.rotation = 1
158+
display.fill_rect(0, 10, 264, 20, Adafruit_EPD.WHITE)
159+
display.text(message, 10, 10, Adafruit_EPD.BLACK)
160+
display.display()
161+
return
162+
163+
164+
# slide show routine
165+
def show_files():
166+
try:
167+
filelist = os.listdir(config["slidefolder"])
168+
display.rotation = 1
169+
led.value = True
170+
while True:
171+
for file in filelist:
172+
starttime = time.monotonic()
173+
display.fill(Adafruit_EPD.WHITE)
174+
print("displaying file", config["slidefolder"] + "/" + file)
175+
display_bitmap(display, config["slidefolder"] + "/" + file)
176+
endtime = time.monotonic()
177+
minutes = (endtime - starttime) // 60
178+
seconds = int(endtime - starttime) - minutes * 60
179+
print("update time:", minutes, "minutes", seconds, "seconds")
180+
print("updating display")
181+
display.display()
182+
print("done")
183+
time.sleep(5)
184+
except OSError:
185+
display_message("Error: Couldn't display file " + file)
186+
led.value = False
187+
return
188+
189+
#pylint: disable-msg=too-many-branches
190+
#pylint: disable-msg=too-many-statements
191+
#pylint: disable-msg=too-many-locals
192+
# run specified job
193+
def run_job(jobfile):
194+
try:
195+
print("running job " + jobfile)
196+
fpr = open(config["jobfolder"] + "/" + jobfile, mode="r")
197+
job = json.load(fpr)
198+
fpr.close()
199+
print("image: ", job["image"])
200+
starttime = time.monotonic()
201+
pixelsize = job["bkpixelsize"]
202+
whitepct = job["bkratio"]
203+
panelcount = 6
204+
led.value = True
205+
206+
display.rotation = 1
207+
display.fill(Adafruit_EPD.WHITE)
208+
print("ePaper display size:", display.width, display.height)
209+
print(config["imagefolder"] + "/" + job["image"])
210+
image, _ = adafruit_imageload.load(
211+
config["imagefolder"] + "/" + job["image"],
212+
bitmap=displayio.Bitmap,
213+
palette=displayio.Palette,
214+
)
215+
inv = False
216+
print("image size:", image.width, image.height)
217+
if image[0] == 1:
218+
inv = True
219+
print("using inv image")
220+
panelwidth = display.width // panelcount
221+
createfile = True
222+
try:
223+
out = open(config["asgfolder"] + "/asg" + job["image"], mode="wb")
224+
print("writing to file asg" + job["image"])
225+
except OSError:
226+
# readonly filesystem, do not create file
227+
createfile = False
228+
if createfile: # == True
229+
# BMP files are all the same dimensions, just different bitmaps,
230+
# writing hardcoded headers here
231+
# write file header (14 bytes)
232+
out.write(
233+
bytearray(
234+
[
235+
0x42, 0x4D, 0xFE, 0x18, 0, 0, 0, 0, 0, 0, 0x3E, 0, 0, 0
236+
]
237+
)
238+
)
239+
# write image header (40 bytes)
240+
out.write(
241+
bytearray(
242+
[
243+
0x28, 0, 0, 0, 0x8, 0x1, 0, 0, 0x50, 0xFF,
244+
0xFF, 0xFF, 0x1, 0, 0x1, 0, 0, 0, 0, 0,
245+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
246+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
247+
]
248+
)
249+
)
250+
# write 2 item color table (8 bytes)
251+
out.write(bytearray([0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0]))
252+
with open(
253+
config["bkfolder"] + "/background-" + str(whitepct)
254+
+ "-" + str(pixelsize) + ".dat", "rb") as fp:
255+
bkdata = fp.read()
256+
canvas = list(bkdata)
257+
for y in range(0, display.height):
258+
# blink the LED
259+
if y % 2 == 0:
260+
led.value = True
261+
else:
262+
led.value = False
263+
tcanvas = [0 for i in range(display.width + panelwidth)]
264+
tpanel = [0 for i in range(panelwidth)]
265+
for x in range(panelwidth):
266+
bytepos = ((x % panelwidth) // 8) + (
267+
y // pixelsize * (panelwidth + 7) // 8
268+
)
269+
bitpos = x % 8
270+
pixel = canvas[bytepos] & 1 << (bitpos)
271+
if pixel != 0:
272+
tpanel[x] = 1
273+
for x in range(display.width + panelwidth):
274+
pixel = tpanel[x % panelwidth]
275+
if pixel != 0:
276+
tcanvas[x] = 1
277+
for x in range(0, display.width):
278+
if (x % panelwidth) == 0 and x > 0:
279+
for x2 in range(x, x + panelwidth):
280+
tcanvas[x2] = tcanvas[x2 - panelwidth]
281+
for x2 in range(panelwidth):
282+
tpanel[x2] = tcanvas[x + x2 - panelwidth]
283+
offset = 0
284+
if (x >= 22 and
285+
x < (image.width + panelwidth // 2)
286+
and y < image.height):
287+
if (
288+
(image[x - panelwidth // 2, y] != 0 and not inv
289+
) or (
290+
image[x - panelwidth // 2, y] == 0 and inv)
291+
):
292+
# offset = 4
293+
if job["imagegrayscale"] == 0:
294+
offset = job["imageheight"]
295+
else:
296+
offset = (
297+
image[x - panelwidth // 2, y]
298+
* job["grayscalecolors"]
299+
// 255
300+
)
301+
if offset != 0:
302+
for x2 in range(x, display.width, panelwidth):
303+
tcanvas[x2] = tcanvas[x2 + offset]
304+
for x3 in range(0, display.width):
305+
# write line to eink display
306+
if tcanvas[x3] != 0:
307+
display.pixel(x3, y, Adafruit_EPD.BLACK)
308+
# else:
309+
# display.pixel(x,y,Adafruit_EPD.WHITE)
310+
if createfile:
311+
count = 0
312+
for x4 in range(0, display.width + 7, 8):
313+
value = 0
314+
for b in range(8):
315+
value |= (tcanvas[x4 + b] << 7) >> b
316+
out.write(bytes([value]))
317+
count += 1
318+
# add padding to end of line
319+
padding = (4 - (count % 4)) % 4
320+
for x4 in range(padding):
321+
out.write(bytes([0]))
322+
if createfile:
323+
out.close()
324+
endtime = time.monotonic()
325+
minutes = (endtime - starttime) // 60
326+
seconds = int(endtime - starttime) - minutes * 60
327+
print("completion time:", minutes, "minutes", seconds, "seconds")
328+
print("updating display")
329+
display.display()
330+
print("done")
331+
except (ValueError) as e:
332+
display_message("Error: " + e.args[0])
333+
led.value = False
334+
return
335+
336+
# main routine
337+
display.fill(Adafruit_EPD.WHITE)
338+
with open("/config.json") as fpx:
339+
config = json.load(fpx)
340+
fpx.close()
341+
print("waiting for button press")
342+
while True:
343+
button = read_buttons()
344+
if button:
345+
# button pressed, waiting for button release
346+
while read_buttons():
347+
time.sleep(0.1)
348+
if not button:
349+
continue
350+
print("Button #%d pressed" % button)
351+
if button == 1:
352+
for jobname in config["jobs"]:
353+
run_job(jobname)
354+
if button == 2:
355+
show_files()
356+
time.sleep(0.01)

0 commit comments

Comments
 (0)