/////////////////////////////////////////
// HXbot HXlcd firmware                 /
// EoF 2016 EoF@itphx.ru                /
/////////////////////////////////////////

#include <Wire.h>
#include <TroykaDHT11.h>
#include <LiquidCrystal.h>

// DEBUG
#define DEBUG 1

// DEFINE
#define SLAVE_ADDRESS 0x06
#define XOR_SEQ 0xFF
#define EXT_COM 0xAA

#define R_LED 10
#define G_LED 9
#define B_LED 11
#define L_BTN A3
#define C_BTN A2
#define R_BTN A1
#define RED_PIN 10
#define GRN_PIN 9
#define LCD_BL 3

// Sensors
#define LIGHT A6
#define LOUD A7
#define TEMP A0
#define BUZZ 13
#define FREE 12

#define LED_PIN 13

#define DELAY1 2000
#define DELAY2 500

// COMMANDS
// Util
#define COM_PING 0x01
#define COM_GET_TEMP 0x02

// Response
#define OK_RSP  0x00
#define NO_RSP  0xFF
#define ERR_RSP 0x01
#define BLK_RSP 0x02
#define CSE_RSP 0x03
#define IOE_RSP 0x04
#define TMO_RSP 0x05

// VAR
LiquidCrystal lcd(2, 4, 5, 6, 7, 8);
DHT11 dht(TEMP);

// эту константу (typVbg) необходимо откалибровать индивидуально
const float typVbg = 1.08; // 1.0 -- 1.2

byte autoresponse = 0;
byte cmd = 0;
byte flg = 0;
byte ext = 0;

int temp, hum;

void setup() {
  
  if (DEBUG) {
    Serial.println("GO!");
    Serial.begin(9600);
  }
  
  // Initialize i2c as slave
  Wire.begin(SLAVE_ADDRESS);
 
  // Define callbacks for i2c communication
  Wire.onReceive(receiveData);
  Wire.onRequest(answer);
  
  // put your setup code here, to run once:
  pinMode(LED_PIN, OUTPUT);
  pinMode(RED_PIN, OUTPUT);
  //digitalWrite(RED_PIN, LOW);
  pinMode(GRN_PIN, OUTPUT);
  //digitalWrite(GRN_PIN, LOW);
 
  pinMode(R_LED, OUTPUT);
  pinMode(G_LED, OUTPUT);
  pinMode(B_LED, OUTPUT);

  pinMode(LCD_BL, OUTPUT);

  pinMode(L_BTN,INPUT_PULLUP);
  pinMode(C_BTN,INPUT_PULLUP);
  pinMode(R_BTN,INPUT_PULLUP);

  pinMode(LIGHT, INPUT);
  pinMode(LOUD, INPUT);
  pinMode(FREE, INPUT);
  pinMode(BUZZ, OUTPUT);

  randomSeed(millis());
  analogReference(DEFAULT);

  lcd.begin(16, 2);
  analogWrite(LCD_BL, 255);
  lcd.print("Hello world!");
  
  dht.begin();

  delay(2000);
  lcd.clear();
}

void loop() {
  if (digitalRead(FREE) == HIGH) {
    analogWrite(GRN_PIN, 128);
  }
  else {
    analogWrite(GRN_PIN, 0);
  }
  
  displayALL();
  
  if (DEBUG) {
    Serial.print("Vcc: ");
    Serial.println(readvcc());
  }
  
  delay(500);
}

void rgb_control() {
  //int tmp1;
  //tmp1 = Read(L_BTN);
  if (digitalRead(L_BTN) == LOW) { 
    analogWrite(R_LED, 255);
    //Serial.println(analogRead(L_BTN));
  }
  else {
    analogWrite(R_LED,0);
  }

  if (digitalRead(C_BTN) == LOW) {
    analogWrite(G_LED, 255);
  }
  else {
    analogWrite(G_LED,0);
  }
  
  if (digitalRead(R_BTN) == LOW) {
    analogWrite(B_LED, 255);
  }
  else {
    analogWrite(B_LED,0);
  }
}

