Alan Wang
Published © CC BY-NC

ArduMeter - Arduino Incident Light Meter

Build a simple incident light meter for old meterless film cameras

BeginnerFull instructions provided18,404
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
31 projects • 103 followers
Please do not ask me for free help for school or company projects. My time is not open sourced and you cannot buy it with free compliments.
Contact

Comments

Please log in or sign up to comment.