Project tutorial
ArduMeter - Arduino Incident Light Meter

ArduMeter - Arduino Incident Light Meter © CC BY-NC

Build a simple incident light meter for old meterless film cameras

  • 5,730 views
  • 2 comments
  • 15 respects

Components and supplies

Apps and online services

About this project

This is a practice project to make an Arduino-based incident light meter for old meterless mechanical film cameras, probably more useful for portrait photography, since you'd have to measure direct light on subjects. And the results may not be as accurate or reliable as real light meters.

(Usually I just use an app on my phone, which is reliable enough for my amatuer film photography. I only own two or three meterless cameras which I use occasionally, so why bother to buy a real one that cost more than any of these cameras?)

The device is powered by a small power bank (connected to USB port of the Arduino); light meter info are updated on a 0.96" SSD1306 OLED each time you press the single button. The measured light level from the BH1750 light intensity sensor will be calculated into Exposure Values (EV). I set the incident-light meter calibration constant to 340 (reference). Aperture and ISO value can be set via two potentiometers. If you plug and Arduino on your computer you can get more detailed serial port data via serial port.

I used two mini breadboards glued back-to-back. But you can reproduce this on regular breadboards. The BH1750 model I used is called BH1750FVI/GY-30. GY-302 is practically the same with slightly different pin arrangement.

The library I used for the OLED display can be found here. The one for BH1750 is here.

Code

ArduMeterC/C++
Code for ArduMeter
/*
  ArduMeter (Arduino incident light meter for cameras)
  by Alan Wang

  BH1750/BH1750FVI digital light intensity sensor:
  VCC -> 3.3V
  GND -> GND
  SDA -> pin A4
  SCL -> pin A5
  ADD -> (none)

  0.96" SSD1306 OLED 128x64 display:
  VCC -> 3.3V
  GND -> GND
  SDA -> pin A4
  SCL -> pin A5

  1x push button
  leg 1 -> 3.3V
  leg 2 -> pin D5 with 10 kOhms pull-down resistor connect to GND

  2x 10 kOhms potentiometer (knob)
  leg 1 -> 3.3V
  leg 2 (middle) -> pin A2/A3 (max analog signal about 730-740)
  leg 3 -> GND

  The Arduino Nano is powered by a USB powerbank via USB cable.

*/

#include <math.h>
#include <Wire.h>
#include <BH1750.h> //for BH1750, https://github.com/claws/BH1750
#include "SSD1306Ascii.h" //for OLED, https://github.com/greiman/SSD1306Ascii
#include "SSD1306AsciiAvrI2c.h" //same as above

BH1750 bh1750LightMeter;
SSD1306AsciiAvrI2c oled1306;
bool started = false; //if user started to use light meter (pressed button)
bool buttonMeasurePressed; //status of measure light button
int knobApertureStatus; //status of aperture select knob
int prevKnobApertureStatus; //previous status of aperture select knob
int knobISOstatus; //status of ISO select knob
int prevKnobISOstatus; //previous status of ISO select knob
long lux; //illuminance
float EV; //exposure value
int ISO; //film or digital sensor sensitivity
const int incidentCalibration = 340; //incident light calibration const, see https://en.wikipedia.org/wiki/Light_meter#Calibration_constants

void setup() {
  //initialize
  pinMode(5, INPUT);
  Serial.begin(9600);
  Wire.begin();
  bh1750LightMeter.begin(0x21); //BH1750_ONE_TIME_HIGH_RES_MODE_2
  //"initialize" BH1750 by using it, or it will return NaN at the first time
  lux = bh1750LightMeter.readLightLevel();
  //initialize OLED
  oled1306.begin(&Adafruit128x64, 0x3C);
  oled1306.setFont(System5x7);
  oled1306.set2X();
  Serial.println("ArduMeter (Arduino incident light meter for camera) READY:");
  Serial.println("button -> measure light");
  Serial.println("knobs -> change aperature/ISO");
  oled1306.clear();
  oled1306.println("ArduMeter");
  oled1306.println("READY");
  oled1306.println("press");
  oled1306.println("button");
}

void loop() {
  //read status from button and potentiometers
  buttonMeasurePressed = digitalRead(5);
  knobApertureStatus = map(analogRead(2), 0, 730, 1, 16);
  knobISOstatus = map(analogRead(3), 0, 730, 1, 10);
  if (buttonMeasurePressed) {
    //measure light
    if (!started) started = true;
    displayExposureSetting(true);
  } else {
    //change aperture/ISO settings
    if ((knobApertureStatus != prevKnobApertureStatus || knobISOstatus != prevKnobISOstatus) && started) {
      displayExposureSetting(false);
    }
  }
  //record potentiometer previous status
  prevKnobApertureStatus = knobApertureStatus;
  prevKnobISOstatus = knobISOstatus;
  delay(250);
}