void displayTH() {
  int result;
  result = dht.read();

  lcd.clear();
  
  lcd.setCursor(0, 0);
  lcd.print("Temp:");
  lcd.setCursor(0, 1);
  lcd.print("Hum:");
    
  if (result == DHT_OK) {
    lcd.setCursor(6, 0);
    lcd.print(dht.getTemperatureC());
    lcd.print(" C");
        
    lcd.setCursor(6, 1);
    lcd.print(dht.getHumidity());
    lcd.print(" %");
  }
  else {
    lcd.setCursor(6, 0);
    lcd.print("ERROR");
    lcd.setCursor(6, 1);
    lcd.print("ERROR");
  }
}

void displayLL() {
  lcd.clear();
  
  lcd.setCursor(0, 0);
  lcd.print("Light:");
  lcd.setCursor(0, 1);
  lcd.print("Loud:");
    
  lcd.setCursor(8, 0);
  lcd.print(analogRead(LIGHT));
      
  lcd.setCursor(8, 1);
  lcd.print(analogRead(LOUD)); 
}

void displayALL() {
  int result;
  result = dht.read();

  lcd.clear();

  lcd.setCursor(0, 0);
  lcd.print("Li:");
  lcd.setCursor(0, 1);
  lcd.print("Lo:");
    
  lcd.setCursor(4, 0);
  lcd.print(analogRead(LIGHT));
      
  lcd.setCursor(4, 1);
  lcd.print(analogRead(LOUD));

  lcd.setCursor(9, 0);
  lcd.print("T:");
  lcd.setCursor(9, 1);
  lcd.print("H:");

  if (result == DHT_OK) {
    lcd.setCursor(11, 0);
    lcd.print(dht.getTemperatureC());
    lcd.print("C");
        
    lcd.setCursor(11, 1);
    lcd.print(dht.getHumidity());
    lcd.print(" %");
  }
  else {
    lcd.setCursor(11, 0);
    lcd.print("ERR");
    lcd.setCursor(11, 1);
    lcd.print("ERR");
  }
}

// Callback for received data
void receiveData(int byteCount) {
  
  while(Wire.available()) {
    // Get command
    cmd = Wire.read();
    if (cmd == EXT_COM && byteCount == 3) {
      flg = 0x00;
      ext = 0x00;
      
      if (Wire.available()) ext = Wire.read();
      if (Wire.available()) flg = Wire.read();
    }
    else {
      // Cleanup I2C bus
      while(Wire.available()) {
        ext = Wire.read();
      }
    }
      
    switch (cmd) {
      case COM_PING:
        autoresponse = OK_RSP;
        blink_red();
        break;
      case COM_GET_TEMP:
        autoresponse = getInternalTemp();
        break;
      default:
        autoresponse = ERR_RSP;
        break;
    }
  }
}

// Callback for sending data
void answer() {
  Wire.write(autoresponse);
}

void red_on() {
  digitalWrite(RED_PIN, HIGH);
}

void red_off() {
  digitalWrite(RED_PIN, LOW);
}

void blink_red() {
  red_on();
  delay(100);
  red_off();
}

// Get the internal temperature of the arduino
double getInternalTemp(void) {
  unsigned int wADC;
  double t;
  ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
  ADCSRA |= _BV(ADEN); // enable the ADC
  delay(20); // wait for voltages to become stable.
  ADCSRA |= _BV(ADSC); // Start the ADC
  while (bit_is_set(ADCSRA,ADSC));
  wADC = ADCW;
  t = (wADC - 324.31 ) / 1.22;
  return (t);
}

float readvcc() {
  byte i;
  float tmp = 0.0;

  // Read 1.1V reference against Avcc
  // set the reference to vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
      ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
      ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
      ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
      // works on an Arduino 168 or 328
      ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif

  delay(3); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH
  uint8_t high = ADCH; // unlocks both

  tmp = (high<<8) | low;
  tmp = (typVbg * 1023.0) / tmp;

  return tmp;
}
