#include <Wire.h> // Библиотека для работы с I2C
// Адреса устройств I2C
#define LCD_I2C_ADDR 0x27 // Адрес ЖК-дисплея
#define BMP180_I2C_ADDR 0x77 // Адрес BMP180
// Определение битов управления для LCD через PCF8574
#define LCD_RS 0x01 // P0
#define LCD_RW 0x00 // P1
#define LCD_EN 0x04 // P2
#define LCD_BACKLIGHT 0x08 // P3
// Определение команд LCD
#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
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
#define LCD_MOVELEFT 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
#define LCD_ENTRYSHIFTDECREMENT 0x00
// Переменные для хранения данных
float temperature = 0;
long pressure = 0;
// Таймер для вывода имени и фамилии
unsigned long previousMillis = 0;
const long interval = 5000; // 5 секунд
// Функции для работы с I2C
// Записывает байт данных на указанный адрес I2C.
void I2C_WriteByte(byte addr, byte data) {
Wire.beginTransmission(addr);
Wire.write(data);
Wire.endTransmission();
}
// Читает байты данных с указанного адреса I2C.
void I2C_ReadBytes(byte addr, byte reg, byte numBytes, byte* buffer) {
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom(addr, numBytes);
for (byte i = 0; i < numBytes; i++) {
if (Wire.available()) {
buffer[i] = Wire.read();
}
}
}
// Функции для работы с ЖК-дисплеем
// Отправляет полубайт на ЖК-дисплей.
void lcd_sendHalfByte(byte data, byte rs) {
byte bits = data & 0xF0;
bits |= (rs ? LCD_RS : 0) | LCD_BACKLIGHT;
I2C_WriteByte(LCD_I2C_ADDR, bits);
I2C_WriteByte(LCD_I2C_ADDR, bits | LCD_EN);
delayMicroseconds(1);
I2C_WriteByte(LCD_I2C_ADDR, bits & ~LCD_EN);
delayMicroseconds(50);
}
// Отправляет команду на ЖК-дисплей.
void lcd_command(byte cmd) {
lcd_sendHalfByte(cmd & 0xF0, 0);
lcd_sendHalfByte((cmd << 4) & 0xF0, 0);
}
// Отправляет данные на ЖК-дисплей.
void lcd_data(byte data) {
lcd_sendHalfByte(data & 0xF0, 1);
lcd_sendHalfByte((data << 4) & 0xF0, 1);
}
// Инициализация ЖК-дисплея.
void lcd_init() {
delay(50); // Подождать, чтобы ЖК-дисплей стабилизировался
// Инициализация в 4-битном режиме
lcd_sendHalfByte(0x30, 0);
delay(5);
lcd_sendHalfByte(0x30, 0);
delayMicroseconds(160);
lcd_sendHalfByte(0x30, 0);
delay(1);
lcd_sendHalfByte(0x20, 0);
delayMicroseconds(160);
// Настройка функций дисплея
lcd_command(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS);
delayMicroseconds(160);
// Включение дисплея без курсора и без мигания
lcd_command(LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
delayMicroseconds(160);
// Очистка дисплея
lcd_command(LCD_CLEARDISPLAY);
delay(2);
// Установка режима ввода: слева направо, без сдвига
lcd_command(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
delayMicroseconds(160);
}
// Устанавливает курсор на позицию.
void lcd_setCursor(byte col, byte row) {
byte row_offsets[] = {0x00, 0x40, 0x14, 0x54};
if (row > 1) row = 1;
lcd_command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}
// Очищает дисплей.
void lcd_clear() {
lcd_command(LCD_CLEARDISPLAY);
delay(2);
}
// Выводит строку на ЖК-дисплей.
void lcd_print(const char* str) {
while (*str) {
lcd_data(*str++);
}
}
// Функции для работы с BMP180
// Константы калибровки (будут заполнены из EEPROM датчика)
short bmp_AC1, bmp_AC2, bmp_AC3, bmp_B1, bmp_B2, bmp_MB, bmp_MC, bmp_MD;
unsigned short bmp_AC4, bmp_AC5, bmp_AC6;
// Чтение 16-битного значения с датчика BMP180.
short bmp180_readInt(byte addr) {
byte msb, lsb;
I2C_ReadBytes(BMP180_I2C_ADDR, addr, 2, &msb);
msb = msb;
lsb = lsb;
return (short)(msb << 8 | lsb);
}
// Инициализация датчика BMP180.
void bmp180_init() {
// Чтение калибровочных данных из EEPROM
bmp_AC1 = bmp180_readInt(0xAA);
bmp_AC2 = bmp180_readInt(0xAC);
bmp_AC3 = bmp180_readInt(0xAE);
bmp_AC4 = bmp180_readInt(0xB0);
bmp_AC5 = bmp180_readInt(0xB2);
bmp_AC6 = bmp180_readInt(0xB4);
bmp_B1 = bmp180_readInt(0xB6);
bmp_B2 = bmp180_readInt(0xB8);
bmp_MB = bmp180_readInt(0xBA);
bmp_MC = bmp180_readInt(0xBC);
bmp_MD = bmp180_readInt(0xBE);
}
// Чтение температуры с BMP180.
float bmp180_readTemperature() {
long UT, X1, X2, B5;
// Запуск измерения температуры
I2C_WriteByte(BMP180_I2C_ADDR, 0xF4);
I2C_WriteByte(BMP180_I2C_ADDR, 0x2E);
delay(5);
UT = bmp180_readInt(0xF6);
// Вычисления согласно даташиту
X1 = ((UT - bmp_AC6) * bmp_AC5) >> 15;
X2 = (bmp_MC << 11) / (X1 + bmp_MD);
B5 = X1 + X2;
float temp = ((B5 + 8) >> 4) / 10.0 * 0.6;
return temp;
}
// Чтение давления с BMP180.
long bmp180_readPressure() {
long UP, X1, X2, X3, B3, B5, B6, P;
unsigned long B4, B7;
// Получение B5 для дальнейших вычислений
X1 = ((bmp180_readInt(0xF6) - bmp_AC6) * bmp_AC5) >> 15;
X2 = (bmp_MC << 11) / (X1 + bmp_MD);
B5 = X1 + X2;
// Запуск измерения давления
I2C_WriteByte(BMP180_I2C_ADDR, 0xF4);
I2C_WriteByte(BMP180_I2C_ADDR, 0x34);
delay(5); // Задержка для измерения
UP = bmp180_readInt(0xF6);
// Вычисления давления согласно даташиту
B6 = B5 - 4000;
X1 = (bmp_B2 * ((B6 * B6) >> 12)) >> 11;
X2 = (bmp_AC2 * B6) >> 11;
X3 = X1 + X2;
B3 = (((bmp_AC1 * 4 + X3) << 1) + 2) >> 2;
X1 = (bmp_AC3 * B6) >> 13;
X2 = (bmp_B1 * ((B6 * B6) >> 12)) >> 16;
X3 = ((X1 + X2) + 2) >> 2;
B4 = (bmp_AC4 * (unsigned long)(X3 + 32768)) >> 15;
B7 = ((unsigned long)UP - B3) * 50000;
if (B7 < 0x80000000) {
P = (B7 * 2) / B4;
} else {
P = (B7 / B4) * 2;
}
X1 = (P >> 8) * (P >> 8);
X1 = (X1 * 3038) >> 16;
X2 = (-7357 * P) >> 16;
P = P + ((X1 + X2 + 3791) >> 4);
return P - 54237;
}
void setup() {
// Инициализация последовательного порта
Serial.begin(9600);
// Инициализация I2C
Wire.begin();
// Инициализация ЖК-дисплея
lcd_init();
// Инициализация BMP180
bmp180_init();
lcd_clear();
}
void loop() {
// Считывание данных с BMP180
temperature = bmp180_readTemperature(); // Температура в Цельсиях
pressure = bmp180_readPressure(); // Давление в Па
// Отображение данных на ЖК-дисплее
lcd_setCursor(0, 0);
lcd_print("T: ");
char tempStr[6];
dtostrf(temperature, 4, 1, tempStr);
lcd_print(tempStr);
lcd_print(" C");
lcd_setCursor(0, 1);
lcd_print("P: ");
char presStr[7];
dtostrf(pressure, 6, 1, presStr);
lcd_print(presStr);
lcd_print(" Pa");
// Обработка таймера для вывода имени и фамилии
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Вывод имени и фамилии в COM порт
Serial.println("Pavel Kutsin");
}
// Задержка перед следующим циклом
delay(1000);
}