void displayExposureSetting(bool measureNewExposure) {
  float aperature;
  float shutterSpeed;
  //select aperature via potentiometer 1; the values are based on most common aperatures you'll find on digital and analog cameras
  switch (knobApertureStatus) {
    case 1: aperature = 1.4; break;
    case 2: aperature = 1.7; break;
    case 3: aperature = 2; break;
    case 4: aperature = 2.8; break;
    case 5: aperature = 3.5; break;
    case 6: aperature = 4; break;
    case 7: aperature = 4.5; break;
    case 8: aperature = 5.6; break;
    case 9: aperature = 6.3; break;
    case 10: aperature = 8; break;
    case 11: aperature = 10; break;
    case 12: aperature = 11; break;
    case 13: aperature = 12.7; break;
    case 14: aperature = 16; break;
    case 15: aperature = 22; break;
    case 16: aperature = 32;
  }
  //select ISO via potentiometer 2; the values are based on common film speeds
  switch (knobISOstatus) {
    case 1: ISO = 12; break;
    case 2: ISO = 25; break;
    case 3: ISO = 50; break;
    case 4: ISO = 100; break;
    case 5: ISO = 160; break;
    case 6: ISO = 200; break;
    case 7: ISO = 400; break;
    case 8: ISO = 800; break;
    case 9: ISO = 1600; break;
    case 10: ISO = 3200;
  }
  if (measureNewExposure) {
    //measure light level (illuminance) and get a new lux value
    lux = bh1750LightMeter.readLightLevel();
    Serial.print("Measured illuminance = ");
    Serial.print(lux);
    Serial.println(" lux");
  }
  //calculate EV
  EV = (log10(lux * ISO / incidentCalibration) / log10(2));
  if (isfinite(EV)) {
    //calculate shutter speed if EV is not NaN or infinity
    shutterSpeed = pow(2, EV) / pow(aperature, 2);
    //output results to serial port and OLED
    Serial.print("Exposure settings: ISO = ");
    Serial.print(ISO);
    Serial.print(", EV = ");
    Serial.print(EV);
    Serial.print(", aperture = f/");
    Serial.print(aperature, 1);
    Serial.print(", ");
    Serial.print("shutter speed = ");
    if (shutterSpeed >= 1) {
      Serial.print("1/");
      Serial.print(shutterSpeed);
    } else {
      Serial.print((1 / shutterSpeed));
    }
    Serial.println("s");
    oled1306.clear();
    oled1306.print("ISO ");
    oled1306.println(ISO);
    oled1306.print("EV: ");
    oled1306.println(EV, 1);
    oled1306.print("-- f/");
    oled1306.println(aperature, 1);
    oled1306.print("-- ");
    if (shutterSpeed >= 1) {
      oled1306.print("1/");
      oled1306.print(shutterSpeed, 0);
    } else {
      oled1306.print((1 / shutterSpeed), 0);
    }
    oled1306.println("s");
  } else {
    Serial.println("Error: exposure out of bounds");
    oled1306.clear();
    oled1306.println("Exposure");
    oled1306.println("value");
    oled1306.println("out of");
    oled1306.println("bounds");
  }
}

Schematics

ArduMeter circuit
Cleaner version by using a normal breadboard
Ardumeter ambhnsgrqd

Comments

Similar projects you might like

sC00p-4-b1l1tY Meter

Project showcase by Team sC00p-4-b1l1tY

  • 686 views
  • 2 comments
  • 1 respect

Stereo NeoPixel Ring VU Meter

Project showcase by ericBcreator

  • 29,185 views
  • 268 comments
  • 70 respects

Sigfox kWh Meter

Project tutorial by jassak

  • 6,115 views
  • 7 comments
  • 15 respects

Pick to Light Project 2 WiFi

Project tutorial by PAJ

  • 776 views
  • 14 comments
  • 4 respects

Pick to Light Project 1 Serial

Project tutorial by PAJ

  • 1,201 views
  • 0 comments
  • 4 respects

Match the Light

Project showcase by Valin

  • 2,623 views
  • 3 comments
  • 6 respects
Add projectSign up / Login