For this project I used a small DC motor. A sandpaper nail file is hot-glued to its shaft as a
makeshift propeller. There are three distinct circuits to this project. The first one is to control
the speed of the motor. A ne555 timer is used and PWM (pulse width modulation) for that purpose. PWM
works like a switch, turning the motor on and off very quickly.
The second circuit is an IR sensor. Every time the propeller
passes the IR-LED and photodiode a signal is generated and the output goes high. The third
circuit comprises of an ATmega328p chip and an OLED 128x64 display. It takes the signal from the IR circuit as input and
calculates the rotation speed and shows the results on the display.
Click
here
to see the video.
    
This circuit controls the speed of the motor by turning the potentiometer (P1). When the power is turned on the voltage on pin 2 and 6 will be below 1/3 of the 5 volt, because of the discharged capacitor (C1). This results in pin 3 going high. Very quickly the capacitor gets charged and the voltage on pin 2 and 6 reaches 2/3 of the voltage and this makes pin 3 to go low and turn on the discharge pin 7. The open pin 7 discharges the capacitor, dropping the voltage below 1/3 again. Pin 3 shows a square wave output. The percentage of the power turned on in relation to the complete cycle (period on + period off ) is called duty cycle. The duty cycle is controlled by the pot (P1).
When the resistance on both fixed ends (behind the diodes D1 & D2) are equal (the wiper is in the middle) the duty cycle is about 50%. The frequency should stay the same no matter the duty cycle. It's around 282 Hertz. Good explanation is found here!
IR sensor circuit consist of an Infrared emitting LED and a photodiode. The IR Led is turned on all
the time. The light can be seen using the camera of a smart phone. The photodiode
resistance changes according to the amount of IR light falling on
it, therefor the voltage drop across it also changes and by using a voltage comparator (like LM358)
the output changes accordingly. A photodiode has a P-N junction and is
reversed biased. The cathode (shorter lead) should be attached to the supply voltage and the anode
(longer lead) towards ground.
The LM358 is an op-amp and it turns the output high when the non-inverting terminal (+) has a higher
voltage than the inverting terminal (-).
In my project I used a micropic 12F675 as comparator and
it
is programmed to turn GP2 (pin 5) high when input pin GP0 (pin 7) has a higher voltage than GP1 (pin
6). The potentiometer is used for fine tuning.
When the propeller of the motor passes by it reflects the IR, the photodiode starts to
conduct and the comparator turns the output high. The red LED is an indicator of
that event. The output signal is input for the third circuit.
The ATmega328p chip receives the input signal on pin 2 (physical pin 4). Pin 2 is one of the two interrupt pins. In the Arduino sketch an interrupt routine is called on the rising edge (when pin 2 goes from low to high). A counter is incremented. After 3 seconds the interrupt routine is halted (detached) and the counter is used for the calculation of hertz and rpm. The result is written to the oled 128x64 display and the interrupt routine is restarted (attached). A variable used in an interrupt routine must be declared as volatile and do not do any calculations with this variable. It messes up the routine. Just copy it into another variable.
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin 4 (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define interrupt_pin 2
unsigned int rpm;
volatile unsigned int counter = 0;
unsigned long passedtime = 0;
void draw(unsigned int _rpm) {
unsigned int cnt;
cnt = counter / 6;
display.clearDisplay();
display.setTextSize(2); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
display.print("RPM:");
display.setCursor(0,16);
display.print(_rpm, DEC);
display.setCursor(0,32);
display.print("Hz:");
display.setCursor(0,48);
display.print(cnt, DEC);
display.display();
}
void isr() {
counter++;
}
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(2); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
display.print("START");
display.display();
attachInterrupt(digitalPinToInterrupt(interrupt_pin), isr, RISING);
passedtime = millis();
}
void loop() {
if ((millis() - passedtime) > 2999){
detachInterrupt(digitalPinToInterrupt(interrupt_pin)); //Interrupts are disabled
// noInterrupts(); no working
rpm = 10 * counter; // 60 * counter(12) / 2 / 3
draw(rpm);
counter = 0;
passedtime = millis();
attachInterrupt(digitalPinToInterrupt(interrupt_pin), isr, RISING);
}
}
The fidget spinner has a magnet in each of its 3 arms. The
electromagnet is made out of a plastic
spindle from a sewing machine and the wire used to wind onto the spindle is coated copper with an
enamel coating that must be removed from the ends to make an electrical connection. The wire is 34 AWG
and a drilling machine with variable speed is used to wind the coil.
Turning the electromagnet on and off is done by an IR sensor circuit, the same circuit as for the
tachometer.
Rotation direction depends on the positioning of the IR-pair as demonstrated in the video. To power the coil 9
volt is applied, but the IR-sensor circuit has a maximum of 5 volt. An LM7805 voltage regulator
brings the 9 volt down to 5. There are 4 bypass capacitors, 2 on the
input and 2 on the output. Their function is to smoothen out any spikes in the voltage.
Click
here
to see the video.
Electromagnet       IR sensor
The electromagnet is turned on when a magnet is near the reed switch. The contacts inside the switch close the circuit. The reed switch is also used as a clock signal for the decade counter 4017. Only 5 of the 10 possible LEDs are used. The 22 uF capacitor is necessary to smoothen the voltage spikes or the 4017 IC goes bonkers. The rotation speed is about 40 Hz using a strobe light app.
Click
here
to see the video.
"Simon says" is a memory game with 4 buttons, 4 LEDs and a speaker. Follow the pattern of lights and sounds and repeat the same combination. It starts with one light (+sound), then 2 and so on, until it reaches 10 and the game is over. As a player you have to memorize the series of lights and sounds and than reproduce it by pushing the buttons in the same order. The challenge was to write the Arduino sketch for this game. Click here to see the video.
#define NOTE_A4 440
#define NOTE_F4 349
#define NOTE_C5 523
#define NOTE_E5 659
#define NOTE_F3 175
#define NOTE_E3 165
#define NOTE_B2 123
#define NOTE_B4 494
#define NOTE_F5 698
#define startbutton 12
#define mic 13
int buttons[] = {0,1,2,3};
int segment7[] = {4,5,6,7};
int leds[] ={8,9,10,11};
int button_notes[] = {NOTE_A4,NOTE_F4,NOTE_C5,NOTE_E5};
int starttune[] = {NOTE_A4, NOTE_A4, NOTE_A4, NOTE_F4, NOTE_C5, NOTE_A4, NOTE_F4, NOTE_C5, NOTE_A4, NOTE_E5, NOTE_E5,
NOTE_E5, NOTE_F5, NOTE_C5, NOTE_A4, NOTE_F4, NOTE_C5, NOTE_A4};
// Array with the note durations: a quarter note has a duration of 4, half note 2 etc.
int startdurations[] = {4, 4, 4, 5, 16, 4, 5, 16, 2, 4, 4, 4, 5, 16, 4, 5, 16, 2};
int startleds[] = {8,8,8,9,10,8,8,9,8,11,11,11,9,10,8,9,10,8 };
// int starttempo = 120;
int failtune[] = { NOTE_F3, NOTE_E3, NOTE_F3, NOTE_E3, NOTE_B2};
int faildurations[] = { 8, 8, 4, 4, 1};
int failleds[] = { 11,10,11,10,8 };
//
int wintune[] = { NOTE_B4, NOTE_E5, NOTE_F5, NOTE_B4, NOTE_E5, NOTE_F5};
int windurations[] = { 8, 8, 8, 4, 4, 1};
int winleds[] = { 8,10,11,8,10,11 };
int turn; // 0 = first turn
long old = 5;
int random_array[10];
int input_array[10];
boolean failed = false;
void setup()
{
for (int i=0; i < 4; i++) {
pinMode(buttons[i], INPUT);
digitalWrite(buttons[i], HIGH); // 20K pull-up
pinMode(segment7[i], OUTPUT);
digitalWrite(segment7[i],LOW);
pinMode(leds[i], OUTPUT);
}
pinMode(mic, OUTPUT);
pinMode(startbutton, INPUT);
digitalWrite(startbutton, HIGH);
playTune(starttune,startdurations,startleds, 18);
delay(1000);
}
void playTune(int notes[], int durations[], int _leds[], int _size) {
for (int thisNote = 0; thisNote < _size; thisNote++) {
int _duration = 1000 / durations[thisNote]; //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
digitalWrite(_leds[thisNote], HIGH);
tone(mic, notes[thisNote], _duration);
int pauseBetweenNotes = _duration * 0.65; // to distinguish the notes, set a minimum time between them.
delay(pauseBetweenNotes);
digitalWrite(_leds[thisNote], LOW);
delay(pauseBetweenNotes);
// stop the tone playing:
noTone(mic);
}
}
void show_random(int act) { // 0 1 2 3 4 5
long val;
// show what has been recorded so far
for (int i=0; i < act; i++) {
val = random_array[i];
digitalWrite(leds[val],HIGH);
tone(mic,button_notes[val], 500);
delay(700);
digitalWrite(leds[val],LOW);
delay(500);
}
// add one memory to the array
val = random(0,4); // I don't like too many doubles
if (val == old) {
val = random(0,4);
} else {
old = val;
}
random_array[act] = val;
digitalWrite(leds[val],HIGH);
tone(mic,button_notes[val], 500);
delay(500);
digitalWrite(leds[val],LOW);
delay(500);
}
void update_7seg(int round) {
round++;
for (int i=0; i<4; i++) {
digitalWrite(segment7[i], LOW);
}
switch (round) {
case 1:
digitalWrite(segment7[0], HIGH);
break;
case 2:
digitalWrite(segment7[1], HIGH);
break;
case 3:
digitalWrite(segment7[0], HIGH);
digitalWrite(segment7[1], HIGH);
break;
case 4:
digitalWrite(segment7[2], HIGH);
break;
case 5:
digitalWrite(segment7[0], HIGH);
digitalWrite(segment7[2], HIGH);
break;
case 6:
digitalWrite(segment7[1], HIGH);
digitalWrite(segment7[2], HIGH);
break;
case 7:
digitalWrite(segment7[0], HIGH);
digitalWrite(segment7[1], HIGH);
digitalWrite(segment7[2], HIGH);
break;
case 8:
digitalWrite(segment7[3], HIGH);
break;
case 9:
digitalWrite(segment7[0], HIGH);
digitalWrite(segment7[3], HIGH);
break;
default:
break;
}
}
void get_input(int act) {
// act = 0 1 2 3 4 5
// from 0 to 0 or from 0 to 1
for (int i=0; i <= act; i++) {
int k = 0;
boolean button_pressed = false;
while (button_pressed == false) {
if (digitalRead(buttons[k]) == LOW) {
button_pressed = true;
if (random_array[i] == k) {
digitalWrite(leds[k],HIGH);
tone(mic,button_notes[k], 300);
delay(500);
digitalWrite(leds[k],LOW);
} else {
user_failed();
return;
}
while (digitalRead(buttons[k]) == LOW) {
// wait for the release of this button
}
}
k++;
if (k == 4) k=0;
} // while
} // for loop
update_7seg(act);
}
void user_failed() {
failed = true;
playTune(failtune, faildurations, failleds, 5);
}
void loop() {
update_7seg(10);
randomSeed(analogRead(0));
turn = 0;
failed = false;
while(digitalRead(startbutton) == HIGH) {
// wait
}
while (failed == false) {
delay(2000);
show_random(turn);
get_input(turn);
turn++;
if (turn == 10) {
if (failed == false) {
playTune(wintune, windurations, winleds, 6);
}
failed = true; // actually it's a win
}
}
}