Alan Wang
Published © CC BY-NC

ArduMeter - Arduino Incident Light Meter

Build a simple incident light meter for old meterless film cameras

BeginnerFull instructions provided12,997
ArduMeter - Arduino Incident Light Meter

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
BH1750FVI Digital Light Intensity Sensor Module
×1
0.96" 128x64 OLED I2C Display
×1
Rotary potentiometer (generic)
Rotary potentiometer (generic)
×2
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×1
Breadboard (generic)
Breadboard (generic)
×1
Resistor 10k ohm
Resistor 10k ohm
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

ArduMeter circuit

Cleaner version by using a normal breadboard

Code

ArduMeter

C/C++
Code for ArduMeter (v2)
/*
  ArduMeter (Arduino incident light meter) version 2
  by Alan Wang

  BH1750/BH1750FVI digital light intensity sensor:
  Library: https://github.com/claws/BH1750
  VCC -> 3.3V
  GND -> GND
  SDA -> pin A4
  SCL -> pin A5
  ADD -> (none)

  0.96" SSD1306 OLED 128x64 display:
  Library: https://github.com/greiman/SSD1306Ascii
  VCC -> 3.3V
  GND -> GND
  SDA -> pin A4
  SCL -> pin A5

  1x push button
  leg 1 -> pin D2
  leg 2 -> GND

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

   ------------------------------ */

// Set this to true if you wish to get "standard" shutter settings
#define STANRARD_SHUTTER_SPEED  false

// Calibration constant (C) for incident light meters.
// see https://en.wikipedia.org/wiki/Light_meter#Calibration_constants
#define INCIDENT_CALIBRATION    330

// Max analog readings from potentiometers. Change it only if you are
// using different input voltage or potentiometers that are not 10K.
#define KNOB_MAX_ANALOG_READING 740

// Define pins
#define BUTTON_PIN              2  // D2
#define KNOB_APERTURE_PIN       0  // A0
#define KNOB_ISO_PIN            1  // A1

// Pre-defined aperture and ISO settings. Modify them for your own preference.
// Elements in the array have to be sorted from min to max.
const double APERATURE_TABLE[]  = {1.0, 1.4, 1.8, 2.0, 2.8, 3.5, 4.0, 4.5, 5.6, 6.3, 8.0, 11.0, 12.7, 16.0, 22.0, 32.0};
const int ISO_TABLE[]           = {6, 12, 25, 50, 100, 160, 200, 400, 800, 1600, 3200};

// The "shutterspeed table" array is used when STANRARD_SHUTTER_SPEED is set to true.
// 1/2 (0.5) seconds is expressed as 2, and 2 seconds is expressed as -2.
double SHUTTERSPEED_TABLE[]     = {-60, -30, -15, -8, -4, -2, -1, 2, 4, 8, 15, 30, 60, 125, 250, 500, 1000, 2000, 4000};

// If you are using older cameras which have different shutter speed settings,
// you can switch to the folloing array:
// double SHUTTERSPEED_TABLE[]    = {-1, 2, 5, 10, 25, 50, 100, 250, 500, 1000};

/* ------------------------------ */

#include <math.h>
#include <Wire.h>
#include <BH1750.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

BH1750 bh1750;
SSD1306AsciiAvrI2c oled;

bool started = false;
int EV, apertureIndex, apertureIndexOld, isoIndex, isoIndexOld;
long lux;

void setup() {

  // initialize serial port
  Serial.begin(9600);
  Wire.begin();

  // prepare the shutterspeed table array if needed
  if (STANRARD_SHUTTER_SPEED) {
    for (int i = 0; i < (sizeof(SHUTTERSPEED_TABLE) / sizeof(double)); i++) {
      if (SHUTTERSPEED_TABLE[i] < 0) {
        SHUTTERSPEED_TABLE[i] = 1 / abs(SHUTTERSPEED_TABLE[i]);
      }
    }
  }

  // setup button
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  // set the BH1750 in BH1750_ONE_TIME_HIGH_RES_MODE_2
  bh1750.begin(0x21);

  // "initialize" BH1750 by using it once
  bh1750.readLightLevel();

  // initialize OLED
  oled.begin(&Adafruit128x64, 0x3c);
  oled.setFont(System5x7);
  oled.set2X();

  Serial.println("ArduMeter (Arduino incident light meter for camera) READY:");
  Serial.println("- Press button to measure light.");
  Serial.println("- Turn knobs to change aperature/ISO.");

  oled.clear();
  oled.println("ArduMeter");
  oled.println("READY");
  oled.println("press");
  oled.println("button");
}

