/* Monitor temperatury, wilgotności i punktu rosy
 Szkic pobiera dane o temperaturze i wilgotności z czujnika DHT22
 i oblicza na podstawie tych wartości punkt rosy.
 */

#include <SoftwareSerial.h>
#include <stdlib.h>

#define DHT22_ERROR_VALUE -99.5

#define DHT22_PIN 4

typedef enum
{
  DHT_ERROR_NONE = 0,
  DHT_BUS_HUNG,
  DHT_ERROR_NOT_PRESENT,
  DHT_ERROR_ACK_TOO_LONG,
  DHT_ERROR_SYNC_TIMEOUT,
  DHT_ERROR_DATA_TIMEOUT,
  DHT_ERROR_CHECKSUM,
  DHT_ERROR_TOOQUICK
} 
DHT22_ERROR_t;

class DHT22
{
private:
  uint8_t _bitmask;
  volatile uint8_t *_baseReg;
  unsigned long _lastReadTime;
  float _lastHumidity;
  float _lastTemperature;

public:
  DHT22(uint8_t pin);
  DHT22_ERROR_t readData(void);
  float getHumidity();
  float getTemperatureC();
  void clockReset();
};


// Konfiguracja czujnika DHT22
DHT22 myDHT22(DHT22_PIN);


#define SerialIn 2
#define SerialOut 3


#define WDelay 900


byte thou=0;
byte hund=0;
byte tens=0;
byte ones=0;


SoftwareSerial mySerialPort(SerialIn, SerialOut);


void setup(void)
{


  // uruchamia port szeregowy
  Serial.begin(9600);
  Serial.println("Demonstracja biblioteki DHT22");


  pinMode(SerialOut, OUTPUT);
  pinMode(SerialIn, INPUT);


  mySerialPort.begin(9600);
  mySerialPort.print("v");

  mySerialPort.print("xxxx");
  delay(WDelay);
  mySerialPort.print("----");
  delay(WDelay);
  mySerialPort.print("8888");
  delay(WDelay);
  mySerialPort.print("xxxx");
  delay(WDelay);


}






void loop(void)
{
  float tempC;
  float tempF;
  float humid;
  float dewPoint;




  DHT22_ERROR_t errorCode;


  delay(2000);


  errorCode = myDHT22.readData();

  Serial.print(errorCode);

  switch(errorCode)
  {
  case DHT_ERROR_NONE:
    Serial.print("Temperatura: ");
    tempC = myDHT22.getTemperatureC();

    Serial.print(tempC);
    Serial.print("st. C; wilgotność: ");

    dispData((int)tempC, 'C');

    tempF = (tempC*1.8)+32;



    delay(WDelay);
    dispData((int) tempF, 'F');
    delay(WDelay);

    humid = myDHT22.getHumidity();

    Serial.print(humid);
    Serial.println("%");

    dispData((int)humid, 'h');
    delay(WDelay);

    dewPoint = calculateDewpoint(tempC, humid);
    dispData((int) dewPoint, 'd');

    Serial.print(dewPoint);
    Serial.println("d");

    delay(WDelay);



    break;
  case DHT_ERROR_CHECKSUM:
    Serial.print("Błędna suma kontrolna");
    break;
  case DHT_BUS_HUNG:
    Serial.print("Zawieszona magistrala");
    break;
  case DHT_ERROR_NOT_PRESENT:
    Serial.print("Brak czujnika");
    break;
  case DHT_ERROR_ACK_TOO_LONG:
    break;
  case DHT_ERROR_SYNC_TIMEOUT:
    break;
  case DHT_ERROR_DATA_TIMEOUT:
    break;
  case DHT_ERROR_TOOQUICK:
    break;
  }




}




float calculateDewpoint(float T, float RH)
{
  // przybliżona wartość punktu rosy na podstawie wzoru w artykule o punkcie rosy na Wikipedii


  float dp = 0.0;
  float gTRH = 0.0;
  float a = 17.271;
  float b = 237.7;


  gTRH = ((a*T)/(b+T))+log(RH/100);
  dp = (b*gTRH)/(a-gTRH);


  return dp;
}



