// Adafruit Circuit Playground Fidget Spinner Tachometer // // This sketch uses the light sensor built in to Circuit Playground // to detect the speed (in revolutions per second) of a fidget spinner. // Load the sketch on a Circuit Playground (either classic or express) // and notice the first three NeoPixels will turn on bright white. // Hold a spinning fidget spinner very close to (but not touching) the // light sensor (look for the eye graphic on the board, it's right // below the three lit NeoPixels) and look at the serial monitor // to see it print out the speed of the spinner. This works best // holding the spinner perpendicular to the sensor, like: // || // || <- Spinner // || // ________ <- Circuit Playground // // Author: Tony DiCola // License: MIT License (https://en.wikipedia.org/wiki/MIT_License) #include // Configuration values. You don't have to change these but they're // interesting to adjust the logic. If your spinner has more or less // than three arms be sure to change the SPINNER_ARMS value below: #define SPINNER_ARMS 3 // Number of arms on the fidget spinner. // This is used to calculate the true // revolutions per second of the spinner // as one full revolution of the spinner // will actually see this number of cycles // pass by the light sensor. Set this to // the value 1 to ignore this calculation // and just see the raw cycles / second. #define SAMPLE_DEPTH 512 // How many samples to take when measuring // the spinner speed. The larger this value // the more memory that will be consumed but // the slower a spinner speed that can be // detected (larger sample depths mean longer // period waves can be detected). You're // limited by the amount of memory on the // board for this value. On a classic // Circuit Playground with 2kb of memory // you can only go up to about 512 or so (each // sample is 2 bytes, so 1kb of space total). // On an express board you can go much higher, // like up to 10240 for 20kb of sample data. #define SAMPLE_PERIOD_US 1500 // Amount of time in microseconds to delay // between each light sensor sample. This is // a balance between how fast and slow of // a spinner reading that can be measured. // The higher this value the slower a spinner // you can measure, but at the tradeoff of // not accurately measuring fast spinners. // Low values (even 0) mean very fast speeds // can be detected but slow speeds (below 10hz) // are harder to detect. You can increase the // sample depth to help improve the range // of detection speeds, but there's a limit // based on the memory available. #define THRESHOLD 400 // How big the amplitude of a cyclic // signal has to be before the measurement // logic kicks in. This is a value from // 0 to 1023 and might need to be adjusted // up or down if the detection is too // sensitive or not sensitive enough. // Raising this value will make the detection // less sensitive and require a very large // difference in amplitude (i.e. a very close // or highly reflective spinner), and lowering // the value will make the detection more // sensitive and potentially pick up random // noise from light in the room. #define MEASURE_PERIOD_MS 1000 // Number of milliseconds to wait // between measurements. Default is // one second (1000 milliseconds). void setup() { // Initialize serial monitor at 115200 baud. Serial.begin(115200); // Initialize CircuitPlayground library and turn on the first 3 pixels (near the light sensor) // to pure white at maximum brightness. CircuitPlayground.begin(255); // 255 means set pixel colors to max brightness. CircuitPlayground.clearPixels(); CircuitPlayground.setPixelColor(0, 255, 255, 255); // Set pixel 0, 1, 2 to white. CircuitPlayground.setPixelColor(1, 255, 255, 255); CircuitPlayground.setPixelColor(2, 255, 255, 255); } void loop() { // Pause between measurements. delay(MEASURE_PERIOD_MS); // Collect samples from the light sensor as quickly as possible. // Keep track of the amount of time (in microseconds) between samples // so the sampling frequency can later be determined. uint16_t samples[SAMPLE_DEPTH] = {0}; uint32_t start = micros(); for (int i=0; i midpoint)) || ((p0 > midpoint) && (p1 < midpoint))) { crossings += 1; } } // Compute signal frequency, RPM, and period. // The period is the amount of time it takes for a complete // sine wave cycle to occur. You can calculate this by dividing the // amount of time that elapsed during the measurement period by the // number of midpoint crossings cut in half (because each complete // sine wave cycle will have 2 midpoint crossings). However since // fidget spinners have multiple arms you also divide by the number // of arms to normalize the period into a value that represents the // time taken for a complete revolution of the entire spinner, not // just the time between one arm and the next. float period = elapsed / (crossings / SPINNER_ARMS / 2.0); // Once the period is calculated it can be converted into a frequency // value (i.e revolutions per second, how many times the spinner spins // around per second) and more common RPM value (revolutions per minute, // just multiply frequency by 60 since there are 60 seconds in a minute). float frequency = 1.0 / period; float rpm = frequency * 60.0; // Print out the measured values! Serial.print("Frequency: "); Serial.print(frequency, 3); Serial.print(" (hz)\t\tRPM: "); Serial.print(rpm, 3); Serial.print("\t\tPeriod: "); Serial.print(period, 3); Serial.println(" (seconds)"); }