#include <DHT.h>
#include <Wire.h>
#define DHTPIN 6
#define DHTTYPE DHT22
#define TUP 4
#define TDN 3
#define OUT 5
#define I2C_ADDR 0x27
#define LCD_COLUMNS 16
#define LCD_LINES 2
DHT dht(DHTPIN, DHTTYPE);
#define PERIOD 5000
#define OFFSET 15
// ***********************************************
// LCD Definitions
// ***********************************************
// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80
// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00
// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00
// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
// flags for backlight control
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00
#define LCD_ENABLE_BIT 0b00000100 // Enable bit
#define LCD_READ_WRITE_BIT 0b00000010 // Read/Write bit
#define LCD_REGISTER_SELECT_BIT 0b00000001 // Register select bit
typedef struct LiquidCrystalDevice_t {
uint8_t Address;
uint8_t Columns;
uint8_t Rows;
uint8_t Backlight;
uint8_t DisplayFunction;
uint8_t DisplayControl;
uint8_t DisplayMode;
} LiquidCrystalDevice_t;
// ***********************************************
// LCD Routines
// ***********************************************
LiquidCrystalDevice_t lq_init(uint8_t address, uint8_t columns, uint8_t rows, uint8_t dotSize)
{
LiquidCrystalDevice_t device;
device.Address = address;
device.Columns = columns;
device.Rows = rows;
device.Backlight = LCD_NOBACKLIGHT;
device.DisplayFunction = LCD_4BITMODE | LCD_1LINE | dotSize;
device.DisplayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
// Initialize to default text direction (for roman languages)
device.DisplayMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
if (rows > 1) {
device.DisplayFunction |= LCD_2LINE;
}
// for some 1 line displays you can select a 10 pixel high font
if ((dotSize != 0) && (rows == 1)) {
device.DisplayFunction |= LCD_5x10DOTS;
}
// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
// according to datasheet, we need at least 40ms after power rises above 2.7V
// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
_delay_ms(100);
// Now we pull both RS and R/W low to begin commands
lq_transmitI2C(&device, LCD_NOBACKLIGHT); // reset expanderand turn backlight off (Bit 8 =1)
_delay_ms(1000);
//put the LCD into 4 bit mode
// this is according to the hitachi HD44780 datasheet
// figure 24, pg 46
// we start in 8bit mode, try to set 4 bit mode
lq_transmitI2C(&device, 0x03 << 4);
_delay_us(4500); // wait min 4.1ms
// second try
lq_writeDevice4Bits(&device, 0x03 << 4);
_delay_us(4500); // wait min 4.1ms
// third go!
lq_writeDevice4Bits(&device, 0x03 << 4);
_delay_us(150); // wait min 150 mics
// finally, set to 4-bit interface
lq_writeDevice4Bits(&device, 0x02 << 4);
// set # lines, font size, etc.
lq_sendCommand(&device, LCD_FUNCTIONSET | device.DisplayFunction);
// turn the display on with no cursor or blinking default
lq_turnOnDisplay(&device);
// clear it off
lq_clear(&device);
// set the entry mode
lq_sendCommand(&device, LCD_ENTRYMODESET | device.DisplayMode);
lq_returnHome(&device);
return device;
};
void lq_print(struct LiquidCrystalDevice_t* device, char * value)
{
char letter = *value;
while(letter != 0x00)
{
lq_writeDeviceByte(device, letter, LCD_REGISTER_SELECT_BIT);
letter = *(++value);
}
};
void lq_turnOnBacklight(struct LiquidCrystalDevice_t* device)
{
device->Backlight = LCD_BACKLIGHT;
lq_transmitI2C(device, 0);
}
void lq_turnOffBacklight(struct LiquidCrystalDevice_t* device)
{
device->Backlight = LCD_NOBACKLIGHT;
lq_transmitI2C(device, 0);
}
void lq_clear(LiquidCrystalDevice_t* device)
{
lq_sendCommand(device, LCD_CLEARDISPLAY); // clear display, set cursor position to zero
_delay_us(2000); // this command takes a long time!
lq_setCursor(device, 0, 0);
}
void lq_setCursor(LiquidCrystalDevice_t* device, uint8_t row, uint8_t column)
{
uint8_t row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
lq_sendCommand(device, LCD_SETDDRAMADDR | (column + row_offsets[row]));
}
void lq_returnHome(LiquidCrystalDevice_t* device)
{
lq_sendCommand(device, LCD_RETURNHOME); // set cursor position to zero
_delay_us(2000); // this command takes a long time!
};
void lq_turnOnDisplay(LiquidCrystalDevice_t* device)
{
device->DisplayControl |= LCD_DISPLAYON;
lq_sendCommand(device, LCD_DISPLAYCONTROL | device->DisplayControl);
};
void lq_turnOffDisplay(LiquidCrystalDevice_t* device)
{
device->DisplayControl &= ~LCD_DISPLAYON;
lq_sendCommand(device, LCD_DISPLAYCONTROL | device->DisplayControl);
};
void lq_turnOnCursor(struct LiquidCrystalDevice_t* device)
{
device->DisplayControl |= LCD_CURSORON;
lq_sendCommand(device, LCD_DISPLAYCONTROL | device->DisplayControl);
}
void lq_turnOffCursor(struct LiquidCrystalDevice_t* device)
{
device->DisplayControl &= ~LCD_CURSORON;
lq_sendCommand(device, LCD_DISPLAYCONTROL | device->DisplayControl);
}
void lq_turnOnBlink(struct LiquidCrystalDevice_t* device)
{
device->DisplayControl |= LCD_BLINKON;
lq_sendCommand(device, LCD_DISPLAYCONTROL | device->DisplayControl);
}
void lq_turnOffBlink(struct LiquidCrystalDevice_t* device)
{
device->DisplayControl &= ~LCD_BLINKON;
lq_sendCommand(device, LCD_DISPLAYCONTROL | device->DisplayControl);
}
void lq_scrollDisplayLeft(struct LiquidCrystalDevice_t* device)
{
lq_sendCommand(device, LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void lq_scrollDisplayRight(struct LiquidCrystalDevice_t* device)
{
lq_sendCommand(device, LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}
void lq_leftToRight(struct LiquidCrystalDevice_t* device)
{
device->DisplayMode |= LCD_ENTRYLEFT;
lq_sendCommand(device, LCD_ENTRYMODESET | device->DisplayMode);
}
void lq_rightToLeft(struct LiquidCrystalDevice_t* device)
{
device->DisplayMode &= ~LCD_ENTRYLEFT;
lq_sendCommand(device, LCD_ENTRYMODESET | device->DisplayMode);
}
void lq_turnOnAutoscroll(struct LiquidCrystalDevice_t* device)
{
device->DisplayMode |= LCD_ENTRYSHIFTINCREMENT;
lq_sendCommand(device, LCD_ENTRYMODESET | device->DisplayMode);
}
void lq_turnOffAutoscroll(struct LiquidCrystalDevice_t* device)
{
device->DisplayMode &= ~LCD_ENTRYSHIFTINCREMENT;
lq_sendCommand(device, LCD_ENTRYMODESET | device->DisplayMode);
}
void lq_createChar(struct LiquidCrystalDevice_t* device, uint8_t slot, uint8_t charmap[8])
{
uint8_t i = 0;
slot &= 0x7; // we only have 8 locations 0-7
lq_sendCommand(device, LCD_SETCGRAMADDR | (slot << 3));
for (i = 0; i < 8; i++)
{
lq_writeDeviceByte(device, charmap[i], LCD_REGISTER_SELECT_BIT);
}
}
void lq_sendCommand(LiquidCrystalDevice_t* device, uint8_t command)
{
lq_writeDeviceByte(device, command, 0);
}
void lq_writeDeviceByte(LiquidCrystalDevice_t* device, uint8_t value, uint8_t mode)
{
uint8_t highnib= value & 0xf0;
uint8_t lownib= (value<<4) & 0xf0;
lq_writeDevice4Bits(device, highnib | mode);
lq_writeDevice4Bits(device, lownib | mode);
};
void lq_writeDevice4Bits(LiquidCrystalDevice_t* device, uint8_t value)
{
lq_transmitI2C(device, value);
lq_writeDevicePulse(device, value);
};
void lq_writeDevicePulse(LiquidCrystalDevice_t* device, uint8_t value)
{
lq_transmitI2C(device, value | LCD_ENABLE_BIT);
_delay_us(2);
lq_transmitI2C(device, value & ~LCD_ENABLE_BIT);
_delay_us(50);
};
void lq_transmitI2C(LiquidCrystalDevice_t* device, uint8_t value)
{
Wire.beginTransmission(device->Address); // transmit to device
Wire.write(value | device->Backlight); // sends one byte
Wire.endTransmission(); // stop transmitting
};
LiquidCrystalDevice_t lcd;
void setup() {
lcd = lq_init(I2C_ADDR, LCD_COLUMNS, LCD_LINES, LCD_5x8DOTS);
lq_turnOnBacklight(&lcd);
Wire.begin(lcd.Address);
dht.begin();
pinMode(TUP,INPUT_PULLUP);
pinMode(TDN,INPUT_PULLUP);
pinMode(OUT, OUTPUT);
}
void loop() {
static float tempLast = 0, humLast = 0;
static float tempSet = 38;
static byte outputState = LOW;
static unsigned long lastSwitchTime = 0;
char buf[10];
float temperature;
float humidity;
temperature = dht.readTemperature();
humidity = dht.readHumidity();
if ((tempLast != temperature) || (humLast != humidity)) {
tempLast = temperature;
humLast = humidity;
lq_clear(&lcd);
lq_setCursor(&lcd, 0, 0);
lq_print(&lcd, "Temp: ");
dtostrf(temperature, 3, 1, buf);
lq_print(&lcd, buf);
lq_print(&lcd, " \xDF");
lq_print(&lcd, "C");
lq_setCursor(&lcd, 1, 0);
lq_print(&lcd, "Hum: ");
dtostrf(humidity, 3, 1, buf);
lq_print(&lcd, buf);
lq_print(&lcd, " %");
}
if (digitalRead(TUP) == LOW) {
tempSet += .1;
lq_clear(&lcd);
lq_setCursor(&lcd, 0, 0);
lq_print(&lcd, "T. Set: ");
dtostrf(tempSet, 3, 1, buf);
lq_print(&lcd, buf);
lq_print(&lcd, " \xDF");
lq_print(&lcd, "C");
delay(100);
}
if (digitalRead(TDN) == LOW) {
tempSet -= .1;
lq_clear(&lcd);
lq_setCursor(&lcd, 0, 0);
lq_print(&lcd, "T. Set: ");
dtostrf(tempSet, 3, 1, buf);
lq_print(&lcd, buf);
lq_print(&lcd, " \xDF");
lq_print(&lcd, "C");
delay(100);
}
byte dutyCycle = map(constrain(tempSet - temperature, -OFFSET , OFFSET), -OFFSET, OFFSET, 0, 100);
unsigned long onTime = (dutyCycle * PERIOD) / 100;
unsigned long offTime = PERIOD - onTime;
unsigned long currentTime = millis();
if (outputState == HIGH && (currentTime - lastSwitchTime >= onTime))
{
lastSwitchTime = currentTime;
outputState = LOW;
}
if (outputState == LOW && (currentTime - lastSwitchTime >= offTime))
{
lastSwitchTime = currentTime;
outputState = HIGH;
}
digitalWrite(OUT, outputState);
}