void loop() {

  //read status from button and potentiometers
  getKnobIndex();

  if (buttonPressed()) { //measure light
    if (!started) started = true;
    displayExposureSetting(true);
    delay(150);

  } else { //change aperture/ISO settings
    if ((apertureIndex != apertureIndexOld || isoIndex != isoIndexOld) && started) {
      displayExposureSetting(false);
      delay(150);
    }
  }

  //record potentiometer previous status
  apertureIndexOld = apertureIndex;
  isoIndexOld = isoIndex;

  delay(50);
}

// read if the button is pressed (pulled low)
bool buttonPressed() {
  return !digitalRead(BUTTON_PIN);
}

// map knob analog readings to array indexes
void getKnobIndex() {
  apertureIndex = round(map(analogRead(KNOB_APERTURE_PIN), 0, KNOB_MAX_ANALOG_READING, 0, sizeof(APERATURE_TABLE) / sizeof(float)));
  isoIndex = round(map(analogRead(KNOB_ISO_PIN), 0, KNOB_MAX_ANALOG_READING, 0, sizeof(ISO_TABLE) / sizeof(int)));
}

// measure/calculate and display exposure settings
void displayExposureSetting(bool measureNewEV) {

  double aperature = APERATURE_TABLE[apertureIndex];
  int iso = ISO_TABLE[isoIndex];

  // measure light level (illuminance) and get a new lux value
  if (measureNewEV) {
    lux = bh1750.readLightLevel();
    Serial.print("Measured illuminance = ");
    Serial.print(lux);
    Serial.println(" lux");
  }

  //calculate EV
  EV = log10(lux * iso / INCIDENT_CALIBRATION) / log10(2);

  if (isfinite(EV)) { //calculate shutter speed if EV is neither NaN nor infinity

    // calculate shutter speed
    double shutterspeed = (pow(2, EV) / pow(aperature, 2));

    // choose standard shutter speed if needed
    if (STANRARD_SHUTTER_SPEED) {
      for (int i = 0; i < (sizeof(SHUTTERSPEED_TABLE) / sizeof(double)) - 1; i++) {
        if (shutterspeed >= SHUTTERSPEED_TABLE[i] && shutterspeed <= SHUTTERSPEED_TABLE[i + 1]) {
          if (abs(shutterspeed - SHUTTERSPEED_TABLE[i]) <= abs(shutterspeed) - SHUTTERSPEED_TABLE[i + 1]) {
            shutterspeed = SHUTTERSPEED_TABLE[i];
          } else {
            shutterspeed = SHUTTERSPEED_TABLE[i + 1];
          }
          break;
        }
      }
    }

    // output result to serial port
    Serial.print("Exposure settings: EV = ");
    Serial.print(EV);
    Serial.print(", ISO = ");
    Serial.print(iso);
    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");

    // output result to OLED
    oled.clear();
    oled.print("EV: ");
    oled.println(EV, 1);
    oled.print("ISO ");
    oled.println(iso);
    oled.print("-- f/");
    oled.println(aperature, 1);
    oled.print("-- ");
    if (shutterspeed > 1) {
      oled.print("1/");
      oled.print(shutterspeed, 0);
    } else {
      oled.print((1 / shutterspeed), 0);
    }
    oled.println("s");

  } else {
    Serial.println("Exposure out of bounds");
    oled.clear();
    oled.println("Exposure");
    oled.println("value");
    oled.println("out of");
    oled.println("bounds");
  }
}

Credits

Alan Wang

Alan Wang

30 projects • 60 followers
Former novel translator, amateur film photographer. Sometime Maker.

Comments