Introduction
All art movements used to have manifestos, at least the more radical ones at the start of the
twentieth century did, so I thought I'd do one for electronic art where the manifesto was, itself, an
artwork. The main art influence for the text of this was a piece written by the American artist Claus
Oldenburg in 1961 called "I'm for an art...", but of course his was much better.
The Work
Here are a couple of pictures of it on the wall of my workroom.
The following video shows it working.
Here's one display running for a while so you can get a flavour of the manifesto
It would be interesting, in a gallery setting, to see just how long viewers stood in front of it
reading the text before they got bored and wandered off.
The Technical Stuff
I had to decide how I was going to present the text of the manifesto. My first thought was an LED
moving-message style display, but decided that would be too much work to construct in a week and would
blow my whole budget (with seven pieces still to do), so, slightly reluctantly, I settled on a LCD
character display. But a single display didn't seem quite right, so I decided to have three displays
all runnning in parallel to add some complexity to the work (after the last slice of pi, I'm bored of
minimalism already). Why three? I've often thought that good 'rules of thumb' for art are to avoid
hard symmetry, unless you're really, really good, and given a choice between an odd number and an even
one, pick the odd one (or even better a prime).
To keep the wiring down and to make the code easier, I chose some Winstar 16x2 displays (WH1602B3-
SLL-JWV) which have an I2C interface. I'm hoping I can address them on one bus. The datasheet, in
time-honoured tradition for LCD panels, is all a bit vague and ambiguous suggesting that there is a
choice of four addresses that can be selected but leaving off from the pin-out any indication of where
to find the pins to do it.
Here are my starting materials. A Pi 3 B+, three LCDs, and a canvas.
Before I worry about the art stuff, I need to get the Pi talking to the displays on the bench and get
the code sorted out.
What seemed like it would be the easiest part turned out to be the hardest. You'd think that something
sold as having an I2C interface would be simple to connect to, but this quickly turned into being a
'hack your own display' type exercise. Not just for how to do the physical interface connections
without blowing up the panel in the process, but also how on earth to send the right commands to make
it present some text on the panel. The problem is that this is a variant of a board that can also be
configured (by them, with 0-ohm links) to be a parallel interface or an SPI one and they've left bits
and pieces of all of them in the datasheet, including a recommended circuit that would apply to SPI
and not I2C. They've hacked their own datasheet and I've got the fun job of deciphering it.
It looks like there's an on-board generator for a negative supply for the LCD voltage and the voltage
comes out on pin 15. Sometimes that pin is used for the anodes of the backlight LEDs, so I'm going to
have to be cautious - the voltage inverter won't be too happy if I short its output to something else.
Here's the panel with a 5.1V supply connected to pins 1 (GND) and 2 (+5V) and you can see from the
meter that the panel generates -5.1V on pin 15.
That's being done by the small 8-pin package that you can see on the back of the board and the two
tants. That -5V will have go to one end of a trimmer pot, with +5V at the other end, and the slider
going to the Vo supply input in order to bias the panel and adjust the contrast. The LCD part of the
panel works relative to the +5V supply, so the trimmer slider will be slightly negative to give the
recommended voltage for the LCD supply.
Although the pinout doesn't show the I2C address lines, they are mentioned later in the text which
also identifies the clock and data pins which are the other way round to those on the datasheet pinout
(which are for SPI). So next I'm going to connect the panel as I think the interface should go and see
if the Pi can talk to it and get acknowledges back. I'm going to leave the backlight for now and come
back to it at the end.
To work out where to connect it to on the Pi expansion header, I'm using this drawing. It's part of
the reduced schematic for the Pi from the Raspberry Pi website. That shows the power pins and the I2C
connections, which is all I need this time round. Although the Pi IO operates on 3.3V and the LCD on
5V, with the SDA and SCL lines I should be ok just connecting them together as the pull-ups are on the
Pi side of things.
You'd think the software side of driving I2C on a Pi would be very easy, but all the information about
it is very confusing so this just represents the point I've reached with it and shouldn't be taken as
a how-to tutorial.
It seems that the actual low-level code to drive the I2C is built into the kernal as it gets used to
control devices in the system under the guise of SMBus. To reach it from Python we need something to
include in the code that allows us to talk to those routines. Many examples on the web use python-
smbus, which is a separate C module. That needs to be installed - Python couldn't find it when I tried
including it.
After a bit of searching, I came across these bindings as an alternative
https://github.com/kplindegaard/smbus2
unlike the smbus module, where the bindings are done with separate C code, this is done with pure
Python. That makes it easier for me to install on my headless Pi because I can just copy the file that
does the work across the network from a laptop to my home directory on the Pi.
After a bit of fiddling about, I got these waveforms on the SDA and SCL lines. That shows the Pi
sending a command (3 bytes in total) to the LCD. I don't have a visible display yet, however you can
see that the panel is responding by the ack at the end of each byte being sent (the ack is slightly
raised up in the air because the MOSFET on the panel is a bit weedy - it's fine for an ack, though: if
the Pi didn't like it, it would stop at that point and not do the rest).
It also means I'm ok with the Pi's 3.3V pull-ups on the I2C driving the +5V panel.
I was very unsure about the backlighting. The usual anode connection is missing as it's been replaced
by the LCD bias voltage. Measuring with a meter, it turned out that the anodes had the +5V supply on
them, so the intention is obviously to put the current limiting resistor in series with the cathode
connnection to pin 16, rather than take it straight to ground. That works ok and, after adjusting the
contrast pot, I can now see a single character that I've written to the display.
Here it is. The displayed digit is a bit dim because I was being very cautious and the LED current is
limited to about 9mA. I've now got it counting from 0 to 9 at one second intervals, so the panel is
working reliably.
This is how I've wired it to the Pi 3 B+.
Notes:
1) this applies only to the B3 variant (I2C-only interface);
2) Vee is a -5V output from the panel that's generated from the Vdd supply. Don't connect it to
anything other than the contrast trimmer pot;
3) SA0 and SA1 set the LSBs of the I2C address. The circuit above gives a slave address of 0x3C;
4) The backlight resistor is for 60mA;
5) The panel is write-only. The only thing that comes back is the ack. That means there's no 'busy
bit' to test and the sending end has to ensure that there's time for the panel to complete a command
before sending the next.
Next job is to leave the software for a moment and wire up the other two panels. Then I can check that
the addressing works. At that point I'll have enough to work with to do the final code for the
artwork.
I've now wired up the three displays and the Pi. At this stage, I changed my mind about the canvas and
decided I would arrange it on an old off-cut of plywood. Not quite sure why. Might have had something
to do with looking at pictures of some early Robert Rauschenberg 'combine' pictures in a book at the
library.
That all seems fine. Here it is with the LCDs showing text. The addressing works and they can all be
written to individually - the individual digit on the bottom row is the display address.
I've put the manifesto text in a text file, so that it can be changed easily. That gets read at the
start into a list of strings. The software has an index for each display to keep track of which string
is being displayed, an index for the next string, and an index for position along the line. It reads
16 characters and uses the ord() function to turn them into ASCII for sending to the panel. Trickiest
part was running over the join between the current string and the next string. I was very confused
about the string handling at first, but once I realised that strings and lists can be treated as
though they were arrays and indexed I was fine with it.
Here's the code as it stands at the moment. It's not good code, there is no exception processing,
testing of bounds, or any of the defensive coding you'd do for something that has to be robust and
work every time, but it will do for what is essentially me hacking art together.
import time import random from smbus2 import SMBus # start off with some sensible values in the various variables currentString = [1,2,3] nextString = [4,5,6] currentPos = [0,0,0] # fetch the whole manifesto from a text file with open("manifesto.txt","r") as fileHandle: #open the file manifestoStrings = fileHandle.readlines() #read all the lines as strings # initialise the LCD panels # and write the static first line bus = SMBus(1) firstLineString = "---MANIFESTO----" displayBuffer = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] for i in range (0, 16): displayBuffer[i] = ord(firstLineString[i]) for display in range(0, 3): bus.write_i2c_block_data(0x3C + display, 0x00, [0x3C]) time.sleep(0.015) bus.write_i2c_block_data(0x3C + display, 0x00, [0x0C]) time.sleep(0.001) bus.write_i2c_block_data(0x3C + display, 0x00, [0x01]) time.sleep(0.001) bus.write_i2c_block_data(0x3C + display, 0x00, [0x06]) time.sleep(0.01) bus.write_i2c_block_data(0x3C + display, 0x00, [0x80]) bus.write_i2c_block_data(0x3C + display, 0x40, displayBuffer) # this loop processes part of the current string into a temporary display buffer # and sends the result to the LCD panel # repeats for each of the three panels # updates current position and, if off end, deals with getting a new next string while True: #wait for half a second time.sleep(0.3) #now for each LCD display get string data and send to panel for display in range(0, 3): if currentPos[display] <= (len(manifestoStrings[currentString[display]]) - 18): for i in range (0, 16): displayBuffer[i] = ord(manifestoStrings[currentString[display]][currentPos[display] + i]) else: j = (len(manifestoStrings[currentString[display]]) - 2) - currentPos[display] for i in range (0, j): displayBuffer[i] = ord(manifestoStrings[currentString[display]][currentPos[display] + i]) for i in range (0, 16-j): displayBuffer[i + j] = ord(manifestoStrings[nextString[display]][i]) bus.write_i2c_block_data(0x3C + display, 0x00, [0xC0]) bus.write_i2c_block_data(0x3C + display, 0x40, displayBuffer) # update current position currentPos[display] = currentPos[display] + 1 # and if off end of string, move next to current and randomly choose another next if currentPos[display] >= (len(manifestoStrings[currentString[display]]) - 2): currentPos[display] = 0 currentString[display] = nextString[display] while nextString[display] == currentString[display]: nextString[display] = random.randint(0, len(manifestoStrings)-2)
I'm not particularly satisfied with the way it turned out - the character displays aren't ideal for
the application and the lettering on the board needs to be more prominent - but I don't have time for any more work
on it for now, so this is it as far as PiCasso is concerned.
Now I'm even further behind the one-a-week schedule. Oh well, ever onwards! Slice 3 next.
Note for the judges: I'm following along with this Design Challenge as an independent participant, not as a challenger. I'm
not entering the competition for the prizes.
Top Comments