void dispData(int i, char c)
{

  if(c == 'k' || c=='K' || c=='m' || c=='l' || c == 'v' || c=='V' || c=='W' || c=='Z' || c=='w' || c=='z')
  {
    mySerialPort.print("bAdx");
    return;

  }


  if((i<-999) || (i>9999))
  {
    mySerialPort.print("ERRx");
    return;
  }

  mySerialPort.print("v");

  if (i > 999) { // i mieści się w przedziale od 1000 do 9999 (włącznie)

    mySerialPort.print(i, DEC);

  } 
  else if (i > 99) { // i mieści się w przedziale od 100 do 999 (włącznie)

    mySerialPort.print(i, DEC);
    mySerialPort.print(c);

  } 
  else if (i > 9) { // i mieści się w przedziale od 10 do 99 (włącznie)

    mySerialPort.print(i, DEC);
    mySerialPort.print("x");
    mySerialPort.print(c);

  } 
  else if (i > 0) { // i mieści się w przedziale od 1 do 9 (włącznie)

    mySerialPort.print("x");
    mySerialPort.print(i, DEC);
    mySerialPort.print("x");
    mySerialPort.print(c);

  } 
  else if (i < -99) { // i mieści się w przedziale od -100 do -999 (włącznie)

    mySerialPort.print(i, DEC);
    mySerialPort.print(c);

  } 
  else if (i < -9) { // i mieści się w przedziale od -10 do -99 (włącznie)

    mySerialPort.print(i, DEC);
    mySerialPort.print(c);

  } 
  else if (i < 0) { // i mieści się w przedziale od -1 do -9 (włącznie)

    mySerialPort.print(i, DEC);
    mySerialPort.print("x");
    mySerialPort.print(c);

  }

}







/*
 DHT22.cpp - biblioteka czujnika DHT22
 Autor: Ben Adams - 2011
 
 Ta biblioteka jest wolnym oprogramowaniem, które może być udostępniane i (lub)
 modyfikowane według zapisów licencji GNU Lesser General Public
 License opublikowanej przez Free Software Foundation (w wersji
 2.1 lub - według uznania użytkowników - dowolnej nowszej.
 
 Biblioteka jest udostępniana w założeniu, że może być pomocna,
 ale BEZ GWARANCJI, także tej dorozumianej
 i niezależnie od przeznaczenia. Więcej informacji można znaleźć w zapisach GNU
 Lesser General Public License.
 
 Kopia licencji GNU Lesser General Public
 License powinna być dołączona do tej biblioteki; w przeciwnym razie należy się skontaktować z Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 
 
 Czujnik wilgotności i temperatury DHT22 dostępny pod adresem
 http://www.sparkfun.com/products/10167
 
 Wersja 0.4; 24 stycznia 2011; autor: Ben Adams
 Dodano zapisywanie stałych kodów w pliku keywords.txt
 Zwraca DHT_ERROR_CHECKSUM w razie niezgodności sumy kontrolnej 
 
 Wersja 0.3; 17 stycznia 2011; autor: Ben Adams
 Ta wersja odczytuje dane
 Wymaga kodu weryfikującego sumy kontrolne na końcu funkcji readData
 
 Wersja 0.2; 16 stycznia 2011; autor: Ben Adams
 Zmieniono styl kodowania, tak aby pasował do innych bibliotek dla Arduino
 Ta wersja w ogóle nie odczytuje danych!
 
 Wersja 0.1; 10 stycznia 2011; autor: Ben Adams (nethoncho na gmail.com)
 Pierwsza wersja to tylko szkielet. Ta wersja nie odczytuje danych!
 Kod jest zmodyfikowaną wersją programu z następujących źródeł:
 Biblioteka Arduino OneWire
 http://sheepdogguides.com/arduino/ar3ne1humDHT11.htm
 
 */

#include "Arduino.h"

extern "C" {
  //#include "WConstants.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
}

#define DIRECT_READ(base, mask) 	(((*(base)) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask)	((*(base+1)) &= ~(mask))
#define DIRECT_MODE_OUTPUT(base, mask)	((*(base+1)) |= (mask))
#define DIRECT_WRITE_LOW(base, mask)	((*(base+2)) &= ~(mask))
//#define DIRECT_WRITE_HIGH(base, mask)	((*(base+2)) |= (mask))

// Powinno być 40, ale czujnik dodaje jeden bit na początku:
#define DHT22_DATA_BIT_COUNT 41

DHT22::DHT22(uint8_t pin)
{
  _bitmask = digitalPinToBitMask(pin);
  _baseReg = portInputRegister(digitalPinToPort(pin));
  _lastReadTime = millis();
  _lastHumidity = DHT22_ERROR_VALUE;
  _lastTemperature = DHT22_ERROR_VALUE;
}

