InkyPHAT = gettng a readable barcode on the display

Hi,
I’m trying to learn python while at the same time make something that may actually have some practical application for me. To that end, I have a Pi Zero WH and an InkyPHAT black display, from which I’m trying to make a clock which tells the time with barcodes (I didn’t say it would have a practical application for anyone else!).

At first I tried using Inkybar but I think that uses the old Inky libraries, and I’m nowehere near competent enough to update it. So I’ve been googling around and come up with something which uses pyBarcode to generate the barcode PNG (encoding a string from datetime.now() ), then PIL to resize and reduce the bit depth of the image. Then it uses inky to display the final barcode image.

I have 2 problems:

  1. The barcode seems to be coming out inverted. I’ve attached a picture of the resultant screen.
  2. The barcode isn’t readable. I’ve tried 2 approaches, generating a barcode using pybarcode defaults and then resizing with PIL, and trying to pass params into pybarcode to generate a small enough barcode in the first place - neither of them seems to produce a readable barcode.

My code is below, any (gentle, please) advice would be much appreciated!

===================================
import barcode

from barcode.writer import ImageWriter

import PIL

from PIL import Image

from inky import InkyPHAT

from datetime import datetime

#get date time

now = datetime.now()

dt_string = now.strftime("%d/%m/%Y:%H%M")

#initialise display

inky_display = InkyPHAT(“black”)

inky_display.set_border(inky_display.BLACK)

generate barcode

bar_class = barcode.get_barcode_class(‘code128’)

barcode = dt_string

writer=ImageWriter()

code128 = bar_class(barcode, writer)

save barcode as png

code128.save(‘timec128’, {“module_width”:0.1, “module_height”:6, “font_size”: 0, “text_distance”: 0, “quiet_zone”: 3})

#code128.save(‘timec128’)

#resize png

to_be_resized = Image.open(‘timec128.png’)

resize image for inkyPHAT

newSize = (212,104)

resized = to_be_resized.resize(newSize, resample=PIL.Image.NEAREST)

resized.save(‘timec128_resized.png’)

convert to black and white (reduce bit depth)

b_w_image = resized.convert(“1”)

#save B&W image

b_w_image.save(‘timec128_resized_bw.png’)

#img = Image.open(‘timec128_resized.png’)

#send image to the inkyPHAT screen

inky_display.set_image(b_w_image)

inky_display.show()

I can only assume that’s something to do with the way the image is converted. I discovered recently that the InkyWHAT library comes with an example for coverting images with the Python Image library, so try replacing:

with:

# Create an image to hold the palette
pal_img = Image.new("P", (1, 1))
# Create the palette
pal_img.putpalette((255, 255, 255, 0, 0, 0, 255, 0, 0) + (0, 0, 0) * 252)
# Convert the resized image to the palette in the pal_img image
b_w_image = resized.convert("RGB").quantize(palette=pal_img)

That creates a black barcode on a white background.

How did you get code128 to work? Python tells me that pyBarcode doesn’t have that.

1 Like

Hi @Shoe, thanks so much for the quick reply - I’ll give that a go ASAP.
Code 128 was added in v0.8 of pyBarcode, maybe you just need an update?

I suspect that I’m asking it to build the barcode so thin that the code is losing some integrity - even the non-reformatted barcode is refusing to scan from my monitor screen… I’m going to tinker around with attempting to encode a smaller string…

I’ll post back with my findings, thanks again!

Just an update, @Shoe
Your suggestion worked! The barcode is now showing up as black on white! Thanks for that, it would have taken me ages to get that far!

I now have the issue that the barocde is no longer readable after coming out of the resizing processing. All fascinating stuff though…

Thanks!Phat barcode black on white

When you say it isn’t readable, are you trying to scan it off your monitor or the Inky board?

PyBarcode for the Pi is apparently only at v0.7 and trying to install v0.8 throws an error. Python-barcode seems to work fine with identical code.

Interestingly, if I generate a barcode using Python-barcode and do the same thing using this website they come out with different barcodes. A barcode app on my phone picks up the website instantly but not the Inky barcode. I wonder if something needs tweaked to get Python to generate them properly.

OK, I’ve gotten my phone app to read EAN13 barcodes on Inkyphat using the Python-barcodes library. EAN13 apparently only accepts numerals so I’ll have a play with it to see if I can get one working which accepts characters as well, but it’ll have to wait until the weekend probably.

If you want to play with it, this is the code I used:

from barcode import EAN13
from barcode.writer import ImageWriter
from PIL import Image
from inky import InkyPHAT

# Set up InkyPHAT
inky_display = InkyPHAT("black")
inky_display.set_border(inky_display.WHITE)

# Create the barcode and save it as "test.jpeg"
with open ("test.jpeg", "wb") as file:
        EAN13("123456789102", writer=ImageWriter()).write(file)

#Open the image and resize it for Inky.
img = Image.open("test.jpeg")
img = img.resize((212, 104))

# Convert the image for InkyPHAT format
pal_img = Image.new("P", (1, 1))
pal_img.putpalette((255, 255, 255, 0, 0, 0, 255, 0, 0) + (0, 0, 0) * 252)
img = img.convert("RGB").quantize(palette=pal_img)

# Display the image.
inky_display.set_image(img)
inky_display.show()

I’m trying to read it using Scandit on my iPhone. I’m trying to scan the generated image off the monitor and the inkyPHAT.

The pre-resized image will read from the monitor screen as long as I don’t ask for the module width to be too small.

