diff --git a/wordclock/Arduino.mk b/wordclock/Arduino.mk index e61067e..ba6289e 100644 --- a/wordclock/Arduino.mk +++ b/wordclock/Arduino.mk @@ -362,8 +362,11 @@ all: $(OBJDIR) $(TARGET_HEX) $(OBJDIR): mkdir $(OBJDIR) +# need to add -lm to the end of CC line for ELF target due to compiler / linker +# bug; see +# http://stackoverflow.com/questions/8188849/avr-linker-error-relocation-truncated-to-fit $(TARGET_ELF): $(OBJS) - $(CC) $(LDFLAGS) -o $@ $(OBJS) $(SYS_OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(SYS_OBJS) -lm $(DEP_FILE): $(OBJDIR) $(DEPS) cat $(DEPS) > $(DEP_FILE) diff --git a/wordclock/lightlevelmapping.m b/wordclock/lightlevelmapping.m new file mode 100644 index 0000000..0d38fcb --- /dev/null +++ b/wordclock/lightlevelmapping.m @@ -0,0 +1,46 @@ +function lightlevelmapping() +MINBRIGHTNESS = 2; +MAXBRIGHTNESS = 20; + +LIGHTSENSOR_BOTTOM = 270; +LIGHTSENSOR_TOP = 350; + +l = [LIGHTSENSOR_BOTTOM - 10:LIGHTSENSOR_TOP + 10]; +b = zeros(size(l)); + +LIGHTSENSOR_SCALE = 1024; +LIGHTSENSOR_BASE = 1.35; + +if (LIGHTSENSOR_BASE <= 1) + error 'LIGHTSENSOR_BASE must be > 1'; +end + +brightness_per_unit_light = floor(floor( LIGHTSENSOR_SCALE * (MAXBRIGHTNESS - MINBRIGHTNESS)) / ... + (LIGHTSENSOR_TOP - LIGHTSENSOR_BOTTOM)) + +LIGHTSENSOR_BASE^(MAXBRIGHTNESS - MINBRIGHTNESS) * (MAXBRIGHTNESS - MINBRIGHTNESS) + MINBRIGHTNESS + +% b = floor(brightness_per_unit_light * (l - LIGHTSENSOR_BOTTOM) / LIGHTSENSOR_SCALE ) + MINBRIGHTNESS; +b1 = (LIGHTSENSOR_BASE.^(brightness_per_unit_light * (l - LIGHTSENSOR_BOTTOM) / ... + LIGHTSENSOR_SCALE)) / LIGHTSENSOR_BASE^(MAXBRIGHTNESS - MINBRIGHTNESS) * (MAXBRIGHTNESS - ... + MINBRIGHTNESS) + MINBRIGHTNESS; +b = floor( (LIGHTSENSOR_BASE.^floor(brightness_per_unit_light * (l - LIGHTSENSOR_BOTTOM) / ... + LIGHTSENSOR_SCALE)) / LIGHTSENSOR_BASE^(MAXBRIGHTNESS - MINBRIGHTNESS) * (MAXBRIGHTNESS - ... + MINBRIGHTNESS)) + MINBRIGHTNESS; + +[brightness_per_unit_light * (l - LIGHTSENSOR_BOTTOM) / LIGHTSENSOR_SCALE; l] + +b1(l <= LIGHTSENSOR_BOTTOM) = MINBRIGHTNESS; +b1(l >= LIGHTSENSOR_TOP) = MAXBRIGHTNESS; +b(l <= LIGHTSENSOR_BOTTOM) = MINBRIGHTNESS; +b(l >= LIGHTSENSOR_TOP) = MAXBRIGHTNESS; + +b2 = round(b1); + +figure +plot(l, b1, l, b, l, b2) +A = axis; +A(3) = -1; +A(4) = MAXBRIGHTNESS + 1; +axis(A); +grid on diff --git a/wordclock/wordclock.ino b/wordclock/wordclock.ino index be5881b..9572b45 100644 --- a/wordclock/wordclock.ino +++ b/wordclock/wordclock.ino @@ -3,6 +3,7 @@ #include // I use the library that Matt Sparks created #include #include +#include // uncomment the following to speed up the timer for testing //#define TESTMODE @@ -61,11 +62,30 @@ void SWversion(void); */ +// set USELIGHTSENSOR to 1 to use ambient light sensor connected to ADC0 (pin +// 23) +#define USELIGHTSENSOR 1 +// analog input put to which ambiend light sensor is connected +#define LIGHTSENSOR_INPUTPIN 0 +// bottom of light sensor (ambient light values at or lower than this level will +// be mapped to MINBRIGHTNESS) +#define LIGHTSENSOR_BOTTOM 270 +// top of light sensor (ambient light values at or higher than this level will +// be mapped to MAXBRIGHTNESS) +#define LIGHTSENSOR_TOP 300 +// weight for exponential decaying averaging (actual weigth is 2 ^ LIGHTSENSOR_WEIGHT) +#define LIGHTSENSOR_WEIGHT 4 +// base of exponential mapping (must be > 1) +#define LIGHTSENSOR_BASE 1.35 // DAY Brightness setting 0 = off 20 = full -#define DAYBRIGHTNESS 20 +#define MAXBRIGHTNESS 20 +// start MAXBRIGHTNESS at DAYLIGHTHOUR (7 am) +#define DAYLIGHTHOUR 7 // NIGHT Brightness setting 0 = off 20 = full -#define NIGHTBRIGHTNESS 15 +#define MINBRIGHTNESS 2 +// start MINBRIGHTNESS at NIGHTLIGHTHOUR (7 pm) +#define NIGHTLIGHTHOUR 19 @@ -79,8 +99,12 @@ void SWversion(void); #define INIT_TIMER_COUNT 6 #define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT - - +// there are only 20 brightness levels and 1024 lightsensor levels -> we need to +// scale the brightness levels to something closer to avoid having to work with +// floating point numbers +#define LIGHTSENSOR_SCALE 10 +unsigned int brightness_per_unit_light = ((1 << LIGHTSENSOR_SCALE) * (MAXBRIGHTNESS - + MINBRIGHTNESS)) / (LIGHTSENSOR_TOP - LIGHTSENSOR_BOTTOM); int hour=12, minute=0, second=00; static unsigned long msTick =0; // the number of Millisecond Ticks since we last @@ -115,6 +139,8 @@ int LED4PIN=16; // Arduino analog 2 int current_brightnes=0; +char ambient_light_to_brightness[1024]; + // define the language to be used for this project: #include LANGUAGE // The language pack @@ -146,6 +172,8 @@ void print_DS1302time() void setup() { + int n; + // initialise the hardware // initialize the appropriate pins as outputs: pinMode(LEDClockPin, OUTPUT); @@ -165,7 +193,9 @@ void setup() pinMode(LED3PIN, OUTPUT); pinMode(LED4PIN, OUTPUT); - current_brightnes=DAYBRIGHTNESS; + analogReference(DEFAULT); + + current_brightnes=MAXBRIGHTNESS; Serial.begin(9600); // setup the serial port to 9600 baud @@ -202,6 +232,28 @@ void setup() else Serial.println("absent"); } + // compute ambient light level to brightness level mapping + for (n = 0; n < sizeof(ambient_light_to_brightness); n++) + { + if (n <= LIGHTSENSOR_BOTTOM) + ambient_light_to_brightness[n] = MINBRIGHTNESS; + else if (n >= LIGHTSENSOR_TOP) + ambient_light_to_brightness[n] = MAXBRIGHTNESS; + else + { + ambient_light_to_brightness[n] = + round( pow((double) LIGHTSENSOR_BASE, ( + (double) brightness_per_unit_light * ((double) n - (double) + LIGHTSENSOR_BOTTOM) / (double) (1 << LIGHTSENSOR_SCALE))) / pow((double) + LIGHTSENSOR_BASE, (double) (MAXBRIGHTNESS - (double) MINBRIGHTNESS)) * + ((double) MAXBRIGHTNESS - (double) MINBRIGHTNESS) + (double) MINBRIGHTNESS ); + /* Serial.print(n); + Serial.print(": "); + Serial.println(ambient_light_to_brightness[n], DEC); */ + } + } + + // determine whether we are running on old or new hardware // old hardware tied the push buttons to ground using 4k7 resistors // and relied on the buttons to pull them high @@ -353,6 +405,10 @@ void SWversion(void) { void loop(void) { + static unsigned long int lightlevel_avg; + unsigned long int lightlevel_sample; + static char n_lightlevel_samples = 0; + static unsigned int counter = 0; #ifdef TESTMODE second=59; @@ -373,13 +429,6 @@ void loop(void) digitalWrite(13,HIGH); delay(50); digitalWrite(13,LOW); - - #ifndef TESTMODE - if (second%5==0) { - Serial.print(second); - Serial.print(".."); - } - #endif } @@ -401,10 +450,56 @@ void loop(void) // set the brightnes level based on the current hour - night=7pm - 6.59am // - if ((hour <7) | (hour >=19)) - current_brightnes=NIGHTBRIGHTNESS; + #if (USELIGHTSENSOR == 1) + lightlevel_sample = analogRead(LIGHTSENSOR_INPUTPIN); + + // update average + if (n_lightlevel_samples < (1 << LIGHTSENSOR_WEIGHT)) + { + // add (1 << (LIGHTSENSOR_WEIGHT - 1)) to average before division so that + // the average is round()-ed instead of floor()-ed + lightlevel_avg = (n_lightlevel_samples * lightlevel_avg + + lightlevel_sample + (1 << (LIGHTSENSOR_WEIGHT - 1))) / + (n_lightlevel_samples + 1); + n_lightlevel_samples++; + } + else + { + // do not update n_lightlevel_samples to prevent overflow + + // add (1 << (LIGHTSENSOR_WEIGHT - 1)) to average before division so that + // the average is round()-ed instead of floor()-ed + lightlevel_avg = ( ((1 << LIGHTSENSOR_WEIGHT) - 1) * lightlevel_avg + + lightlevel_sample + (1 << (LIGHTSENSOR_WEIGHT - 1))) >> LIGHTSENSOR_WEIGHT; + } + + // compute new brightness level + /* + // linear method + if (lightlevel_avg <= LIGHTSENSOR_BOTTOM) + current_brightnes = MINBRIGHTNESS; + else if (lightlevel_avg >= LIGHTSENSOR_TOP) + current_brightnes = MAXBRIGHTNESS; + else + { + current_brightnes = (brightness_per_unit_light * (lightlevel_avg - + LIGHTSENSOR_BOTTOM)) / (1 << LIGHTSENSOR_SCALE) + MINBRIGHTNESS; + } */ + + current_brightnes = ambient_light_to_brightness[lightlevel_avg]; + + Serial.print("lightsensor: "); + Serial.print(lightlevel_sample); + Serial.print(" / "); + Serial.print(lightlevel_avg); + Serial.print(", brightness: "); + Serial.println(current_brightnes); + #else + if ((hour < DAYLIGHTHOUR) | (hour >= NIGHTLIGHTHOUR)) + current_brightnes=MINBRIGHTNESS; else - current_brightnes=DAYBRIGHTNESS; + current_brightnes=MAXBRIGHTNESS; + #endif // test to see if both buttons are being held down