Project showcase
Crazy Arduino Hose Display

Crazy Arduino Hose Display © GPL3+

How to make a unique display from a transparent hose and an Arduino UNO.

  • 20,376 views
  • 5 comments
  • 68 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)

About this project

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.

Code

FirmwareC/C++
for Arduino Uno
/*
 *  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 &amp; 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;
  
  }



}

Schematics

Schematic Overview
Block diagram
Bild1 iatxw57zzq

Comments

Similar projects you might like

GPS Location Display With GPS And TFT Display Shields

Project tutorial by Boian Mitov

  • 8,888 views
  • 5 comments
  • 23 respects

Soil Moisture Sensor With LCD Display

Project tutorial by Patel Darshil

  • 19,499 views
  • 4 comments
  • 37 respects

Nipkow Disk Based Digital Display Device

Project showcase by christopheArduino

  • 15,811 views
  • 8 comments
  • 51 respects

Lightpipe 7-Segment Display

Project tutorial by Brian Lough

  • 5,403 views
  • 4 comments
  • 21 respects

Rule Sony Car Stereo Display with LC75829

by pdio

  • 1,641 views
  • 0 comments
  • 8 respects

Digital Clock with Mirrored Display Driven by Accelerometers

Project showcase by LAGSILVA

  • 14,396 views
  • 1 comment
  • 38 respects
Add projectSign up / Login