/*
* DỰ ÁN: TRANSMITTER PRO - LIGHTWEIGHT VERSION
* TỐI ƯU: Loại bỏ thư viện RTClib để tiết kiệm bộ nhớ Flash
* CHỨC NĂNG: Dual Display + Giám sát mất điện + Tuỳ chỉnh thang đo
*/
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <TM1637Display.h>
// --- CẤU HÌNH ĐỊA CHỈ I2C ---
#define DS1307_ADDR 0x68
#define OLED_ADDR 0x3C
// --- CẤU HÌNH CHÂN ---
// I2C: D4 (SDA), D5 (SCL)
#define TM_CLK D11
#define TM_DIO D12
#define PIN_RELAY_WARN A1
#define PIN_RELAY_DANGER A4
#define PIN_SENSOR A0
#define PIN_ANALOG_OUT D9
#define PIN_BTN_MENU D2
#define PIN_BTN_UP D3
#define PIN_BTN_DOWN D6
SSD1306AsciiWire oled;
TM1637Display tm(TM_CLK, TM_DIO);
// --- CẤU TRÚC LƯU THỜI GIAN ---
struct TimeData {
uint8_t sec;
uint8_t min;
uint8_t hour;
uint8_t day;
uint8_t month;
uint8_t year; // Lưu 2 số cuối (ví dụ 24 cho 2024)
};
TimeData now;
TimeData lastPowerOff;
// --- BIẾN HỆ THỐNG ---
long rawADC = 0;
int percentValue = 0;
int displayedValue = 0;
int currentOutput_x100 = 400;
int rangeMin = 0;
int rangeMax = 100;
int alarmWarn = 25;
int alarmDanger = 50;
int holdCounter = 0;
enum SystemMode { RUN, INFO, SET_WARN, SET_DANGER, SET_MIN, SET_MAX };
SystemMode currentMode = RUN;
unsigned long lastUpdate = 0;
unsigned long lastSecondMillis = 0;
unsigned long uptimeSeconds = 0;
// --- CÁC HÀM HỖ TRỢ BCD (Thay thế thư viện) ---
uint8_t decToBcd(uint8_t val) { return ((val / 10 * 16) + (val % 10)); }
uint8_t bcdToDec(uint8_t val) { return ((val / 16 * 10) + (val % 16)); }
// Khai báo hàm
void readRTC();
void readNVRAM();
void writeNVRAM();
void handleButtons();
void updateOLED();
void updateTM1637();
void processLogic();
long readSensorSmooth();
// --- SETUP ---
void setup() {
pinMode(PIN_RELAY_WARN, OUTPUT);
pinMode(PIN_RELAY_DANGER, OUTPUT);
pinMode(PIN_ANALOG_OUT, OUTPUT);
pinMode(PIN_BTN_MENU, INPUT_PULLUP);
pinMode(PIN_BTN_UP, INPUT_PULLUP);
pinMode(PIN_BTN_DOWN, INPUT_PULLUP);
Wire.setSDA(D4);
Wire.setSCL(D5);
Wire.begin();
oled.begin(&Adafruit128x64, OLED_ADDR);
oled.setFont(Adafruit5x7);
tm.setBrightness(4);
// Đọc dữ liệu mất điện lần trước từ NVRAM (Địa chỉ 0x08)
readNVRAM();
// Khởi động RTC (Gửi bit 0 vào giây để bật dao động)
Wire.beginTransmission(DS1307_ADDR);
Wire.write(0); // Đặt con trỏ về giây
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDR, 1);
byte sec = Wire.read();
if (sec & 0x80) { // Nếu bit CH (Clock Halt) đang bật
Wire.beginTransmission(DS1307_ADDR);
Wire.write(0);
Wire.write(0); // Bật dao động
Wire.endTransmission();
}
oled.clear();
oled.println("SYSTEM READY");
delay(500);
oled.clear();
}
// --- LOOP ---
void loop() {
handleButtons();
// Cập nhật mỗi giây
if (millis() - lastSecondMillis >= 1000) {
lastSecondMillis = millis();
uptimeSeconds++;
readRTC(); // Đọc giờ hiện tại
writeNVRAM(); // Ghi ngay vào NVRAM
}
// Cập nhật logic (100ms)
if (millis() - lastUpdate >= 100) {
lastUpdate = millis();
rawADC = readSensorSmooth();
percentValue = map(rawADC, 0, 1023, 0, 100);
percentValue = constrain(percentValue, 0, 100);
displayedValue = map(rawADC, 0, 1023, rangeMin, rangeMax);
currentOutput_x100 = 400 + (percentValue * 16);
processLogic();
updateOLED();
updateTM1637();
}
}
// --- GIAO TIẾP DS1307 THỦ CÔNG (KHÔNG DÙNG THƯ VIỆN) ---
void readRTC() {
Wire.beginTransmission(DS1307_ADDR);
Wire.write(0); // Đặt con trỏ thanh ghi về 0
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDR, 7); // Đọc 7 byte đầu
now.sec = bcdToDec(Wire.read() & 0x7F);
now.min = bcdToDec(Wire.read());
now.hour = bcdToDec(Wire.read() & 0x3F);
Wire.read(); // Bỏ qua thứ trong tuần
now.day = bcdToDec(Wire.read());
now.month = bcdToDec(Wire.read());
now.year = bcdToDec(Wire.read());
}
void writeNVRAM() {
// NVRAM của DS1307 bắt đầu từ địa chỉ 0x08
Wire.beginTransmission(DS1307_ADDR);
Wire.write(0x08);
Wire.write(decToBcd(now.sec));
Wire.write(decToBcd(now.min));
Wire.write(decToBcd(now.hour));
Wire.write(decToBcd(now.day));
Wire.write(decToBcd(now.month));
Wire.write(decToBcd(now.year));
Wire.endTransmission();
}
void readNVRAM() {
Wire.beginTransmission(DS1307_ADDR);
Wire.write(0x08);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDR, 6);
lastPowerOff.sec = bcdToDec(Wire.read());
lastPowerOff.min = bcdToDec(Wire.read());
lastPowerOff.hour = bcdToDec(Wire.read());
lastPowerOff.day = bcdToDec(Wire.read());
lastPowerOff.month = bcdToDec(Wire.read());
lastPowerOff.year = bcdToDec(Wire.read());
}
// --- HIỂN THỊ ---
void updateOLED() {
if (currentMode == RUN) {
oled.setCursor(0, 0); oled.print("PV: "); oled.print(displayedValue); oled.print(" ");
oled.setCursor(0, 2); oled.print("[");
int bar = map(percentValue, 0, 100, 0, 16);
for(int i=0; i<16; i++) { if(i<bar) oled.print("|"); else oled.print("."); }
oled.print("]");
oled.setCursor(0, 4); oled.print("TIME: ");
if(now.hour<10) oled.print("0"); oled.print(now.hour); oled.print(":");
if(now.min<10) oled.print("0"); oled.print(now.min); oled.print(":");
if(now.sec<10) oled.print("0"); oled.print(now.sec);
oled.setCursor(0, 6); oled.print("[MENU] Log Tat");
}
else if (currentMode == INFO) {
oled.setCursor(0, 0); oled.print("LICH SU TAT:");
oled.setCursor(20, 2); oled.set2X();
if(lastPowerOff.hour<10) oled.print("0"); oled.print(lastPowerOff.hour); oled.print(":");
if(lastPowerOff.min<10) oled.print("0"); oled.print(lastPowerOff.min);
oled.set1X();
oled.setCursor(0, 5);
oled.print("Ngay: ");
oled.print(lastPowerOff.day); oled.print("/");
oled.print(lastPowerOff.month); oled.print("/20");
oled.print(lastPowerOff.year);
}
else {
oled.setCursor(0, 0); oled.print("- CAI DAT -");
oled.setCursor(0, 2);
if (currentMode == SET_WARN) oled.print("Canh bao (Warn):");
else if (currentMode == SET_DANGER) oled.print("Nguy hiem (Danger):");
else if (currentMode == SET_MIN) oled.print("Thang do MIN:");
else if (currentMode == SET_MAX) oled.print("Thang do MAX:");
oled.set2X(); oled.setCursor(40, 4);
if (currentMode == SET_WARN) oled.print(alarmWarn);
else if (currentMode == SET_DANGER) oled.print(alarmDanger);
else if (currentMode == SET_MIN) oled.print(rangeMin);
else if (currentMode == SET_MAX) oled.print(rangeMax);
oled.set1X();
}
}
// --- LOGIC VÀ NÚT BẤM ---
void handleButtons() {
if (digitalRead(PIN_BTN_MENU) == LOW) {
delay(100); oled.clear(); holdCounter = 0;
if (currentMode == RUN) currentMode = INFO;
else if (currentMode == INFO) currentMode = SET_WARN;
else if (currentMode == SET_WARN) currentMode = SET_DANGER;
else if (currentMode == SET_DANGER) currentMode = SET_MIN;
else if (currentMode == SET_MIN) currentMode = SET_MAX;
else currentMode = RUN;
while(digitalRead(PIN_BTN_MENU) == LOW);
}
if (currentMode >= SET_WARN) {
bool isUp = (digitalRead(PIN_BTN_UP) == LOW);
bool isDown = (digitalRead(PIN_BTN_DOWN) == LOW);
if (isUp || isDown) {
holdCounter++;
int step = 1;
if (holdCounter > 10) step = 10;
if (holdCounter > 30) step = 100;
if (isUp) {
if (currentMode == SET_WARN) alarmWarn += step;
else if (currentMode == SET_DANGER) alarmDanger += step;
else if (currentMode == SET_MIN) rangeMin += step;
else if (currentMode == SET_MAX) rangeMax += step;
} else {
if (currentMode == SET_WARN) alarmWarn -= step;
else if (currentMode == SET_DANGER) alarmDanger -= step;
else if (currentMode == SET_MIN) rangeMin -= step;
else if (currentMode == SET_MAX) rangeMax -= step;
}
delay(100);
} else { holdCounter = 0; }
if (rangeMax > 9999) rangeMax = 9999;
}
}
void processLogic() {
bool isDanger = (displayedValue >= alarmDanger);
bool isWarn = (displayedValue >= alarmWarn);
if (isDanger) { digitalWrite(PIN_RELAY_DANGER, HIGH); digitalWrite(PIN_RELAY_WARN, HIGH); }
else if (isWarn) { digitalWrite(PIN_RELAY_DANGER, LOW); digitalWrite(PIN_RELAY_WARN, HIGH); }
else { digitalWrite(PIN_RELAY_DANGER, LOW); digitalWrite(PIN_RELAY_WARN, LOW); }
analogWrite(PIN_ANALOG_OUT, map(percentValue, 0, 100, 0, 255));
}
void updateTM1637() {
tm.showNumberDecEx(currentOutput_x100, 0b01000000, true);
}
long readSensorSmooth() {
long sum = 0;
for (int i = 0; i < 10; i++) { sum += analogRead(PIN_SENSOR); delay(1); }
return sum / 10;
}Loading
st-nucleo-l031k6
st-nucleo-l031k6