#include <avr/io.h>      // this contains all the IO port definitions
//#include <math.h>
#include <avr/pgmspace.h>

// Using an LED as a light sensor, as described in Mitsubishi Electric
// Research Laboratories Tech Report TR2005-35, "Very Low-Cost Sensing
// and Communication Using Bidirectional LEDs", Paul Dietz, William
// Yerazunis, Darren Leigh,
// http://www.merl.com/publications/TR2003-035/

// I'm using a minipov2 with the low side of the high-bit LED wired to
// the low bit of port D (PD0/RXD, pin 2).  (Very easy modification
// --- cut one trace and add one patch wire.)  As long as that pin is
// putting out 0, there's no difference from the normal minipov2, but
// it allows us to reverse-bias the LED and see how long it takes to
// discharge it.

// To measure the light, we have to back-bias the LED, putting a 0 on
// the port B side and a 1 on the port D side, then switch the port-D
// side to input-only and measure how long it takes to go from 1 to 0.
// We need to disable the internal pull-up resistors, which means
// setting the PUD bit in the MCUCR.

// So here's a program which, every 100ms (at its fastest), measures
// how long it takes to discharge, logarithmically.  log_base_2() goes
// as low as 3 in direct sunlight, 8 when I'm holding it next to a
// lightbulb, 15 in normal indoor night-time lighting, and as high as
// 21 in darkness, meaning 2^3 to 2^21 iterations of the loop.  This
// last number seems to take a noticeable amount of time --- the
// lights are off for maybe half a second to a second.  The
// disassembly suggests that the loop should take 6 or 7 cycles, and
// the chip is running at 8MHz, so that should be about a second.  I
// should get a crystal...

// So 2^15 iterations should be around a quarter of a million cycles,
// or about 30ms.  That's too slow for pleasant communication.

// If I turn on the 0x40 LED while measuring, in room light, I get
// numbers in the range of sqrt(2)^30--31; with it off, I get
// sqrt(2)^32.  So most of the light impinging is coming from that
// LED, which is perhaps badly aimed, but it's still in the 30ms-45ms
// range.

// This function basically wastes time
void delay_ms(long int ms) {
  unsigned long int timer;

  while (ms != 0) {
    // This number depends on the clock frequency; Limor's minipov2
    // code had it set to 4200, which is wrong; 1155 is correct on my
    // chip to within about 1%.
    for (timer=0; timer <= 1155; timer++);
    ms--;
  }
}

void setup_for_measurement(void) {
  DDRB = 0xFF; // set all port B pins to output
  PORTB = 0;   // turn off the other LEDs so they don't interfere
  DDRD = 0xFF; // set all port D pins to output
  PORTD = 1;   // output 1 on the low bit of port D (PD0)
  MCUCR |= (1 << PUD);  // disable pull-up resistors
  // You might think we would have to delay here, but no, the
  // capacitance seems to already be fully charged before you can say
  // "Turn off that pin!"
  DDRD = 0xFE; // set port D pin 0 to input
}

// Return logarithm to base sqrt(2), or something nearby.

// Unfortunately, if I use the 'log' function from math.h, it pulls in
// so much stuff that the file becomes 0x11a0 bytes (4510 bytes) in
// .text.  Which is about twice the size of the chip.  So we can't do
// that.
unsigned char log_base_root_2(long num) {
  long are_we_there_yet = 1;
  unsigned char ceil_log_base_2 = 0;
  while (are_we_there_yet < num) {
    are_we_there_yet <<= 1;
    ceil_log_base_2++;
  }
  // 362 / 256 is roughly sqrt(2)
  if (((num * 362) >> 8) < are_we_there_yet) return ceil_log_base_2 * 2 - 1;
  else return ceil_log_base_2 * 2;
}

// Represent a small number (up to 36) as a pattern of 8 lights.  More
// lights means a bigger number of lights on, and further to the left.
// So the first 8 patterns have 1 bit set (0x01, 0x02, 0x04, etc.),
// the next 7 have 2 bits set, the next 6 have 3 bits set, etc.
unsigned char represent(unsigned char n) {
  short rv = 1;
  unsigned char nbits = 1;
  for (;;) {
    if (rv & 256) {
      nbits++;
      rv = (1 << nbits) - 1;
    }
    if (!n) return rv;
    n--;
    rv <<= 1;
  }
}

int main(void) {
  long reps;
  DDRB = 0xFF;        // set port B to output only
  for (;;) {
    reps = 0;
    setup_for_measurement();
    while (PIND & 1) reps++;  // this loop is 6-7 cycles
    DDRD = 0xFF;
    PORTD = 0;
    // When I was just setting PORTB = log_base_2(reps), when I had
    // log_base_2, the number never got higher than 21 --- so between
    // 1 million and 2 million repetitions.
    PORTB = represent(42 - log_base_root_2(reps));
    // Or this for a display that's less intuitive but easier to
    // interpret precisely:
    //PORTB = log_base_root_2(reps);
    delay_ms(100);
  }
}
