top of page
Screenshot_2020-08-26-21-00-11-08.png
Arduino Levitation

★ᴍᴀᴋᴇ ᴀ ʟᴇᴠɪᴛᴀᴛᴏʀ ᴡɪᴛʜ ᴀʀᴅᴜɪɴᴏ ɴᴀɴᴏ 

 

 If you’d like to experiment with ultrasonic levitation — making objects hover in mid-air using just the energy in sound waves!

you don’t need any scientific equipment, complicated control loop setups, or expensive kits. An Arduino, a stepper motor driver, and a repurposed distance sensor will do . . . .

 

Yeah, we admit it, our Micro Ultrasonic Levitator won’t levitate any heavy items. But it’s fascinating enough to watch tiny styrofoam balls hover like magic . . . .

Acoustic levitation is made possible through the fact that sound behaves as a wave.  When two sound waves intersect each other, they can either constructively or destructively interfere with each other.  (This is how noise - cancelling headphones work.)

This project uses an ultrasonic distance sensor to create a levitation effect. This works by creating "pockets" where two opposing sound waves interfere with each other. When an object is placed in the pocket it will stay there, seemingly hovering in place.

COMPONENTS AND SUPPLIES
IMG_4962B.jpg
Troubleshooting

It took me about fifteen minutes to get it working the first time, but after that it was pretty easy to get it going again.  Here are some things you can try if it doesn't work at first:

  • Make sure you wired everything correctly

  • Increase the voltage to the H-bridge (different battery)

  • Get a smaller piece of Styrofoam

  • Try a different position for the transmitters

  • Try adding the capacitors (if you didn't already)

  • If it still doesn't work, maybe something is broken: try a different set of transmitters or a new battery. 

IMG_20200622_192322.jpg
Schematic
Download Button Ui 1.png
Circuit Diagram.jpg
Circuit Diagram 2.jpg
Code
Download Button Ui 1.png
CODE Window 5.png

#include <avr/sleep.h>
#include <avr/power.h>

#define N_PORTS 1 //1
#define N_DIVS 42 //30

#define WAIT_LOT(a) __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop");__asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop");__asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop");__asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop")
#define WAIT_MID(a) __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop");__asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop");__asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop")
#define WAIT_LIT(a) __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop"); __asm__ __volatile__ ("nop")


#define OUTPUT_WAVE(pointer, d) PORTC = pointer[d*N_PORTS + 0]

#define N_BUTTONS 6
//half a second
#define STEP_SIZE 1
#define BUTTON_SENS 2500
#define N_FRAMES 24

static byte frame = 0;

static byte animation[N_FRAMES][N_DIVS] =
{{0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa},
{0x9,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x6,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa},
{0x9,0x9,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x6,0x6,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa},
{0x9,0x9,0x9,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x6,0x6,0x6,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa},
{0x9,0x9,0x9,0x9,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x6,0x6,0x6,0x6,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa},
{0x9,0x9,0x9,0x9,0x9,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x6,0x6,0x6,0x6,0x6,0xa,0xa,0xa,0xa,0xa,0xa,0xa},
{0x9,0x9,0x9,0x9,0x9,0x9,0x5,0x5,0x5,0x5,0x5,0x5,0x6,0x6,0x6,0x6,0x6,0x6,0xa,0xa,0xa,0xa,0xa,0xa},
{0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x5,0x5,0x5,0x5,0x5,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0xa,0xa,0xa,0xa,0xa},
{0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x5,0x5,0x5,0x5,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0xa,0xa,0xa,0xa},
{0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x5,0x5,0x5,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0xa,0xa,0xa},
{0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x5,0x5,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0xa,0xa},
{0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x5,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0xa},
{0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6},
{0x5,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0xa,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6},
{0x5,0x5,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0xa,0xa,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6},
{0x5,0x5,0x5,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0xa,0xa,0xa,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6},
{0x5,0x5,0x5,0x5,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0xa,0xa,0xa,0xa,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6},
{0x5,0x5,0x5,0x5,0x5,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0xa,0xa,0xa,0xa,0xa,0x6,0x6,0x6,0x6,0x6,0x6,0x6},
{0x5,0x5,0x5,0x5,0x5,0x5,0x9,0x9,0x9,0x9,0x9,0x9,0xa,0xa,0xa,0xa,0xa,0xa,0x6,0x6,0x6,0x6,0x6,0x6},
{0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x9,0x9,0x9,0x9,0x9,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x6,0x6,0x6,0x6,0x6},
{0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x9,0x9,0x9,0x9,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x6,0x6,0x6,0x6},
{0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x9,0x9,0x9,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x6,0x6,0x6},
{0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x9,0x9,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x6,0x6},
{0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x9,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x6}};

CODE Window 5.png

void setup()
{

/*
for (int i = 0; i < (N_PORTS*N_DIVS); ++i){
animation[frame][i] = 0;
}

for (int i = 0; i < (N_PORTS*N_DIVS/2); ++i){
animation[frame][i] = 0b11111111;
}

for(int i = 0; i < N_DIVS; ++i){
if (i % 2 == 0){
animation[frame][i * N_PORTS] |= 0b00000001;
}else{
animation[frame][i * N_PORTS] &= 0b11111110;
}
}
*/
DDRC = 0b00001111; //A0 to A3 are the signal outputs
PORTC = 0b00000000;

pinMode(10, OUTPUT); //pin 10 (B2) will generate a 40kHz signal to sync
pinMode(11, INPUT_PULLUP); //pin 11 (B3) is the sync in
//please connect pin 10 to pin 11

for (int i = 2; i < 8; ++i)
{ //pin 2 to 7 (D2 to D7) are inputs for the buttons
pinMode(i, INPUT_PULLUP);
}

// generate a sync signal of 40khz in pin 10
noInterrupts();
// disable all interrupts
TCCR1A = bit (WGM10) | bit (WGM11) | bit (COM1B1); // fast PWM, clear OC1B on compare
TCCR1B = bit (WGM12) | bit (WGM13) | bit (CS10); // fast PWM, no prescaler
OCR1A = (F_CPU / 40000L) - 1;
OCR1B = (F_CPU / 40000L) / 2;
interrupts(); // enable all interrupts

// disable everything that we do not need
ADCSRA = 0; // ADC
power_adc_disable ();
power_spi_disable();
power_twi_disable();
power_timer0_disable();
//power_usart0_disable();

Serial.begin(115200);

byte* emittingPointer = &animation[frame][0];
byte buttonsPort = 0;

bool anyButtonPressed;
bool buttonPressed[N_BUTTONS];
short buttonCounter = 0;

LOOP:
while(PINB & 0b00001000); //wait for pin 11 (B3) to go low

OUTPUT_WAVE(emittingPointer, 0); buttonsPort = PIND; WAIT_LIT();
OUTPUT_WAVE(emittingPointer, 1); anyButtonPressed = (buttonsPort & 0b11111100) != 0b11111100; WAIT_MID();
OUTPUT_WAVE(emittingPointer, 2); buttonPressed[0] = buttonsPort & 0b00000100; WAIT_MID();
OUTPUT_WAVE(emittingPointer, 3); buttonPressed[1] = buttonsPort & 0b00001000; WAIT_MID();
OUTPUT_WAVE(emittingPointer, 4); buttonPressed[2] = buttonsPort & 0b00010000; WAIT_MID();
OUTPUT_WAVE(emittingPointer, 5); buttonPressed[3] = buttonsPort & 0b00100000; WAIT_MID();
OUTPUT_WAVE(emittingPointer, 6); buttonPressed[4] = buttonsPort & 0b01000000; WAIT_MID();
OUTPUT_WAVE(emittingPointer, 7); buttonPressed[5] = buttonsPort & 0b10000000; WAIT_MID();

CODE Window 5.png

OUTPUT_WAVE(emittingPointer, 8); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 9); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 10); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 11); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 12); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 13); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 14); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 15); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 16); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 17); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 18); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 19); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 20); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 21); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 22); WAIT_LOT();
OUTPUT_WAVE(emittingPointer, 23);


if( anyButtonPressed )
{
++buttonCounter;
if (buttonCounter > BUTTON_SENS)
{
buttonCounter = 0;

if (! buttonPressed[0] )
{
if( frame < STEP_SIZE )
{
frame = N_FRAMES-1;
}
else
{
frame-=STEP_SIZE;
}
}
else if (! buttonPressed[1] )
{
if ( frame >= N_FRAMES-STEP_SIZE )
{
frame = 0;
}
else
{
frame+=STEP_SIZE;
}
}
else if (! buttonPressed[2] )
{
frame = 0;
}
emittingPointer = & animation[frame][0];
}
}
else
{
buttonCounter = 0;
}

goto LOOP;

}

void loop(){

}

bottom of page