// sound_meter.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);
  
  return result;
}

void do_sound_meter(){
  // holder variables for temperature data
  uint16_t samples = 1000;
  uint16_t max;
  uint16_t min;
  uint16_t i;

  uint16_t this_sample;
    
  max = 0;
  min = 1024;
  for(i=0; i<samples; i++) { //take a set of samples, keept track of the min and max
    this_sample = adc_read();
    if(this_sample > max) {
      max = this_sample;
    } else if(this_sample < min) {
      min = this_sample;
    }
  }

  //diff between min and max represents the sound level
  int16_t diff = max - min;

  // write the info to LCD
  lcd_home();
  lcd_write_string(PSTR("Max: "));
  lcd_write_int16(max);
  lcd_write_string(PSTR("      "));
  lcd_line_two();
  lcd_write_string(PSTR("Min: "));
  lcd_write_int16(min);
  lcd_write_string(PSTR("      "));
  lcd_line_three();
  lcd_write_string(PSTR("Dif: "));
  lcd_write_int16(diff);
  lcd_write_string(PSTR("      "));

  lcd_line_four();
  uint8_t j;
  if(diff == 0) diff=1;
  double diff_d = 1.0* diff;
  double blocks_raw = 6.6*log10(diff_d); //take a log of the sound level and scale to fit the LCD

  uint8_t blocks;    
  if(blocks_raw >= 0.0)
    blocks = blocks_raw;
  else
    blocks = 0;

  // 0xff is black square  
  for(j = 0; j < blocks; j++){
     lcd_write_data(0xff); //write black squares
  }
  for(; j < 20; j++){
     lcd_write_data(' '); //wire spaces on the rest of the characters
  }


}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();  
    
  while(1){
    do_sound_meter();
  } 

  return 0;
}
