/*
Project Idea:
This project is to ajust the brightness level in the room by measuring
light intensity outside the room. A Servo motor is attached with Curtains
that can open or close the curtains based on light readings by light sensor.
3 light bulbs are used that are lighted up based on external light intensity.
when there is no light outside all inside lights are ON and when there is light
outside the inside lights turn off graudally based on outside light.
The curtains also open close based on outside light conditions.
When there is no light outside the curtains are closed and when light is detected
by light sensor and its value increases from 1000 lux then the curtains gradually
open
*/
float lux;
uint16_t ServoAngle;
int auto_manual;
// These constants should match the photoresistor's "gamma" and "rl10" attributes
const float GAMMA = 0.7;
const float RL10 = 50;
ISR(ADC_vect) {
if (ADMUX & (1 << MUX0)) { // Channel-1
ServoAngle = adc2pwm(ADC); // Read temperature value
ADMUX |= (1 << MUX1); // set to channel-2
ADMUX &= ~(1 << MUX0); // Disable Channel-1
} else if (ADMUX & (1 << MUX1)) { // Channel-2
lux = adc2lux(ADC);
ADMUX |= (1 << MUX0);
ADMUX &= ~(1 << MUX1); // Disable Channel-2
}}
ISR(TIMER0_COMPA_vect) {
ADCSRA |= (1 << ADSC); // Trigger ADC conversion
}
ISR(TIMER1_COMPA_vect) {
if(auto_manual)
OCR1A = ServoAngle;
else
OCR1A = lux;
//Serial.println(OCR1A);
}
float adc2lux(uint16_t x)
{
// Sensor Calculation based on Wokwi
// https://docs.wokwi.com/parts/wokwi-photoresistor-sensor
return pow(RL10*1e3*pow(10, GAMMA)/(2000*(x/1024.*5)/(1-x/1024.*5/5)),(1/GAMMA));
}
uint16_t adc2pwm(uint16_t y)
{
// Min Servo Position 1000 and Max 5000
return y * 3.85 + 1000;
}
void setup() {
Serial.begin(9600);
// .........Setup ADC......................
// Ref Voltage is AVcc and Select Channel-1
ADMUX |= (1 << REFS0) | (1 << MUX0);
// ADC ON, ADC with interrupt, AutoTrig Enable
ADCSRA |= (1 << ADEN) | (1 << ADIE) | (1 << ADATE);
// Timer/Counter0 compare match A
ADCSRB |= (1 << ADTS1) | (1 << ADTS0);
// Setup Timer0 for ADC Trigger Source
// Normal Mode Operation with Overflow Interrupt
TCCR0B = (1 << CS02) | (1 << CS00); // 1024 Prescalar
OCR0A = 255; // Value for CompA interrupt
TIMSK0 = (1 << OCIE0A); // Overflow Interrupt Enable
// Enable global interrupts
// Using 16-bit Timer1 to Control the position of Servo
// Non-Inverting mode, Fast PWM mode Mode-14
TCCR1A = (1 << COM1A1) | (1 << WGM11);
// Fast PWM mode, No Prescaler-8
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
ICR1 = 10000; // Freq = 16MHz / (2 * 8 * 10000) = 100 MHz
OCR1A = 2500; // Initial servo position at Middle
DDRB |= (1 << PB1); // Set servo pin as output
TIMSK1 = (1 << OCIE1A);
sei();
// Port for auto_manual Switch
DDRB &= ~(1 << PB5) & ~(1 << PB4) & ~(1 << PB3) ;
// Enable PullUps
PORTB |= (1 << PB5) | (1 << PB4) | (1 << PB3);
// Set LED ports as output
DDRD |= (1 << PD4) | (1 << PD3) | (1 << PD2);
}
void loop() {
// Check Auto Switch for Auto/Manual Operation
if(PINB & (1 << PB5))
auto_manual = 1;
else
auto_manual = 0;
if(auto_manual)
{
// Logic for Manual Operation
if(PINB & (1 << PB4))
PORTD |= (1 << PD4);
else
PORTD &= ~(1 << PD4);
if(PINB & (1 << PB3))
PORTD |= (1 << PD3);
else
PORTD &= ~(1 << PD3);
if(PINB & (1 << PB2))
PORTD |= (1 << PD2);
else
PORTD &= ~(1 << PD2);
}
else
{
// Logic for Automatically Turn/OFF lights based on Light
if(lux < 2000)
{
// Turn ON All lights
PORTD |= (1 << PD4) | (1 << PD3) | (1 << PD2);
}
else if(lux < 3000)
{
// Turn OFF one Light
PORTD |= (1 << PD3) | (1 << PD2);
PORTD &= ~(1 << PD4);
}
else if(lux < 4000)
{
// Turn OFF 2 lights
PORTD |= (1 << PD2);
PORTD &= ~(1 << PD4) & ~(1 << PD3);
}
else if(lux < 5000)
{
// Turn OFF All lights
PORTD &= ~(1 << PD4) & ~(1 << PD3) & ~(1 << PD2);
}
}
}