This was a fun experiment measuring the rotational speed (rpm) of a fidget spinner using an Arduino Uno, a hall effect sensor and a neodymium magnet. Then seeing just how fast I could make the spinner spin with a shop vac!
Measuring Spinner RPM every Revolution with a Hall Effect Sensor and Neodymium Magnet
The Hall effect sensor output was connected to the Arduino Uno digital input pin 2. TheĀ neodymium magnet was glued to a section of 8mm wooden dowel and pushed into one of the outer bearings on the fidget spinner.
// Fidget spinner RPM counter
// techydiy.org
// https://creativecommons.org/licenses/by-nc-sa/4.0/
// Calculates and displays the RPM of a fidget spinner for every revolution
// Hall effect sensor connected to digital pin 2
// Uses the lcd ibrary from F Malpartida
// https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4 ,5, 6, 7, 3, POSITIVE);
const byte ledpin = 13; // Led output
const byte interruptPin = 2; // Hall effect sensor connected to this pin
volatile unsigned int spins =0; // Total spins
volatile unsigned long timenow=0; //
volatile unsigned long lasttime=0; // Last time in millis interupt routine was called
volatile unsigned int rpm=0; // Current rpm
unsigned int displayrpm=10; // Displayed rpm
unsigned int displayspins = 999; // Displayed spins
volatile unsigned int maxrpm=0; // Maximum rpm
void setup()
{
lcd.begin(16,2);
lcd.clear();
pinMode(ledpin, OUTPUT); // Onboard LED output
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), countspin, RISING); // Digital pin 2 hardware interrupt
}
void loop() {
if (rpm != displayrpm | spins != displayspins) { // Only update the display if the RPM or spins change
displayrpm = rpm; // Update rpm displayed
displayspins = spins; // Update spins displayed
lcd.setCursor(0,0);
lcd.print("RPM ");
lcd.setCursor(4,0);
lcd.print(rpm, DEC); // Print the RPM on the lcd display
lcd.print(" ");
lcd.setCursor(10,0);
lcd.print(maxrpm, DEC); // Print the Max RPM on the lcd display
lcd.setCursor(0,1);
lcd.print("SPINS "); // Print the no. of spin on the lcd display
lcd.print(spins);
lcd.print(" ");
}
}
// Interupt service routine for digital pin 2
// connected to the hall effect sensor
void countspin()
{
digitalWrite(ledpin, !digitalRead(ledpin)); // Toggle LED
spins++; // Increment spins counter
timenow=micros(); // Read current time
rpm = 60000000 / ( timenow - lasttime ) ; // Calculate RPM
lasttime=timenow; // Store current time
if(rpm > maxrpm){ // Update max rpm if it has increased
maxrpm=rpm;
}
}
3144 Hall effect switch datasheet
Measuring the Spinner RPM once every second
The following sketch uses a timer 1 interrupt and a hardware interrupt to measure the RPM over a one second period.
// Fidget spinner RPM ccunter
// techydiy.org
// https://creativecommons.org/licenses/by-nc-sa/4.0/
// Measures RPM over a 1 second period
// with a minimum resolution of 60 rpm
// Increase the period to increase the resolution
// Uses the lcd ibrary from F Malpartida
// https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
//
// and the Timer1 library
// https://playground.arduino.cc/Code/Timer1
#include <TimerOne.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4 ,5, 6, 7, 3, POSITIVE);
const byte ledpin = 13; // Onboard Led
const byte interruptPin = 2; // Hall effect sensor connected to this pin
volatile unsigned int spins =0; // Number of spins counter
volatile unsigned int rpm=0; // The current RPM value
volatile unsigned int maxrpm=0; // The maximum RPM value
unsigned int displayrpm=9999; // The currently displayed RPM value
void setup()
{
lcd.begin(16,2); // Initialise lcd display
lcd.clear(); // Clear lcd display
pinMode(ledpin, OUTPUT); // Onboard LED output
Timer1.initialize(1000000); // set a timer of length 1000000 microseconds (or 1 second )
Timer1.attachInterrupt( timerIsr ); // attach the service routine
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), countspin, RISING); // Digital pin 2 hardware interrupt
}
void loop()
{
if (rpm != displayrpm) { // Only update if the RPM value has changed
lcd.setCursor(0,0);
lcd.print("RPM ");
lcd.print(rpm, DEC); // Display RPM
lcd.print(" ");;
lcd.setCursor(0,2);
lcd.print("MAX ");
lcd.print(maxrpm, DEC); // Display maximum RPM achieved
}
}
// Timer Interupt
// Called every one second
// Calculates rpm and updates maximum rpm
void timerIsr()
{
rpm = spins * 60 ; // Calculate RPM
spins = 0; // Reset spin counter
if(rpm > maxrpm){ // Update maximum RPM
maxrpm=rpm;
}
}
// Hardware interupt for digitsl input 2
// which is connected to the hall effect sensor.
// When the input transitions from low to high
// it increments the spins counter and toggles the led.
void countspin()
{
digitalWrite(ledpin, !digitalRead(ledpin)); // Toggle LED
spins++; // Increment spins counter
}
How fast can the spinner spin with a shop vac?
To test the Arduino tachometer at higher rpm values, I used a shop vac. The bottom of a drinks bottle was cut off and then the bottle inserted into the shop vac blower output. I also added a small nozzle to increase the air speed.
Measuring the Spinner RPM with the timer input capture unit
It is also possible to make the Arduino timer do most of the work.
The hall effect sensor is connected to pin 8.
The timer counts the prescaled clock and then stores the count when the magnet passes over the hall effect sensor. An interrupt is generated and this then retrieves the stored count and uses it calculate the rpm value.
The prescaler is used to reduce the clock frequency.
// Fidget spinner RPM counter
// techydiy.org
// https://creativecommons.org/licenses/by-nc-sa/4.0/
// Calculates and displays the RPM of a fidget spinner for every revolution
// Hall effect sensor connected to ICP1 digital pin 8
// Using timer input capture unit
// Uses the lcd ibrary from F Malpartida
// https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
//
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4 ,5, 6, 7, 3, POSITIVE);
const byte ledpin = 13; // Led output
const byte interruptPin = 8; // Hall effect sensor connected to this pin
volatile unsigned int spins =0; // Total spins
volatile unsigned int rpm=0; // Current rpm
unsigned int displayrpm=10; // Displayed rpm
unsigned int displayspins = 999; // Displayed spins
volatile unsigned int maxrpm=0; // Maximum rpm
volatile byte countoverflow=0; // counter overflows
void setup()
{
lcd.begin(16,2);
lcd.clear();
pinMode(ledpin, OUTPUT); // Onboard LED output
pinMode(interruptPin, INPUT_PULLUP); // Hall effect sensor connected to this pin
noInterrupts();
TCCR1A = 0; // Normal wave generation mode
TCCR1B = bit(ICNC1) | bit(ICES1) | bit(CS12) | bit(CS10); // Timer counter contol register
// Noise canceller, Edge detect
// /1024 prescale clock frequency
// TCCR1B = bit(ICES1) | bit(CS12) | bit(CS10); // Noise canceller off instead
TIMSK1 = bit(ICIE1) | bit(TOIE1); // Timer interrupt mask register
// Input capture interrupt enable, Timer overflow interrupt enable
interrupts();
}
void loop() {
if (rpm != displayrpm | spins != displayspins) { // Only update the display if the RPM or spins change
displayrpm = rpm; // Update rpm displayed
displayspins = spins; // Update spins displayed
lcd.setCursor(0,0);
lcd.print("RPM ");
lcd.setCursor(4,0);
lcd.print(rpm, DEC); // Print the RPM on the lcd display
lcd.print(" ");
lcd.setCursor(10,0);
lcd.print(maxrpm, DEC); // Print the Max RPM on the lcd display
lcd.setCursor(0,1);
lcd.print("SPINS "); // Print the no. of spin on the lcd display
lcd.print(spins);
lcd.print(" ");
}
}
// Input capture interrupt routine
ISR(TIMER1_CAPT_vect){
TCNT1 = 0; // Reset counter to zero
rpm = 60000000 / (( ICR1 + countoverflow * 65535 ) * 64 ); // Total count
countoverflow = 0;
// digitalWrite(ledpin, !digitalRead(ledpin)); // Toggle LED
spins++; // Increment spins counter
if(rpm > maxrpm){ // Update max rpm if it has increased
maxrpm=rpm;
}
}
// Counter overflow interrupt routine
ISR(TIMER1_OVF_vect){
TCNT1 = 0; // Reset counter to zero
countoverflow++; // Increment the counter over flows
digitalWrite(ledpin, !digitalRead(ledpin)); // Toggle LED
}
Measuring Spinner RPM with a Light Dependent Resistor (LDR)
Measuring the spinner speed is also possible using a light source along with a photo transistor or a light dependent resistor (LDR) as a receiver.
The fidget spinner breaks the light beam and the Arduino measures the resulting pulse.
In this case I have used an LDR and a 1K resistor connected as a potential divider, with the output connected to pin 8.
The sketch takes account of the number of arms on the fidget spinner and once again uses the input capture interrupt.
// Fidget spinner RPM counter
// techydiy.org
// https://creativecommons.org/licenses/by-nc-sa/4.0/
// Calculates and displays the RPM of a fidget spinner for every part revolution
// Light dependent resistor (LDR) connected to ICP1 digital pin 8
// Using timer input capture unit
// Uses the lcd ibrary from F Malpartida
// https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
//
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4 ,5, 6, 7, 3, POSITIVE);
const byte spinnerarms = 3; // The number of arms on the spinner
const byte ledpin = 13; // Led output
const byte interruptPin = 8; // LDR connected to this pin
volatile unsigned int spins =0; // Total spins
volatile unsigned int rpm=0; // Current rpm
unsigned int displayrpm=10; // Displayed rpm
unsigned int displayspins = 999; // Displayed spins
volatile unsigned int maxrpm=0; // Maximum rpm
volatile byte countoverflow=0; // counter overflows
void setup()
{
lcd.begin(16,2);
lcd.clear();
pinMode(ledpin, OUTPUT); // Onboard LED output
pinMode(interruptPin, INPUT_PULLUP); // Hall effect sensor connected to this pin
noInterrupts(); // Turn off interrupts
TCCR1A = 0; // Timer counter contol register. Normal wave generation mode
TCCR1B = bit(ICNC1) | bit(ICES1) | bit(CS12) | bit(CS10); // Timer counter contol register
// Noise canceller, Edge detect
// /1024 prescale clock frequency
// TCCR1B = bit(ICES1) | bit(CS12) | bit(CS10); // Noise canceller off instead
TIMSK1 = bit(ICIE1) | bit(TOIE1); // Timer interrupt mask register
// Input capture interrupt enable, Timer overflow interrupt enable
interrupts(); // Turn interrupts on
}
void loop() {
if (rpm != displayrpm | spins != displayspins) { // Only update the display if the RPM or spins change
displayrpm = rpm; // Update rpm displayed
displayspins = spins; // Update spins displayed
lcd.setCursor(0,0);
lcd.print("RPM ");
lcd.setCursor(4,0);
lcd.print(rpm, DEC); // Print the RPM on the lcd display
lcd.print(" ");
lcd.setCursor(10,0);
lcd.print(maxrpm, DEC); // Print the Max RPM on the lcd display
lcd.setCursor(0,1);
lcd.print("SPINS "); // Print the no. of spin on the lcd display
lcd.print(spins / spinnerarms ); // Adjust for part spins
lcd.print(" ");
}
}
// Input capture interrupt routine
ISR(TIMER1_CAPT_vect){
TCNT1 = 0; // Reset counter to zero
rpm = 60000000 / (( ICR1 + countoverflow * 65535 ) * 64 * spinnerarms); // Calculate RPM
countoverflow = 0;
digitalWrite(ledpin, !digitalRead(ledpin)); // Toggle LED
spins++; // Increment spins counter
if(rpm > maxrpm){ // Update max rpm if it has increased
maxrpm=rpm;
}
}
// Counter overflow interrupt routine
ISR(TIMER1_OVF_vect){
TCNT1 = 0; // Reset counter to zero
countoverflow++; // Increment the counter over flows
}