Just see your EAN13 code. I’ll definitely have a play with that. Thanks!

So I’ve poked at this a little more and I suspect that InkyPHAT doesn’t have the resolution for Code39 or Code128 barcodes. I tested the same code on InkyWHAT and it reads Code39 barcodes just fine, but struggled to properly read Code128.

What is reading the time, and what is it doing with the data? You could encode the time and date as simply numerals in an EAN13 barcode and decode it at the other end.

EDIT:

If you check here there’s a suggestion that Code39 barcodes should be at least 288 pixels wide, while EAN13 should be at least 166. Obviously this depends on how wide your pixels are, but it does back up the suggestion that Code39 needs more resolution than EAN13.

1 Like

That’s some great research thanks @Shoe. I was coming to the same conclusion myself.

Been tinkering with QR code, Next issue seems to be that InkyPHAT requires the image to be in the exact dimensions of the screen. QR codes are obviously square. Next area of research is padding out the image.

By sheer coincidence, someone asked about this the other day.

Fingers crossed the QR codes work! Which barcode module are you using for that?

1 Like

Oooh! Perfect! I’ve not had much chance to play over the weekend but will be into that shortly.

At the moment, qrcode but I might have a look at PyQRCode too.

(Sorry, tried to add links to PyPI for those but wasn’t allowed.

Hey @Shoe

I just wanted to show you the latest version.

The QR code looks like a much better idea.

I followed your link and created a canvas image with the exact dimensions required by the InkyPHAT. I then used the PIL image.paste() function to add the QR code to the canvas, I then grabbed some code from the ‘Getting Started’ examples to display the date and time as well.

I’ve rounded it off with my logo (saved at bit depth of 1).

I added a shebang to the top of the script and used chmod to make the whole thing executable and now it’s running from a cron job every minute!

Thank you so much for your help! I think I’m in the lining stuff up and messing with fonts stage of the project!

Code below, in case it’s any use to anyone:


#! /usr/bin/python3

import qrcode

from PIL import Image, ImageDraw, ImageFont

from inky import InkyPHAT

from datetime import datetime

#get your 32x32 logo

logo_img = Image.open()

#instantiate qr

qr = qrcode.QRCode(

version=1,

error_correction=qrcode.constants.ERROR_CORRECT_L,

box_size=4,

border=2,

)

#get date time

now = datetime.now()

dt_string = now.strftime("%d/%m/%Y:%H%M")

d_string = now.strftime("%d/%m/%Y")

t_string = now.strftime("%H:%M")

#print for debug/logging

print('Running for ',dt_string)

#initialise display

inky_display = InkyPHAT(“black”)

inky_display.set_border(inky_display.BLACK)

#set fonts

datefontfile = ‘/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf’

timefontfile = ‘/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf’

datefont = ImageFont.truetype(datefontfile,15)

timefont = ImageFont.truetype(timefontfile,34)

#create blank image as canvas (hopefully to PHAT dimensions)

img = Image.new(“1”, (inky_display.WIDTH, inky_display.HEIGHT), 0)

draw = ImageDraw.Draw(img)

#write date time on canvas

draw.text((112,5),d_string, inky_display.BLACK,datefont)

draw.text((102,25),t_string, inky_display.BLACK,timefont)

generate qr

qr.add_data(dt_string)

qr.make(fit=True)

save qr as png

qr_img = qr.make_image(fill_color=“black”, back_color=“white”)

qr_img.save(‘qr_img.png’)

add the qr png to the canvas

img.paste(qr_img)

img.paste(logo_img,(140,70))

img.save(‘qr_canvas.png’)

#send canvas image to the inkyPHAT screen

inky_display.set_image(img)

inky_display.show()

Ah brilliant, that looks really good! It also scans far more easily than the barcodes, and still gives you space at the side for other data. I’m glad you’ve got it working.

You’ve actually given me an idea here, for a while I’ve wanted to build a sort of home-IoT hub with temperature, humidity etc. I always thought about a web page to show the data, but basic stuff could be on one side of the Phat with a QR code which takes you to the web page with the full data on the other, saving you the hassle of remembering the URL. Hmmm…I might have to look into that!

Excellent! Sounds like a great project.
I’m thinking of building an Enviro+ setup, I wonder if that tiny display could show a readable QR containing a website?

I was a bit skeptical of that because the OLED is quite small but even relatively detailed URLs (“AVeryLongWebAddressForTesting.com”) actually seem to work fine on it. I’m quite impressed with that OLED.

Wow!
Might be time for me to break out the card again!

See, this is how they get you, they make good products and then people feel the need to buy them. Before you know it you’ve entire drawers of bits you bought for projects you’ve totally forgotten about.

:-)
This is an absolute tangent, so please feel free to ignore @shoe, but is there an outdoor case I could use for a zero WH with Enviro+?
I’d be looking to add the air quality sensor too. I don’t have access to a 3D printer.

There’s no official, manufactured case, but when Enviro+ was first released Pimoroni published this guide for making one with a few DIY parts. Unfortunately it obscures the OLED. The other thing I’ve seen people use is tupperware boxes, but you’d need to cut air vents and be careful about condensation.

1 Like

If you want to turn the Enviro+ into a handsome looking project, you could consider mounting the electronics into a Stevenson Screen. I was given a kit for a small one from Russell Scientific and intend to do this. (https://www.russell-scientific.co.uk/1295-marine-screen-501-p.asp, of Dereham Norfolk, UK). Disclaimer:- I am not connected to Russell Scientific.

1 Like