/*
* DỰ ÁN: MẠCH TRANSMITTER 4-20mA (DUAL DISPLAY)
* BOARD: STM32 NUCLEO-L031K6
* DISPLAY 1: OLED SSD1306 (D4/D5) -> Hiển thị thông tin chính
* DISPLAY 2: TM1637 (D11/D12) -> Hiển thị dòng mA Output
*/
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <TM1637Display.h>
// --- 1. CẤU HÌNH CHÂN ---
// OLED I2C (SoftWire hoặc HardWire tùy core, trên Nucleo32 D4=SDA, D5=SCL)
#define OLED_SDA D4
#define OLED_SCL D5
#define I2C_ADDRESS 0x3C
// TM1637 (LED 7 Thanh)
#define TM_CLK D11
#define TM_DIO D12
// Relay Output
#define PIN_RELAY_WARN A1
#define PIN_RELAY_DANGER A4
// Analog
#define PIN_SENSOR A0
#define PIN_ANALOG_OUT D9
// Button
#define PIN_BTN_MENU D2
#define PIN_BTN_UP D3
#define PIN_BTN_DOWN D6
// --- 2. KHỞI TẠO ĐỐI TƯỢNG ---
SSD1306AsciiWire oled;
TM1637Display tm(TM_CLK, TM_DIO);
// --- 3. BIẾN HỆ THỐNG ---
int percentValue = 0; // Giá trị %
int currentOutput_x100 = 400; // Giá trị mA (400 = 4.00mA)
int alarmWarn = 25;
int alarmDanger = 50;
enum SystemMode { RUN, SET_LOW, SET_HIGH };
SystemMode currentMode = RUN;
unsigned long lastUpdate = 0;
const int UPDATE_INTERVAL = 100;
// Khai báo hàm
void handleButtons();
void updateOLED();
void updateTM1637();
void processAlarmOutput();
void processAnalogOutput();
long readSensorSmooth();
// --- 4. SETUP ---
void setup() {
// Config Pin
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);
// 1. Khởi tạo OLED
Wire.setSDA(OLED_SDA);
Wire.setSCL(OLED_SCL);
Wire.begin();
oled.begin(&Adafruit128x64, I2C_ADDRESS);
oled.setFont(Adafruit5x7);
// 2. Khởi tạo TM1637
tm.setBrightness(4); // Độ sáng trung bình
// Màn hình chào
oled.clear();
oled.println("SYSTEM START...");
oled.println("Dual Display Mode");
// Test TM1637 (Hiện 8888)
uint8_t data[] = { 0xff, 0xff, 0xff, 0xff };
tm.setSegments(data);
delay(1000);
oled.clear();
tm.clear();
}
// --- 5. LOOP ---
void loop() {
handleButtons();
if (millis() - lastUpdate >= UPDATE_INTERVAL) {
lastUpdate = millis();
// Đọc cảm biến
long raw = readSensorSmooth();
percentValue = (raw * 100) / 1023;
if (percentValue < 0) percentValue = 0;
if (percentValue > 100) percentValue = 100;
// Tính dòng điện Output (400 -> 2000)
currentOutput_x100 = 400 + (percentValue * 16);
// Xử lý Logic
processAlarmOutput();
processAnalogOutput();
// Cập nhật 2 màn hình
updateOLED();
updateTM1637();
}
}
// --- 6. CÁC HÀM XỬ LÝ ---
void handleButtons() {
if (digitalRead(PIN_BTN_MENU) == LOW) {
delay(100);
oled.clear(); // Xóa OLED khi chuyển menu
if (currentMode == RUN) currentMode = SET_LOW;
else if (currentMode == SET_LOW) currentMode = SET_HIGH;
else currentMode = RUN;
while(digitalRead(PIN_BTN_MENU) == LOW);
}
if (currentMode != RUN) {
if (digitalRead(PIN_BTN_UP) == LOW) {
delay(150);
if (currentMode == SET_LOW) alarmWarn++;
else if (currentMode == SET_HIGH) alarmDanger++;
}
if (digitalRead(PIN_BTN_DOWN) == LOW) {
delay(150);
if (currentMode == SET_LOW) alarmWarn--;
else if (currentMode == SET_HIGH) alarmDanger--;
}
// Limit
if (alarmWarn < 0) alarmWarn = 0; if (alarmWarn > 100) alarmWarn = 100;
if (alarmDanger < 0) alarmDanger = 0; if (alarmDanger > 100) alarmDanger = 100;
}
}
// -- Cập nhật OLED (Giao diện chính) --
void updateOLED() {
if (currentMode == RUN) {
oled.setCursor(0, 0);
oled.print("PV: "); oled.print(percentValue); oled.print("% ");
// Bar Graph
oled.setCursor(0, 2);
oled.print("[");
int barLength = (16 * percentValue) / 100;
for(int i=0; i<16; i++) {
if(i < barLength) oled.print("|"); else oled.print(".");
}
oled.print("]");
// Status
oled.setCursor(0, 4);
if (percentValue >= alarmDanger) oled.print("STATUS: DANGER!");
else if (percentValue >= alarmWarn) oled.print("STATUS: WARNING");
else oled.print("STATUS: NORMAL ");
oled.setCursor(0, 6);
oled.print("L:"); oled.print(alarmWarn);
oled.print(" H:"); oled.print(alarmDanger);
}
else if (currentMode == SET_LOW) {
oled.setCursor(0, 0); oled.print("- SETTING MODE -");
oled.setCursor(0, 2); oled.print("Set Warning (L):");
oled.set2X();
oled.setCursor(40, 4); oled.print(alarmWarn); oled.print("% ");
oled.set1X();
}
else if (currentMode == SET_HIGH) {
oled.setCursor(0, 0); oled.print("- SETTING MODE -");
oled.setCursor(0, 2); oled.print("Set Danger (H):");
oled.set2X();
oled.setCursor(40, 4); oled.print(alarmDanger); oled.print("% ");
oled.set1X();
}
}
// -- Cập nhật TM1637 (Chỉ hiện dòng mA Output) --
void updateTM1637() {
// Luôn luôn hiển thị dòng điện Output bất kể đang ở Menu nào
// 0b01000000 bật dấu hai chấm (:) -> Ví dụ: 12:00
tm.showNumberDecEx(currentOutput_x100, 0b01000000, true);
}
void processAlarmOutput() {
bool isDanger = (percentValue >= alarmDanger);
bool isWarn = (percentValue >= 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);
}
}
void processAnalogOutput() {
int pwmValue = (percentValue * 255) / 100;
analogWrite(PIN_ANALOG_OUT, pwmValue);
}
long readSensorSmooth() {
long sum = 0;
for (int i = 0; i < 10; i++) {
sum += analogRead(PIN_SENSOR);
delay(1);
}
return sum / 10;
}