What Should We Eat?
Caleb Sun
Caleb Sun
This device is intended to sit inside of grocery stores and farmers markets, prompting two users to deepen their understandings of one another through a simple question: Do you desire tenderness or clarity from your partner? Once both users have decided, the device will print out a suggestion for a recipe to try, sparking a bit of inspiration and understanding between the two. Food is repositioned as a vessel for understanding and connecting, and a symbol for a relationship that can break out of endless cycles and emotionless habit.
One person pushes a white button on one side of the triangle shaped box. The button lights up in a red color. Another person pushes a white button on the other side of the box. The button lights up in a red color. A slip of paper with a short note is printed from the top of the box.
The core of this project was using an Adafruit mini thermal printer, which meant that my first step was to borrow one from lending and test it out. I found that the printer worked best when run on 9 volts instead of 5, which produced slow and light text.
I've never used simple buttons before so I did some testing on how to read a signal and write it to an LED. While simple buttons could easily be used to bridge a constant electrical signal, I needed it to be read and stored as a separate value on the Arduino because of later computational requirements. I used these metal buttons which weren't great, but more on that later.
I had set up these strong LED lights to serve as the backlights for the buttons, each with a 9v step-up transistor so that the LED could actually light up, only for Zach to point out that the strong LEDs are crazily enough pretty strong. So strong that each would require a huge metal heatsink for longevity. Which is bad for the compact design I have planned. I decided to pivot to neoPixel LEDs instead.
I used the neoPixel 16 ring which worked out rather well because it fit almost exactly around the arcade button base that I ended up choosing. The neoPixels gave pretty good documentation for setup, but I quickly ran into an issue with the smoothness of light that Zach diagnosed as being just the way we percieve light.
I was able to resolve some of that by just applying the gamma correction guide provided by neoPixel. However, more work was needed to make sure that the gamma correction would work without using delays, since in my tests with this correction, you had to time a button press exactly to when a loop starts or ends in order to trigger some other state.
Zach and I figured out a looping function that resolved this issue, essentially using a self-resetting timer to allow for the function to be interrupted at any time when buttons are pressed. Yay.
Something unfortunate that happened was that function Zach helped me with was self-contained, meaning when I ran it with four different lights, the lights were all off and buggy. I ended up addressing this by pulling all the counter variables out of individual functions and into the main loop function. This came at the second to last day of the project at like. 12 am. Lots of pain and suffering.
I knew that I wanted the buttons to be backlit given the independent nature of this device. There wouldn't be anyone guiding users or giving them instructions, so the affordances of the device itself need to prompt and indicate different states. Here I am testing out different acrylic swatches to see which produces the best diffused light effect.
On a water break I saw these felted pin boards in the lounge and was inspired to cover my device with felt. I thought it would be suited for the intimate and cute vibe I was going for, and to also allow for a multi-step approach for constructing the complex shape I had in mind. Laser-cutting is fast but doesn't do well with curves...
I lasercut some stencils so that I could paint on the felt with confidence. Didn't think at all about the counters in the letterforms not being maintained in a stencil but I decided to roll with it.
I really did not want to do Solidworks this entire project and that meant that the Friday before the project was due I was three parts short for fabrication that needed to be 3D printed. Ideate printers are closed and I don't trust them also. In comes Stanley, the crazy bastard, who has a 3D printer just in his house. He saved my lazy ass.
I had originally planned for the printer to be held up by wooden rods I would run through the bases of the triangular prism (see the two holes in the center of the image to the right). I unfortunately didn't measure things well. At all. Also, the way that wires come out of the printer means that it can't sit on two poles at the corner diagonal from the slit that paper comes out from. I remedied this by using zip ties and some plastic zip tie holders.
The buttons were an ordeal. Essentially, I had to paste the felt onto the acrylic on the right side, cut the felt so that it can bend into the shell, glue the button on, then a spacer, then the LED. To streamline this process, I first soldered everything together, constructed the button-spacer-LED situation, and then glued everything together.
Thanks to Stanley's assist I had three really awesome curved bits to combine with laser-cut acrylic pieces to form the shell. The top curved piece has a slot along the top to account for the paper feed. It also has a space to account for a blade so that the paper can be cut better.
I had a good time making this project, loved having a chance to combine a lot of the skills I learned last year with a more creative and fun goal. I think I underestimated how complicated my code was unfortunately, which lead to some pain in the final stages of the project. I also really, really procrastinated on fabrication which bit me in the ass in the final days of this project and didn't give me a chance to workshop some final errors. Overall though, I think I've really established a workflow with physical computing projects that I enjoy. I start off with electrical prototyping and make working prototypes to iron out any technical and engineering kinks, before focusing on fabrication and building. Ideally with more time, I think there can be a more back-and-forth process, but this works for now.
If I had some more time, I think I would revisit the mechanism for printing and cutting paper, since it really does not work as intended right now. Everything electrical works fine, but if the paper is cut where the blades are right now, the paper will catch and print back down into the shell. For demonstration purposes I just fed the paper a little bit out so that it wouldn't catch. I think the blades are too far away from the slot exit, and if I could I would reprint the curved piece so that the blades can sit almost exactly to where the slot exit is. Another thing I wasn't able to figure out was large-scale data storage. My original plan was to print out recipes instead of recipe names, so that users would be able to shop around for ingredients as suggested by the device. Unfortunately the way that the thermal printers worked necessitated that I put string data in the live code, rather than in a .h file that would save working memory. In the end I had to cut down on excess code so that the working memory could handle the rest of my program. Expanding the actual print data capability is definitely another priority for this project.
I learned a lot in this project about laser cutting. For some reason I've never done it before and always defaulted to 3D printing. My brain doesn't necessarily work in flat parts and it's something I've really wanted to build up. Not sure if I mentioned it in the first form, but I think working in a hybrid 3D and laser cut environment for this project was a good way to get acquainted.
//What Should We Eat
//Made by Caleb Sun
//This program runs four buttons and an Adafruit thermal printer to
print short messages based on combinations of two button presses.
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#include "Adafruit_Thermal.h"
#include "SoftwareSerial.h"
#define TX_PIN 13 // Arduino transmit YELLOW WIRE labeled RX on printer
#define RX_PIN 12 // Arduino receive GREEN WIRE labeled TX on printer
SoftwareSerial mySerial(RX_PIN, TX_PIN); // Declare SoftwareSerial obj first
Adafruit_Thermal printer(&mySerial); // Pass addr to printer constructor
//gamm-adjusted steps for lights
const uint8_t jstep[] = {
5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 99,
98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82,
81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65,
64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 49, 47, 45,
43, 41, 39, 37, 35, 33, 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11,
9, 7, 5
};
//pin declarations; indicators for which button was pressed
const int lightA1 = 8;
const int lightA2 = 9;
const int lightB1 = 10;
const int lightB2 = 11;
//neopixel declarations
Adafruit_NeoPixel stripA1 = Adafruit_NeoPixel(60, lightA1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel stripA2 = Adafruit_NeoPixel(60, lightA2, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel stripB1 = Adafruit_NeoPixel(60, lightB1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel stripB2 = Adafruit_NeoPixel(60, lightB2, NEO_GRB + NEO_KHZ800);
//pin declatations; button
const int buttonA1 = 4; //user1 love
const int buttonA2 = 5; //user1 clarity
const int buttonB1 = 6; //user2 love
const int buttonB2 = 7; //user2 clarity
unsigned long timerVar = 0;
const int tenSeconds = 10000;
//reading button data
int readA1 = 0;
int readA2 = 0;
int readB1 = 0;
int readB2 = 0;
//looping variables
int jlength = sizeof(jstep) / sizeof(jstep[0]);
int jcounter = 0;
int j = 0;
bool upswing = true;
float timer = 0;
// -----------------------------------------------------------------------
void setup() {
Serial.begin(9600);
Serial.print("started");
//button
pinMode(buttonA1, INPUT); //pin2
pinMode(buttonA2, INPUT); //pin3
pinMode(buttonB1, INPUT); //pin4
pinMode(buttonB2, INPUT); //pin5
//neopixel begin
stripA1.begin();
stripA1.setBrightness(50);
stripB1.begin();
stripB1.setBrightness(50);
stripA2.begin();
stripA2.setBrightness(50);
stripB2.begin();
stripB2.setBrightness(50);
stripA1.show();
stripA2.show();
stripB1.show();
stripB2.show();
mySerial.begin(19200); // Initialize SoftwareSerial
printer.begin(); // Init printer (same regardless of serial type)
}
void loop() {
j = jstep[jcounter];
// Serial.println(j);
if (upswing) {
jcounter++;
if (jcounter == (jlength - 1)) {
upswing = false;
}
} else {
jcounter--;
if (jcounter == 0) {
upswing = true;
}
}
rainbowA1(1000);
rainbowA2(1000);
rainbowB1(1000);
rainbowB2(1000);
//reads inputs of the buttons
readA1 = digitalRead(buttonA1);
readA2 = digitalRead(buttonA2);
readB1 = digitalRead(buttonB1);
readB2 = digitalRead(buttonB2);
//resets timer after 5 seconds to account for inactivity
if (readA1 == 1) {
Serial.print("A1");
unsigned long future = millis() + 5000;
while (millis() < future) {
colorWipeA1(stripA1.Color(255, 0, 0));
colorWipeA2(stripA2.Color(0, 0, 0));
colorWipeB1(stripB1.Color(100, 100, 100));
colorWipeB2(stripB2.Color(100, 100, 100));
readB1 = digitalRead(buttonB1);
readB2 = digitalRead(buttonB2);
if (readB1 == 1) {
while (millis() < future) {
colorWipeB1(stripB1.Color(255, 0, 0));
colorWipeB2(stripB2.Color(0, 0, 0));
option1();
}
}
if (readB2 == 1) {
while (millis() < future) {
colorWipeB2(stripB2.Color(255, 0, 0));
colorWipeB1(stripB1.Color(0, 0, 0));
option2();
}
}
}
if (millis() >= future) {
rainbowA1(1000);
rainbowA2(1000);
rainbowB1(1000);
rainbowB2(1000);
}
}
if (readA2 == 1) {
Serial.print("A2");
unsigned long future = millis() + 5000;
while (millis() < future) {
colorWipeA2(stripA2.Color(255, 0, 0));
colorWipeA1(stripA1.Color(0, 0, 0));
colorWipeB1(stripB1.Color(100, 100, 100));
colorWipeB2(stripB2.Color(100, 100, 100));
readB1 = digitalRead(buttonB1);
readB2 = digitalRead(buttonB2);
if (readB1 == 1) {
while (millis() < future) {
colorWipeB1(stripB1.Color(255, 0, 0));
colorWipeB2(stripB2.Color(0, 0, 0));
Serial.print("Option 3");
option2();
}
}
if (readB2 == 1) {
while (millis() < future) {
colorWipeB2(stripB2.Color(255, 0, 0));
colorWipeB1(stripB1.Color(0, 0, 0));
Serial.print("Option 4");
option3();
}
}
}
if (millis() >= future) {
rainbowA1(1000);
rainbowA2(1000);
rainbowB1(1000);
rainbowB2(1000);
}
}
if (readB1 == 1) {
Serial.print("B1");
unsigned long future = millis() + 5000;
while (millis() < future) {
colorWipeB1(stripB1.Color(255, 0, 0));
colorWipeB2(stripB2.Color(0, 0, 0));
colorWipeA1(stripA1.Color(100, 100, 100));
colorWipeA2(stripA2.Color(100, 100, 100));
readA1 = digitalRead(buttonA1);
readA2 = digitalRead(buttonA2);
if (readA1 == 1) {
while (millis() < future) {
colorWipeA1(stripA1.Color(255, 0, 0));
colorWipeA2(stripA2.Color(0, 0, 0));
option1();
}
}
if (readA2 == 1) {
while (millis() < future) {
colorWipeA2(stripA2.Color(255, 0, 0));
colorWipeA1(stripA1.Color(0, 0, 0));
Serial.print("Option 4");
option2();
}
}
}
if (millis() >= future) {
rainbowA1(1000);
rainbowA2(1000);
rainbowB1(1000);
rainbowB2(1000);
}
}
if (readB2 == 1) {
Serial.print("A2");
unsigned long future = millis() + 5000;
while (millis() < future) {
colorWipeB2(stripB2.Color(255, 0, 0));
colorWipeB1(stripB1.Color(0, 0, 0));
colorWipeA1(stripA1.Color(100, 100, 100));
colorWipeA2(stripA2.Color(100, 100, 100));
readA1 = digitalRead(buttonA1);
readA2 = digitalRead(buttonA2);
if (readA1 == 1) {
while (millis() < future) {
colorWipeA1(stripA1.Color(255, 0, 0));
colorWipeA2(stripA2.Color(0, 0, 0));
option2();
}
}
if (readA2 == 1) {
while (millis() < future) {
colorWipeA2(stripA2.Color(255, 0, 0));
colorWipeA1(stripA1.Color(0, 0, 0));
option3();
}
}
}
if (millis() >= future) {
rainbowA1(1000);
rainbowA2(1000);
rainbowB1(1000);
rainbowB2(1000);
}
}
//idle light state
void rainbowA1(uint8_t wait) {
if (millis() - timer >= wait) {
for (int i = 0; i < stripA1.numPixels(); i++) {
stripA1.setPixelColor(i, j, j, j);
}
stripA1.show();
}
}
void rainbowA2(uint8_t wait) {
if (millis() - timer >= wait) {
for (int i = 0; i < stripA2.numPixels(); i++) {
stripA2.setPixelColor(i, j, j, j);
}
stripA2.show();
}
}
void rainbowB1(uint8_t wait) {
if (millis() - timer >= wait) {
for (int i = 0; i < stripB1.numPixels(); i++) {
stripB1.setPixelColor(i, j, j, j);
}
stripB1.show();
}
}
void rainbowB2(uint8_t wait) {
if (millis() - timer >= wait) {
for (int i = 0; i < stripB2.numPixels(); i++) {
stripB2.setPixelColor(i, j, j, j);
}
stripB2.show();
}
}
//selected light state
void colorWipeA1(uint32_t c) {
for (uint16_t i = 0; i < stripA1.numPixels(); i++) {
stripA1.setPixelColor(i, c);
stripA1.show();
}
}
void colorWipeA2(uint32_t c) {
for (uint16_t i = 0; i < stripA2.numPixels(); i++) {
stripA2.setPixelColor(i, c);
stripA2.show();
}
}
void colorWipeB1(uint32_t c) {
for (uint16_t i = 0; i < stripB1.numPixels(); i++) {
stripB1.setPixelColor(i, c);
stripB1.show();
}
}
void colorWipeB2(uint32_t c) {
for (uint16_t i = 0; i < stripB2.numPixels(); i++) {
stripB2.setPixelColor(i, c);
stripB2.show();
}
}
// results
void option1(){
printer.feed(2);
printer.println("Let's eat:");
printer.println("Something from our childhood");
printer.println("Something that makes us feel \ncloser together");
printer.println("Something rich and savory");
printer.feed(2);
printer.println("Maybe...");
printer.feed(1);
printer.println("Beef Bolognese");
printer.feed(8);
printer.sleep(); // Tell printer to sleep
delay(3000L); // Sleep for 3 seconds
printer.wake();
}
void option2(){
printer.feed(2);
printer.println("Let's eat:");
printer.println("Something new and fresh");
printer.println("Something that makes us feel new");
printer.println("Something light and airy");
printer.feed(2);
printer.println("Maybe...");
printer.feed(1);
printer.println("Cold Tomato Soba Noodles");
printer.feed(8);
printer.sleep(); // Tell printer to sleep
delay(3000L); // Sleep for 3 seconds
printer.wake();
}
void option3(){
printer.feed(2);
printer.println("Let's eat:");
printer.println("Something hearty and full");
printer.println("Something that makes us feel \nmore assured");
printer.println("Something spiced and filling");
printer.feed(2);
printer.println("Maybe...");
printer.feed(1);
printer.println("Shrimp Jambalaya");
printer.feed(8);
printer.sleep(); // Tell printer to sleep
delay(3000L); // Sleep for 3 seconds
printer.wake();
}