#include <Wire.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define LCD_I2C_ADDRESS 0x27 // Define I2C Address where the PCF8574/A is
// Define LCD device operation constants
#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 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 En 0x04 // Enable bit
#define Rw 0x02 // Read/Write bit
#define Rs 0x01 // Register select bit
void lcdWriteNibble(int data) {
Wire.beginTransmission(LCD_I2C_ADDRESS);
Wire.write(data | LCD_BACKLIGHT);
Wire.write(data | En | LCD_BACKLIGHT);
delayMicroseconds(1); // Enable pulse must be >450ns
Wire.write(data & ~En | LCD_BACKLIGHT);
Wire.endTransmission();
delayMicroseconds(50); // commands need >37us to settle
}
void lcdWriteByte(int data, int mode) {
lcdWriteNibble(mode | (data & 0xF0));
lcdWriteNibble(mode | ((data << 4) & 0xF0));
}
void lcdCommand(int command) {
lcdWriteByte(command, 0);
}
void lcdWrite(int value) {
lcdWriteByte(value, Rs);
}
void lcdSetCursor(uint8_t col, uint8_t row) {
const uint8_t row_offsets[] = { 0x00, 0x40 };
if (row >= sizeof(row_offsets) / sizeof(row_offsets[0])) {
row = sizeof(row_offsets) / sizeof(row_offsets[0]) - 1; // keep row within bounds
}
lcdCommand(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}
void lcdPrint(const String &str, uint8_t col, uint8_t row) {
lcdSetCursor(col, row);
for (int i = 0; i < str.length(); i++) {
lcdWrite(str[i]);
}
}
void lcdInit() {
Wire.begin();
delay(50);
// Initialize in 4-bit mode
lcdWriteNibble(0x03 << 4);
delayMicroseconds(4500);
lcdWriteNibble(0x03 << 4);
delayMicroseconds(4500);
lcdWriteNibble(0x03 << 4);
delayMicroseconds(150);
lcdWriteNibble(0x02 << 4);
lcdCommand(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS);
lcdCommand(LCD_DISPLAYCONTROL | LCD_DISPLAYON);
lcdCommand(LCD_CLEARDISPLAY);
lcdCommand(LCD_ENTRYMODESET | LCD_ENTRYLEFT);
delayMicroseconds(2000); // This command takes a long time!
}
const uint8_t lm35Channel = 0;
volatile bool readTemperatureFlag = false;
volatile float temperature = 0.0;
ISR(TIMER1_COMPA_vect) {
readTemperatureFlag = true;
}
void adc_init() {
ADMUX |= (1 << REFS0);
ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
}
void readTemperature() {
if (readTemperatureFlag) {
ADMUX = (ADMUX & 0xF0) | (lm35Channel & 0x0F);
ADCSRA |= (1 << ADSC);
while (ADCSRA & (1 << ADSC));
uint16_t adcValue = ADC;
float voltage = adcValue * (5.0 / 1024.0);
temperature = voltage * 100.0;
readTemperatureFlag = false;
}
}
void setup() {
adc_init();
Wire.begin();
lcdInit();
lcdPrint("Temperature", 0, 0); // Print on the first line
cli(); // ปิด global interrupt
TCCR1A = 0; // ตั้งค่า Timer1 ให้เป็น normal mode
TCCR1B = (1 << WGM12) | (1 << CS12); // ตั้งค่า prescaler เป็น 1024
OCR1A = 7812; // ตั้งค่า compare match ให้เริ่มต้นนับเมื่อถึงค่านี้ (ประมาณ 1 วินาที)
TIMSK1 |= (1 << OCIE1A); // เปิดใช้งาน interrupt จาก compare match A
sei(); // เปิด global interrupt
}
void loop() {
if (readTemperatureFlag) {
readTemperature();
readTemperatureFlag = false;
lcdPrint(String(temperature), 0, 1);
delay(1000);
}
}