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.
  Foot-counter for 16mm synchronizer, based on debounce example and ToneAC library for louder piezo. 
  Attach a reed switch or opto sensor to the sync block for 1 pulse per foot.
  Uses toneAC library to drive piezo:
  toneAC( frequency [, volume [, length [, background ]]] ) - Play a note.

    frequency - Play the specified frequency indefinitely, turn off with toneAC().
    volume - [optional] Set a volume level. (default: 10, range: 0 to 10 [0 = off])
    length - [optional] Set the length to play in milliseconds. (default: 0 [forever], range: 0 to 2^32-1)
    background - [optional] Play note in background or pause till finished? (default: false, values: true/false)

toneAC() - Stop output.

noToneAC() - Same as toneAC().

#include "toneAC.h" //This alternative library is much louder than the "tone" library, but needs specific pins (9 & 10)

// constants won't change. They're used here to set pin numbers:
const int buttonPin = 2;    // the number of the pushbutton pin
const int ledPin = 13;      // the number of the LED pin

// Variables will change:
int ledState = LOW;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
int feetCount = 0;

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 5;    // the debounce time; increase if the output flickers

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

  // set initial LED state
  digitalWrite(ledPin, ledState);

  Serial.println("Footage Counter RESET");

  toneAC(2300, 10, 50);
  toneAC(2300, 10, 50);

void loop() {
  // read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // increment count on high-going transition of button
      if (buttonState == HIGH) {
        ledState = !ledState;
        Serial.println(String(feetCount) + " ft");
        if (feetCount % 100 == 0 && feetCount > 1) {
          toneAC(2500, 10, 3000);
        } else if (feetCount % 10 == 0 && feetCount > 1) {
          toneAC(2300, 10, 75);
        } else {
        toneAC(2000, 10, 20);

  // set the LED:
  //digitalWrite(ledPin, ledState);

  // save the reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;