More info at http://wp.josh.com/2018/05/07/deconstructing-kitty
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This Arduino sketch drives a coil to make a Lucky Cat wave | |
// The coil should be connected directly to pins A0 and ground | |
// It uses the voltage generated by the magnet passing the coil to time its pulses | |
// More info at... | |
// http://wp.josh.com/2018/05/07/deconstructing-kitty | |
#define LED_PIN 13 // Show a flash on this pin everytime we pulse the coil | |
void setup() { | |
Serial.begin(9600); // setup serial | |
pinMode( A0 , INPUT ); | |
pinMode(LED_PIN, OUTPUT); | |
} | |
// Keep track of the highest value since we last triggered | |
// so we can detect when the bob is moving away from the coil just | |
// as it switngs past bottom dead center | |
int peak=0; | |
int triggered=0; | |
// The values from the ADC are very noisy, so we make a crude | |
// averaging filter here | |
#define BUFFER_COUNT 25 | |
int buffer[BUFFER_COUNT]; | |
int nextSlot=0; | |
int runningTotal=0; | |
// Push the magnet away by energizing the coil | |
void push(unsigned int ms) { | |
pinMode( A0 , OUTPUT ); // Switch the input pin to output so we can drive it. | |
digitalWrite( A0 , HIGH ); | |
digitalWrite(LED_PIN, HIGH); // Flash the LED to show the coil is on | |
delay(ms); | |
digitalWrite( A0 , LOW ); | |
digitalWrite(LED_PIN, LOW); | |
pinMode( A0 , INPUT ); // Go back to input mode so we can keep sensing the back voltage | |
} | |
void nudge() { // A very short and low power nudge | |
push(1); | |
} | |
void kick() { // A big kick to get it swinging from rest | |
push(50); | |
} | |
#define KICKCOUNT_TIMEOUT_MS (5*1000) // Give a kick if we have not seen a swing in 5 seconds to get things started | |
unsigned long nextkick=0; | |
void resetKickTimeout() { | |
nextkick = millis() + KICKCOUNT_TIMEOUT_MS; | |
} | |
// Slow down the data feed back to the Arduino plotter so it doesn't run too | |
// fast to see | |
unsigned long nextplot=0; | |
// Display the trigger as the 2nd variable so we can see when we give a nudge | |
// on the plot. | |
int triggerflag=0; | |
#define PLOT_INTERVAL_MS 3 | |
void plot( int val1 ) { | |
if (millis() >= nextplot ) { | |
Serial.print(val1); // debug value | |
Serial.print(" "); // debug value | |
Serial.print(triggerflag ); // debug value | |
Serial.println(); | |
nextplot = millis() + PLOT_INTERVAL_MS; | |
triggerflag=0; | |
} | |
} | |
void loop() { | |
// If we have not seen a swing in a while then give it a kick to get it started | |
if ( millis() >= nextkick ) { | |
kick(); | |
resetKickTimeout(); | |
} | |
int val; | |
val = analogRead(A0); // read the input pin. We are lucky that the voltage of the coil | |
// falls in the range of the Arduino ADC or we would need some | |
// kind of scaling here | |
// Remove oldest value | |
runningTotal-=buffer[nextSlot]; | |
// Add new value | |
runningTotal += val; | |
buffer[nextSlot] = val; | |
// Bump to next slot | |
nextSlot++; | |
if (nextSlot==BUFFER_COUNT) { | |
nextSlot=0; | |
} | |
int smoothedvalue = runningTotal/BUFFER_COUNT; | |
if (triggered==0) { | |
if (smoothedvalue> peak ) { | |
peak = smoothedvalue; | |
} else if ( smoothedvalue < (peak*8)/10 ) { | |
// Trigger a nudge when we are at 80% of the most recent peak | |
// this helps prevent false triggers from noise and also | |
// I think gives a better force vector away rather than just up. | |
nudge(); | |
triggerflag=peak; | |
resetKickTimeout(); | |
triggered=peak; | |
} | |
} | |
// Display the data on the Arduino Serial Plotter | |
plot( smoothedvalue ); | |
if (smoothedvalue == 0 ) { // Clear a trigger if we are back down to zero | |
triggered=0; | |
peak=0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment