Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Hand tools and fabrication machines | ||||||
|
This was a demo project for the Maker Faire Hannover 2016. The project was inspired by the work of the artist Julius Popp.
We are using a transparent hose, 2 peristaltic pumps and an Arduino Uno with motor shield to build a simple display.
The pumps pressing a pattern of water and red paraffin oil in the hose. In the end a text will be visible.
The reservoir was an old vessel for instant tee. Paraffin oil is easier as water and will swim on the top. You have to place 3 lead-ups to the vessel with different length of hose inside:
- to the bottom for the water pump
- to the middle for the paraffin pump
- to the top for the reflow
Stick the Arduino Uno and the motor shield into the arduibox protoboard. It's useful to mount the arduibox and a 12V power supply together to a din rail. Connect 5 push buttons to the A0 - A4 pins of the Arduino.
Drill 2 holes in a wooden panel and mount the pumps in this holes. Connect the hose as in the picture above. You have to connect the cables of the parafin pump to stepper out put 1 of the shield and the water pump to stepper output 2.
You have to fix 5 rows of transparent hose with the cable straps to the wooden panel. The minimum lenght of each row is 80 cm. Connect one end of the hose to the t-junction and the other end with the reflow junction of the reservoir
Thats all. You will find the firmware for the Arduino on our website.
/*
* hose display for Maker Faire Hannover 2016
* Version 1.0
* Copyright (C) 2016 Hartmut Wendt www.hwhardsoft.de
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <AFMotor.h>
#include <font5x7.h>
#define DEBOUNCE 10 // button debouncer, how many ms to debounce, 5+ ms is usually plenty
// here is where we define the buttons that we'll use. button "1" is the first, button "6" is the 6th, etc
byte buttons[] = {14, 15, 17, 18, 19}; // the analog 0-5 pins are also known as 14-19
// This handy macro lets us determine how big the array up above is, by checking the size
#define NUMBUTTONS sizeof(buttons)
// we will track if a button is just pressed, just released, or 'currently pressed'
byte pressed[NUMBUTTONS], justpressed[NUMBUTTONS], justreleased[NUMBUTTONS];
byte previous_keystate[NUMBUTTONS], current_keystate[NUMBUTTONS];
unsigned char pump_air_cnt;
unsigned char pump_liquid_cnt;
unsigned char last_pixel;
unsigned char display_row[8];
const unsigned char test_pattern[] = {
0xC0, 0xC0, 0x00,
0x40, 0x3C, 0x02,
0x40, 0x3C, 0x02,
0x40, 0x3C, 0x02,
0x40, 0x3C, 0x02,
0x40, 0x3C, 0x02,
0x40, 0x3C, 0x02,
};
const unsigned char Maker_pattern[] = {
0xC0, 0xC0, 0xC0,
0x89, 0x92, 0xEE,
0xDA, 0x54, 0x89,
0xAB, 0xD8, 0xCE,
0x8A, 0x54, 0x89,
0x8A, 0x52, 0xE9,
};
const unsigned char Faire_pattern[] = {
0xC0, 0xC0, 0xC0,
0x73, 0x3B, 0x9C,
0x44, 0x92, 0x50,
0x77, 0x93, 0x9C,
0x44, 0x92, 0x50,
0x44, 0xBA, 0x5C,
};
// program modes
enum {
PM_stop, // program mode stop
PM_pump_air, // program mode pump air runs endless
PM_pump_liquid, // program mode pump liquid runs endless
PM_demo, // program mode full automatic demo
PM_demo2, // program mode full automatic demo
PM_test, // shows test pattern
PM_wait, // wait 1 min in demo
PM_wait2, // wait 1 min in demo
};
// set pin numbers:
#define buttonStop 0
#define buttonDemo 1
#define buttonPumpAir 4
#define buttonPumpLiquid 3
#define buttonTest 2
#define pixel_liquid 0
#define pixel_air 1
#define RIGHT 0
#define LEFT 1
#define space_picture_start 10
//#define space_picture_loop 30
//#define space_picture_end 62
#define space_picture_loop 7
#define space_picture_end 13
#define pump_speed 80 //80
#define pump_steps 200
#define pump_delay 100 //100
AF_Stepper pump_liquid(pump_steps, 2);
AF_Stepper pump_air(pump_steps, 1);
unsigned char PMode = PM_stop;
unsigned int wait_cnt;
/*
void draw_pixel_air() {
if (pump_air_cnt < 2) {
pump_air.step(66, BACKWARD, SINGLE);
pump_air_cnt++;
} else {
pump_air.step(68, BACKWARD,SINGLE);
pump_air_cnt = 0;
}
pump_air.release();
delay(5);
}
void draw_pixel_liquid() {
if (pump_liquid_cnt < 2) {
pump_liquid.step(66, BACKWARD, SINGLE);
pump_liquid_cnt++;
} else {
pump_liquid.step(68, BACKWARD, SINGLE);
pump_liquid_cnt = 0;
}
pump_liquid.release();
delay(5);
}
*/
void draw_pixel_air() {
pump_air.step(200, FORWARD, DOUBLE);
pump_air.release();
//delay(5);
}
void draw_pixel_liquid() {
pump_liquid.step(200, FORWARD, DOUBLE);
pump_liquid.release();
//delay(5);
}
void copy_line(unsigned char ucline, const unsigned char *pattern) {
unsigned char uc1;
for (uc1 = 0; uc1 < 3; uc1++) {
display_row[uc1] = pattern[(ucline * 3) + uc1];
}
}
void draw_line(unsigned ucdirection) {
unsigned char uc1, uc2;
if (ucdirection == LEFT) {
for (uc1 = 0; uc1 < 3; uc1++)
for (uc2 = 0; uc2 < 8; uc2++) {
if ((display_row[uc1] << uc2) & 0x80) {
if (last_pixel != pixel_liquid) delay(pump_delay);
draw_pixel_liquid();
last_pixel = pixel_liquid;
} else {
if (last_pixel != pixel_air) delay(pump_delay);
draw_pixel_air();
last_pixel = pixel_air;
}
}
} else {
for (uc1 = 3; uc1 > 0; uc1--)
for (uc2 = 0; uc2 < 8; uc2++) {
if ((display_row[uc1 - 1] >> uc2) & 0x01) {
if (last_pixel != pixel_liquid) delay(pump_delay);
draw_pixel_liquid();
last_pixel = pixel_liquid;
} else {
if (last_pixel != pixel_air) delay(pump_delay);
draw_pixel_air();
last_pixel = pixel_air;
}
}
}
}
void draw_empty_space(unsigned char ucSpace) {
unsigned char uc1;
for (uc1 = 0; uc1 < ucSpace; uc1++) draw_pixel_air();
}
void draw_picture(const unsigned char *pattern) {
//draw_empty_space(space_picture_start);
copy_line(0, pattern);
draw_line(RIGHT);
draw_empty_space(space_picture_loop);
copy_line(1, pattern);
draw_line(LEFT);
draw_empty_space(space_picture_loop);
copy_line(2, pattern);
draw_line(RIGHT);
draw_empty_space(space_picture_loop);
copy_line(3, pattern);
draw_line(LEFT);
draw_empty_space(space_picture_loop);
copy_line(4, pattern);
draw_line(RIGHT);
draw_empty_space(space_picture_loop);
copy_line(5, pattern);
draw_line(LEFT);
draw_empty_space(space_picture_end);
}
void check_switches()
{
static byte previousstate[NUMBUTTONS];
static byte currentstate[NUMBUTTONS];
static long lasttime;
byte index;
if (millis() < lasttime) {
lasttime = millis(); // we wrapped around, lets just try again
}
if ((lasttime + DEBOUNCE) > millis()) {
return; // not enough time has passed to debounce
}
// ok we have waited DEBOUNCE milliseconds, lets reset the timer
lasttime = millis();
for (index = 0; index < NUMBUTTONS; index++) {
justpressed[index] = 0; // when we start, we clear out the "just" indicators
justreleased[index] = 0;
currentstate[index] = digitalRead(buttons[index]); // read the button
if (currentstate[index] == previousstate[index]) {
if ((pressed[index] == HIGH) && (currentstate[index] == HIGH)) {
// just pressed
justpressed[index] = 1;
}
else if ((pressed[index] == LOW) && (currentstate[index] == LOW)) {
// just released
justreleased[index] = 1;
}
pressed[index] = !currentstate[index]; // remember, digital HIGH means NOT pressed
}
//Serial.println(pressed[index], DEC);
previousstate[index] = currentstate[index]; // keep a running tally of the buttons
}
}
byte thisSwitch_justPressed() {
byte thisSwitch = 255;
check_switches(); //check the switches & get the current state
for (byte i = 0; i < NUMBUTTONS; i++) {
current_keystate[i] = justpressed[i];
if (current_keystate[i] != previous_keystate[i]) {
if (current_keystate[i]) thisSwitch = i;
}
previous_keystate[i] = current_keystate[i];
}
return thisSwitch;
}
void setup() {
byte i;
Serial.begin(9600); // set up Serial library at 9600 bps
Serial.println("bit.flow!");
// set input pins for keys
pump_liquid.setSpeed(pump_speed); // 100 rpm
pump_air.setSpeed(pump_speed); // 100 rpm
pump_liquid.release();
pump_air.release();
pump_air_cnt = 0;
pump_liquid_cnt = 0;
// Make input on switch pins
for (i = 0; i < NUMBUTTONS; i++) {
pinMode(buttons[i], INPUT);
}
}
void loop() {
byte thisSwitch = thisSwitch_justPressed();
switch (thisSwitch)
{
case buttonStop:
Serial.println("switch Stop just pressed");
PMode = PM_stop;
break;
case buttonDemo:
Serial.println("switch demo just pressed");
PMode = PM_demo;
break;
case buttonTest:
Serial.println("switch test pattern just pressed");
PMode = PM_test;
break;
case buttonPumpLiquid:
Serial.println("switch pump liquid just pressed");
PMode = PM_pump_liquid;
break;
case buttonPumpAir:
Serial.println("switch pump air just pressed");
PMode = PM_pump_air;
break;
}
switch (PMode) {
case PM_stop:
pump_air.release();
pump_liquid.release();
break;
case PM_pump_air:
draw_pixel_air();
break;
case PM_pump_liquid:
draw_pixel_liquid();
break;
case PM_test:
draw_picture(test_pattern);
PMode = PM_stop;
break;
case PM_demo:
draw_picture(Maker_pattern);
wait_cnt = 0;
PMode = PM_wait;
break;
case PM_demo2:
draw_picture(Faire_pattern);
wait_cnt = 0;
PMode = PM_wait2;
break;
case PM_wait:
delay(10);
wait_cnt++;
if (wait_cnt > 6000) PMode = PM_demo2;
break;
case PM_wait2:
delay(10);
wait_cnt++;
if (wait_cnt > 6000) PMode = PM_demo;
break;
}
}
Comments