/////////////////////////////////////////////////////////////////// // TSL2561 EXPOSURE METER V1.0 // // KEVIN KADOOKA FEBRUARY 2014 // /////////////////////////////////////////////////////////////////// #include //Including all of the libraries needed for TSL2561, OLED, and EEPROM #include #include #include #include #include #include #define OLED_RESET 4 //OLED reset pin set to GPIO4 Adafruit_SSD1306 display(OLED_RESET); int Bn1p = 16; //+ increment button pin int Bn2p = 10; //- increment button pin int Rp = 14; //Metering button pin int Bat = 0; int Tdisplay; //State of shutter speed value display (fractional, seconds, minutes) int Tfr; //Fractional time e.g. 1/1000, where Tfr = 1000 int Aaddr = 0; //Address to write Av to EEPROM int Saddr = 1; //Address to write Sv to EEPROM float Vraw; float V; float lux; //Lux value from TSL2561 float Ev; //Calculated Exposure Value float Tmin; //Time in minutes boolean Bn1; //+ increment button state boolean Bn2; //- increment button state boolean R; //Metering button state boolean ISOmode=0; //ISO mode state boolean save=0; //Save to EEPROM state float A[] = {1, 1.4, 2, 2.8, 4, 5.6, 8, 11, 16, 22, 32, 45, 64, 90}; //Array of aperture values int Am=EEPROM.read(0); //Remember the last aperture value saved to EEPROM (addr = 0) float S[] = {6, 12, 25, 50, 100, 200, 400, 800, 1600, 3200, 6400, 12800, 25600, 51200}; //Array of sensitivity values int Sm=EEPROM.read(1); //Remember the last sensitivity value saved to EEPROM (addr = 1) void setup() { pinMode(Bn1p, INPUT); //Set up pin modes and start serial for debugging pinMode(Bn2p, INPUT); pinMode(Rp, INPUT); Serial.begin(9600); Vraw = analogRead(Bat); V = (Vraw*10/1023); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Initialize with the I2C addr 0x3C (for the 128x32 OLED) display.display(); display.clearDisplay(); display.drawRoundRect(2,2,124,28,3,WHITE); //Show splash screen display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(4,6); display.println("Kadooka Camera Works"); display.setCursor(7,19); display.println("Exposure Meter v1.0"); display.display(); display.clearDisplay(); delay(800); display.drawRoundRect(2,2,124,28,3,WHITE); display.setCursor(10,6); display.println(""); if (V < 7.2) { display.setCursor(5,19); display.print("Low Battery! ("); display.print(V,2); display.println("V)"); } else { display.setCursor(8,19); display.print("Battery OK! ("); display.print(V,2); display.println("V)"); } display.display(); display.clearDisplay(); delay(1000); if (Am > (sizeof(A)/sizeof(float)-1)) //If nothing has ever been to EEPROM, it will read "255" which is probably not a valid position in our Av and Sv arrays. If the EEPROM value is out of range, reset it to something more suitable { Am = 3; } if (Sm > (sizeof(S)/sizeof(float)-1)) { Sm = 4; } lux = getLux(); //Politely ask the TSL2561 to give us an illuminance measurement } void loop() { Bn1 = digitalRead(Bn1p); //Poll for button presses Bn2 = digitalRead(Bn2p); R = digitalRead(Rp); /////////IF BUTTON 2 AND 3 ARE BOTH PRESSED, SAVE EEPROM DATA if (Bn1 == 1 & R == 1) { save = 1; } while (Bn1 == 1 & R == 1) { delay(100); Bn1 = digitalRead(Bn1p); Bn2 = digitalRead(Bn2p); R = digitalRead(Rp); } if (save == 1) //If + increment and metering button are both pressed, save to EEPROM and display "Saved." { EEPROM.write(Aaddr,Am); EEPROM.write(Saddr,Sm); Serial.print("Save = "); Serial.println(save); //display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(0,0); display.println("Saved."); display.display(); delay(500); display.clearDisplay(); save = 0; Bn1 = 0; Bn2 = 0; } //READ BUTTON 1 AND INCREMENT APERTURE VALUE if (Bn1 == 1) { if (Am >= (sizeof(A)/sizeof(float)-1)) { Am = 0; } else { Am = Am + 1; } } //READ BUTTON 2 AND INCREMENT APERTURE VALUE if (Bn2 == 1) { if (Am == 0) { Am = (sizeof(A)/sizeof(float)-1); } else { Am = Am - 1; } } //////////IF BUTTON 1 AND 2 ARE BOTH PRESSED, ENTER ISO MODE if (Bn1 == 1 & Bn2 == 1) { ISOmode = 1; } while (Bn1 == 1 & Bn2 == 1) { delay(100); Bn1 = digitalRead(Bn1p); Bn2 = digitalRead(Bn2p); } while (ISOmode == 1) { Bn1 = digitalRead(Bn1p); Bn2 = digitalRead(Bn2p); if (Bn1 == 1 & Bn2 == 1) { ISOmode = 0; } //READ BUTTON 1 AND INCREMENT SENSITIVITY VALUE if (Bn1 == 1) { if (Sm >= (sizeof(S)/sizeof(float)-1)) { Sm = 0; } else { Sm = Sm + 1; } } //READ BUTTON 2 AND INCREMENT SENSITIVITY VALUE if (Bn2 == 1) { if (Sm == 0) { Sm = (sizeof(S)/sizeof(float)-1); } else { Sm = Sm - 1; } } refresh(); //DELAYS FOR BUTTON HOLD while(Bn1 == 1) { delay(10); Bn1 = digitalRead(Bn1p); } while(Bn2 == 1) { delay(10); Bn2 = digitalRead(Bn2p); } Serial.print("ISO_m = "); Serial.println(Sm); Serial.print("ISOmode ="); Serial.println(ISOmode); } ///////END OF ISOMODE ROUTINE///// refresh(); //DELAYS FOR BUTTON HOLD while(Bn1 == 1) { delay(10); Bn1 = digitalRead(Bn1p); } while(Bn2 == 1) { delay(10); Bn2 = digitalRead(Bn2p); } Serial.print("Aperture_m = "); Serial.println(Am); Serial.print("ISOmode = "); Serial.println(ISOmode); delay(10); if (R == 1) //If the metering button is pressed, get a new illuminance value. { lux = getLux(); } } /////////////////////////////////////////////// // FUNCTIONS // /////////////////////////////////////////////// float getLux() { Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345); sensor_t sensor; tsl.getSensor(&sensor); // tsl.setGain(TSL2561_GAIN_1X); /* No gain ... use in bright light to avoid sensor saturation */ // tsl.setGain(TSL2561_GAIN_16X); /* 16x gain ... use in low light to boost sensitivity */ tsl.enableAutoGain(true); /* Auto-gain ... switches automatically between 1x and 16x */ //tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS); /* fast but low resolution */ tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS); /* medium resolution and speed */ //tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS); /* 16-bit data but slowest conversions */ tsl.begin(); sensors_event_t event; tsl.getEvent(&event); float lux = event.light; return lux; } void refresh() //Calling the function gives a new exposure calculation based on the last illuminance value, and refreshes the display. { float T = pow(A[Am],2)*64/(lux*S[Sm]); //T = exposure time, in seconds if (T >= 60) { Tdisplay = 0; //Exposure is now in minutes Tmin = T/60; } else if (T < 0.75) { Tdisplay = 1; //Exposure is now in fractional form if (T < 0.000125) {Tdisplay = 3;} if ((T <= 0.000188) && (T > 0.000125)) {Tfr = 8000;} if ((T <= 0.000375) && (T > 0.000188)) {Tfr = 4000;} if ((T <= 0.00075) && (T > 0.000375)) {Tfr = 2000;} if ((T <= 0.0015) && (T > 0.00075)) {Tfr = 1000;} if ((T <= 0.003) && (T > 0.0015)) {Tfr = 500;} if ((T <= 0.006) && (T > 0.003)) {Tfr = 250;} if ((T <= 0.012333) && (T > 0.006)) {Tfr = 125;} if ((T <= 0.025) && (T > 0.012333)) {Tfr = 60;} if ((T <= 0.05) && (T > 0.025)) {Tfr = 30;} if ((T <= 0.095833) && (T > 0.05)) {Tfr = 15;} if ((T <= 0.1875) && (T > 0.095833)) {Tfr = 8;} if ((T <= 0.375) && (T > 0.1875)) {Tfr = 4;} if ((T <= 0.75) && (T > 0.375)) {Tfr = 2;} } else if ((T>=0.75)&&(T<60)) { Tdisplay = 2; //Exposure in seconds } if (lux == 0) //This happens if the sensor is overloaded or senses no light. { Tdisplay = 3; } Serial.println(Tdisplay); display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(0,0); display.print("f/"); display.println(A[Am], 1); if (Tdisplay == 0) { display.print(Tmin,1); display.println("m"); } else if (Tdisplay == 1) { display.print("1/"); display.println(Tfr); } else if (Tdisplay == 2) { display.print(T,1); display.println("s"); } else if (Tdisplay == 3) { display.println("RANGE!"); } display.println(T,3); display.drawLine(73, 0, 73, 32, WHITE); display.setTextSize(1); display.setCursor(76,0); display.print("ISO"); display.println(S[Sm],0); display.setCursor(76,11); display.print("EV="); Ev = log(pow(A[Am],2))/log(2) + log(1/T)/log(2); display.println(floor(Ev+0.5),1); display.setCursor(76,22); display.print(lux,1); display.println("Lx"); display.display(); display.clearDisplay(); }