Audible 16mm Footage Counter

I modified a 16mm film synchronizer to add an audible footage counter. This helps me spool down large loads of film in the dark. Conventional wisdom says that 42 turns of the (4:1 geared) rewinds will fill a 100ft daylight spool. I was curious to see if that’s accurate. (It is.) More importantly, I can barely count without using my fingers, so why not make a robot do the work!

What I did

  • Cleaned the synchronizer and checked for scratches. Don’t wanna scratch the film!
  • Glued a magnet to one of the gangs, with a matching reed switch on the frame. Each pass of the magnet will momentarily close the reed switch. 1 pulse = 1 foot.
  • Mounted a handy mint box enclosure containing an Arduino with a piezo buzzer and reset button. Each foot is marked with a beep, each 10th foot with a higher beep, until finally 100ft causes a long beep at a still higher frequency. (I chose frequencies within the resonant band of the piezo to get maximum volume.)
  • If you are using this in the dark, remember to remove or cover the Arduino power LED! If your board has an LED on pin 13, it will flash during boot/reset, so cover that too.

How It Sounds

Arduino Code

  • Requires the ToneAC library
  • At boot/reset you’ll hear a few beeps to let you know it’s running
  • You can monitor the count via serial for debugging
  • If you uncomment the LED lines, Pin 13 will go high for odd feet & low for even feet.
  1. /*
  2.   Foot-counter for 16mm synchronizer, based on debounce example and ToneAC library for louder piezo. 
  3.   
  4.   Attach a reed switch or opto sensor to the sync block for 1 pulse per foot.
  5.   
  6.   Uses toneAC library to drive piezo:
  7.   
  8.   toneAC( frequency [, volume [, length [, background ]]] ) - Play a note.
  9.  
  10.     frequency - Play the specified frequency indefinitely, turn off with toneAC().
  11.     volume - [optional] Set a volume level. (default: 10, range: 0 to 10 [0 = off])
  12.     length - [optional] Set the length to play in milliseconds. (default: 0 [forever], range: 0 to 2^32-1)
  13.     background - [optional] Play note in background or pause till finished? (default: false, values: true/false)
  14.  
  15. toneAC() - Stop output.
  16.  
  17. noToneAC() - Same as toneAC().
  18. */
  19.  
  20. #include "toneAC.h" //This alternative library is much louder than the "tone" library, but needs specific pins (9 & 10)
  21.  
  22. // constants won't change. They're used here to set pin numbers:
  23. const int buttonPin = 2;    // the number of the pushbutton pin
  24. const int ledPin = 13;      // the number of the LED pin
  25.  
  26. // Variables will change:
  27. int ledState = LOW;         // the current state of the output pin
  28. int buttonState;             // the current reading from the input pin
  29. int lastButtonState = LOW;   // the previous reading from the input pin
  30. int feetCount = 0;
  31.  
  32. // the following variables are unsigned longs because the time, measured in
  33. // milliseconds, will quickly become a bigger number than can be stored in an int.
  34. unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
  35. unsigned long debounceDelay = 5;    // the debounce time; increase if the output flickers
  36.  
  37. void setup() {
  38.   Serial.begin(9600);
  39.   pinMode(buttonPin, INPUT_PULLUP);
  40.   pinMode(ledPin, OUTPUT);
  41.  
  42.   // set initial LED state
  43.   digitalWrite(ledPin, ledState);
  44.  
  45.   Serial.println("Footage Counter RESET");
  46.  
  47.   toneAC(2300, 10, 50);
  48.   delay(50);
  49.   toneAC(2300, 10, 50);
  50.   delay(50);
  51. }
  52.  
  53. void loop() {
  54.   // read the state of the switch into a local variable:
  55.   int reading = digitalRead(buttonPin);
  56.  
  57.   // check to see if you just pressed the button
  58.   // (i.e. the input went from LOW to HIGH), and you've waited long enough
  59.   // since the last press to ignore any noise:
  60.  
  61.   // If the switch changed, due to noise or pressing:
  62.   if (reading != lastButtonState) {
  63.     // reset the debouncing timer
  64.     lastDebounceTime = millis();
  65.   }
  66.  
  67.   if ((millis() - lastDebounceTime) > debounceDelay) {
  68.     // whatever the reading is at, it's been there for longer than the debounce
  69.     // delay, so take it as the actual current state:
  70.  
  71.     // if the button state has changed:
  72.     if (reading != buttonState) {
  73.       buttonState = reading;
  74.  
  75.       // increment count on high-going transition of button
  76.       if (buttonState == HIGH) {
  77.         ledState = !ledState;
  78.         Serial.println(String(feetCount) + " ft");
  79.         if (feetCount % 100 == 0 && feetCount > 1) {
  80.           toneAC(2500, 10, 3000);
  81.         } else if (feetCount % 10 == 0 && feetCount > 1) {
  82.           toneAC(2300, 10, 75);
  83.         } else {
  84.         toneAC(2000, 10, 20);
  85.         }
  86.         feetCount++;
  87.       }
  88.     }
  89.   }
  90.  
  91.   // set the LED:
  92.   //digitalWrite(ledPin, ledState);
  93.  
  94.   // save the reading. Next time through the loop, it'll be the lastButtonState:
  95.   lastButtonState = reading;
  96. }