//
// Odczytuje strumień 40 bitów danych z czujnika DHT 22
// Zapisuje wynik w prywatnej składowej, tak aby mogły być odczytywane przez publiczne funkcje składowe
//
DHT22_ERROR_t DHT22::readData()
{
  uint8_t bitmask = _bitmask;
  volatile uint8_t *reg asm("r30") = _baseReg;
  uint8_t retryCount;
  uint8_t bitTimes[DHT22_DATA_BIT_COUNT];
  int currentHumidity;
  int currentTemperature;
  uint8_t checkSum, csPart1, csPart2, csPart3, csPart4;
  unsigned long currentTime;
  int i;

  currentHumidity = 0;
  currentTemperature = 0;
  checkSum = 0;
  currentTime = millis();
  for(i = 0; i < DHT22_DATA_BIT_COUNT; i++)
  {
    bitTimes[i] = 0;
  }

  if(currentTime - _lastReadTime < 2000)
  {
    // Kod wywołujący musi odczekać 2 sekundy pomiędzy kolejnymi wywołaniami funkcji readData
    return DHT_ERROR_TOOQUICK;
  }
  _lastReadTime = currentTime;

  // Początkowym stan pinu musi być HIGH; szkic czeka na ten stan z określonym limitem czasowym
  cli();
  DIRECT_MODE_INPUT(reg, bitmask);
  sei();
  retryCount = 0;
  do
  {
    if (retryCount > 125)
    {
      return DHT_BUS_HUNG;
    }
    retryCount++;
    delayMicroseconds(2);
  } 
  while(!DIRECT_READ(reg, bitmask));
  // Wysyła impuls aktywacji
  cli();
  DIRECT_WRITE_LOW(reg, bitmask);
  DIRECT_MODE_OUTPUT(reg, bitmask); // Sygnał LOW
  sei();
  delayMicroseconds(1100); // 1,1 ms
  cli();
  DIRECT_MODE_INPUT(reg, bitmask);	// Ponownie przełącza pin na tryb wejściowy
  sei();
  // Szuka początku impulsu ACK
  retryCount = 0;
  do
  {
    if (retryCount > 25)
    {
      return DHT_ERROR_NOT_PRESENT;
    }
    retryCount++;
    delayMicroseconds(2);
  } 
  while(!DIRECT_READ(reg, bitmask));
  // Szuka końca impulsu ACK
  retryCount = 0;
  do
  {
    if (retryCount > 50)
    {
      return DHT_ERROR_ACK_TOO_LONG;
    }
    retryCount++;
    delayMicroseconds(2);
  } 
  while(DIRECT_READ(reg, bitmask));
  // Odczytuje 40-bitowy strumień danych
  for(i = 0; i < DHT22_DATA_BIT_COUNT; i++)
  {
    // Szuka początku impulsu synchronizacji
    retryCount = 0;
    do
    {
      if (retryCount > 35)
      {
        return DHT_ERROR_SYNC_TIMEOUT;
      }
      retryCount++;
      delayMicroseconds(2);
    } 
    while(!DIRECT_READ(reg, bitmask));
    // Mierzy szerokość impulsu danych
    retryCount = 0;
    do
    {
      if (retryCount > 50)
      {
        return DHT_ERROR_DATA_TIMEOUT;
      }
      retryCount++;
      delayMicroseconds(2);
    } 
    while(DIRECT_READ(reg, bitmask));
    bitTimes[i] = retryCount;
  }
  // bitTimes zawiera liczbę ponownych prób (us *2)
  // liczba jest potrzebna do znalezienia końca każdego strumienia danych
  // Specyfikacja: 0 to 26-28 us
  // Specyfikacja: 1 to 70 us
  // bitTimes[x] <= 11 to 0
  // bitTimes[x] > 11 to 1
  // Uwaga: bity są przesunięte o jeden względem specyfikacji (nie wiadomo, dlaczego)
  for(i = 0; i < 16; i++)
  {
    if(bitTimes[i + 1] > 11)
    {
      currentHumidity |= (1 << (15 - i));
    }
  }
  for(i = 0; i < 16; i++)
  {
    if(bitTimes[i + 17] > 11)
    {
      currentTemperature |= (1 << (15 - i));
    }
  }
  for(i = 0; i < 8; i++)
  {
    if(bitTimes[i + 33] > 11)
    {
      checkSum |= (1 << (7 - i));
    }
  }

  _lastHumidity = (float(currentHumidity & 0x7FFF) / 10.0);
  if(currentTemperature & 0x8000)
  {
    // Poniżej zera - niestandardowy sposób kodowania liczb ujemnych!
    currentTemperature &= 0x7FFF;
    _lastTemperature = (float(currentTemperature) / 10.0) * -1.0;
  }
  else
  {
    _lastTemperature = float(currentTemperature) / 10.0;
  }

  csPart1 = currentHumidity >> 8;
  csPart2 = currentHumidity & 0xFF;
  csPart3 = currentTemperature >> 8;
  csPart4 = currentTemperature & 0xFF;
  if(checkSum == ((csPart1 + csPart2 + csPart3 + csPart4) & 0xFF))
  {
    return DHT_ERROR_NONE;
  }
  return DHT_ERROR_CHECKSUM;
}

float DHT22::getHumidity()
{
  return _lastHumidity;
}

float DHT22::getTemperatureC()
{
  return _lastTemperature;
}

//
// Funkcja jest używana po osiągnięciu zera przez licznik millis
//
void DHT22::clockReset()
{
  _lastReadTime = millis();

}

