This project is a collaboration with Tanvi Sharma and Sophie Shiff, two colleagues and friends of mine here at MICA! This piece makes use of a Raspberry Pi, a thermal printer, some Python code, and Adafruit IO (yep, it returns!) to generate rituals for visitors to take with them. The random nature of the generation can result in rituals that are strange and bizarre, or oddly poetic.
They then approached me to suggest we work together to create a physical version. I happily accepted! Work on the project took about 3 weeks, to prepare for submission to several shows.
This project was selected for the Ritual show here at MICA, and was on display in the Gateway building from September 15th to October 15th, 2019.
The physical version of ritualbot is meant to emulate the Tracery-based twitter version as much as possible, but is instead built in Python 3, for ease of interfacing with the thermal printer over the Raspberry Pi’s serial ports, and to talk with Adafruit IO.
When the Pi boots and this code starts running, it needs to establish a connection to Adafruit IO and collect some data. We make use of 10 different feeds, 8 of which are word types and store a large list of words (adjectives, adverbs, movementVerbs, nouns, occurrences, pastVerbs, placeWords, verbs), and the other 2 are for diagnostic/statistical purposes (monitor, counter). Many of these feeds could very likely be combined together, but for ease of use and for the sake of time, I kept them separate.
The Pi requests all the data from each word type feed (each of which is represented as an array), and then creates new arrays of each word type by using a
for d in array style for loop that specifically takes the value of each “post” to a feed and sets it as an element in this new array (otherwise we’d get a lot of metadata attached to each element).
After collecting our Adafruit IO data, we set up our ritual generation object. The class for ritual generation contains several functions for actually making the strings that will be printed out, which we will discuss in a minute. But first, we establish our connection to the button over the Pi’s serial ports, and set up a button callback function. When the button is pressed, we check to see when the button was last pressed to prevent abuse, and if it’s been long enough, we run our ritual generation code.
There are two main steps to creating a ritual in the code. First, we select an “origin”. This refers to how the ritual begins and the overall structure of the ritual. For example, we have a ritual structure that begins with “you wake up in your [random adjective] [random place]”, one that begins with “you are [random adjective] again”, and one that begins with “do you remember the last time you [random past-tense verb] your [random noun]?”. The full structures of each are as follows:
wake up in your (adjective) (place). (verb) your (noun) (adverb). (moveVerb) to your (place). you are (adjective) again. (adverb), (verb) your (adjective) self and (verb) from the (noun). bearing (noun) of (noun) on which a (noun) and a (noun) (verb) (adverb). do you remember the first time you (pastverb) your (noun)? if only you could (verb) (occurrence), you might (verb) something for the (noun).
So first, we pick one of those structures. In the code this is accomplished by picking a random integer, then using a specific structure based on the integer selected. Then the structure generates each sentence by selecting random elements from arrays that store our different word types, like adjectives or movement verbs or nouns, to name a few. So now we have a few strings to represent our ritual. These are then passed on to the printer code…
The printer code has to perform a handful of operations on the line strings passed to it. Each string is passed to a function that checks how long it is. This function was written because there’s no built in text wrap on the printer, it just splits words when the character length for the line is exceeded. So our function checks how long the string is against our maximum line length variable, and if it is too long, it looks for the space between two words that is closest to the line maximum, and splits the string in two at that space. The function then recursively calls itself with the second string as an argument, to check the length of that string and repeat this process if necessary.
Once a line string has been made to fit on the thermal paper, it is printed out, and the printer feed is advanced before printing the next line, to create space between each line. Now that we’ve printed out the ritual for the user, we need to send stats to Adafruit IO!
Now that we’ve printed the ritual, our code does two things: it increments the printCount variable by one, and sends this updated value to the counter feed. It sends “Someone printed a ritual!” to the monitor feed, and then we want to send the entire new ritual, so we concatenate the line strings together and send the compound string to the monitor feed as well.
Here’s a quick look at our Adafruit IO dashboard for ritualbot.
Our dashboard allows us to add new words to the word feeds on the fly, and also monitor ritualbot’s status and see how many rituals have been printed and what they said!
In terms of hardware, we used the Mini model thermal printer from Adafruit! Working with it took some doing, as the libraries for doing so are not entirely complete, and have some bugs depending on which version of the printer you receive. Ours had a few issues, primarily printing out some seemingly-nonsense characters each time we ran the printer. I soon realized that the characters, x(J, were not nonsense at all, but was actually hex code instructions that the library was trying to send to the printer, but was failing to do so for some reason. I had to hunt down the instructions that matched the hex code values, and comment out those lines.
We also made use of a breadboard and a simple push button, we decided to go for the “DIY” barebones, barewires look. Connecting the button was fairly simple, but creating button callback code that worked in the framework I had set up also took some time.
On the software end, creating the custom text-wrapping function took up a lot of time. Mostly it was me searching for the Python functions that would do what I needed, and then tweaking to make sure we didn’t lose any text and everything looked neat. As far as hardware is concerned, the printer gave us a lot of trouble, though primarily for software reasons, as previously mentioned. Digging through and modifying the thermal printer libraries was a big time sink that involved some trial and error.
There are still a lot of quality-of-life improvements that can be made to the code, primarily being able to batch-add multiple words at once as opposed to just manual input. We’re also hoping to better link the physical version with the twitter bot, so they share a words base without manually bringing words from one to the other.
Additionally, we plan to submit ritualbot to some more shows! Stay tuned :)
Here’s the source code for this project!