#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
// --- Cấu hình chân màn hình ILI9341 ---
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 21
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// --- Cấu hình chân nút bấm (Giữ nguyên) ---
#define BTN_MODE 5
#define BTN_EDIT 25
#define BTN_UP 13
#define BTN_DOWN 14
#define BTN_PLUS1 32
#define BTN_PLUS1_DOWN 4
#define BTN_PLUS10 27
#define BTN_START 26
// --- Biến quản lý hệ thống ---
int systemState = 0;
int menuIndex = 0;
int mode = 0;
#define TOTAL_MODES 14 // 14 Mode Vật Lý
// Biến chống chớp màn hình (chỉ vẽ lại khi có thay đổi)
bool needUpdate = true;
// ==========================================
// HỆ THỐNG ĐỘNG CƠ TÍNH TOÁN (MATH ENGINE) & ĐƠN VỊ
// ==========================================
int calcVar = 0;
int cursorVar = 1;
float varVals[4] = {10.0, 10.0, 10.0, 10.0};
int varUnitIdx[4] = {0, 0, 0, 0};
const char* uNames[][3] = {
{""}, {"m", "cm", "km"}, {"s", "min", "h"}, {"kg", "g", "ton"},
{"m/s", "km/h", ""}, {"m/s2", "", ""}, {"N", "kN", ""},
{"J", "kJ", ""}, {"W", "kW", ""}
};
const float uMults[][3] = {
{1.0}, {1.0, 0.01, 1000.0}, {1.0, 60.0, 3600.0}, {1.0, 0.001, 1000.0},
{1.0, 0.277778, 1.0}, {1.0, 1.0, 1.0}, {1.0, 1000.0, 1.0},
{1.0, 1000.0, 1.0}, {1.0, 1000.0, 1.0}
};
const int numUnits[] = {1, 3, 3, 3, 2, 1, 2, 2, 2};
int numVarsInMode[14] = {3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4};
const char* mTitles[14] = {"VAN TOC: v=s/t", "BAM GIO: v=s/t", "ROI TU DO: s=1/2gt^2", "VAN TOC: v=v0+at", "QD: s=v0t+1/2at^2", "NEWTON: F=ma", "MA SAT: Fms=uN", "DAN HOI: Fdh=k.dl", "DONG LUONG: p=mv", "CONG: A=Fs", "CONG SUAT: P=A/t", "DONG NANG: Wd=1/2mv^2", "THE NANG: Wt=mgh", "HUONG TAM: Fht=mv^2/r"};
const char* vNames[14][4] = {
{"v", "s", "t", ""}, {"v", "s", "t", ""}, {"s", "g", "t", ""}, {"v", "v0", "a", "t"},
{"s", "v0", "a", "t"}, {"F", "m", "a", ""}, {"Fms", "u", "N", ""}, {"Fdh", "k", "dl", ""},
{"p", "m", "v", ""}, {"A", "F", "s", ""}, {"P", "A", "t", ""}, {"Wd", "m", "v", ""},
{"Wt", "m", "g", "h"}, {"Fht", "m", "v", "r"}
};
int vTypes[14][4] = {
{4, 1, 2, 0}, {4, 1, 2, 0}, {1, 5, 2, 0}, {4, 4, 5, 2},
{1, 4, 5, 2}, {6, 3, 5, 0}, {6, 0, 6, 0}, {6, 0, 1, 0},
{0, 3, 4, 0}, {7, 6, 1, 0}, {8, 7, 2, 0}, {7, 3, 4, 0},
{7, 3, 5, 1}, {6, 3, 4, 1}
};
unsigned long startTime = 0; bool running = false; float convInput = 36.0, convOutput = 10.0; int convType = 0;
void setup() {
pinMode(BTN_MODE, INPUT_PULLUP); pinMode(BTN_EDIT, INPUT_PULLUP);
pinMode(BTN_UP, INPUT_PULLUP); pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_PLUS1, INPUT_PULLUP); pinMode(BTN_PLUS1_DOWN, INPUT_PULLUP);
pinMode(BTN_PLUS10, INPUT_PULLUP); pinMode(BTN_START, INPUT_PULLUP);
// Khởi tạo màn hình ILI9341
tft.begin();
tft.setRotation(1); // Xoay ngang màn hình (320x240)
tft.fillScreen(ILI9341_BLACK);
// Nền đen, chữ trắng để overwrite text không bị lỗi hiển thị
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
// Màn hình khởi động (Đã được scale lên)
tft.drawRect(0, 0, 320, 240, ILI9341_WHITE);
tft.setTextSize(4); tft.setCursor(50, 60); tft.println("PhysiCore");
tft.setTextSize(3); tft.setCursor(140, 120); tft.println("1.0");
delay(1500);
needUpdate = true;
}
void adjustVal(float amt) {
varVals[cursorVar] += amt;
if(varVals[cursorVar] < 0 && (vTypes[mode][cursorVar] == 2 || vTypes[mode][cursorVar] == 3))
varVals[cursorVar] = 0;
needUpdate = true;
}
void solveMath() {
// (Giữ nguyên toàn bộ logic tính toán của bạn)
float si[4];
for(int i=0; i<numVarsInMode[mode]; i++) {
si[i] = varVals[i] * uMults[vTypes[mode][i]][varUnitIdx[i]];
}
switch(mode) {
case 0: case 1:
if(calcVar==0) si[0] = si[1] / (si[2]!=0?si[2]:1); else if(calcVar==1) si[1] = si[0] * si[2]; else if(calcVar==2) si[2] = si[1] / (si[0]!=0?si[0]:1); break;
case 2:
if(calcVar==0) si[0] = 0.5 * si[1] * si[2] * si[2]; else if(calcVar==1) si[1] = (2*si[0]) / (si[2]*si[2]!=0?si[2]*si[2]:1); else if(calcVar==2) si[2] = sqrt(abs((2*si[0]) / (si[1]!=0?si[1]:1))); break;
case 3:
if(calcVar==0) si[0] = si[1] + (si[2]*si[3]); else if(calcVar==1) si[1] = si[0] - (si[2]*si[3]); else if(calcVar==2) si[2] = (si[0] - si[1]) / (si[3]!=0?si[3]:1); else if(calcVar==3) si[3] = (si[0] - si[1]) / (si[2]!=0?si[2]:1); break;
case 4:
if(calcVar==0) si[0] = (si[1]*si[3]) + (0.5*si[2]*si[3]*si[3]); else if(calcVar==1) si[1] = (si[0] - 0.5*si[2]*si[3]*si[3]) / (si[3]!=0?si[3]:1); else if(calcVar==2) si[2] = 2*(si[0] - si[1]*si[3]) / (si[3]*si[3]!=0?si[3]*si[3]:1); else if(calcVar==3) { if (si[2] == 0) si[3] = si[0] / (si[1]!=0?si[1]:1); else { float delta = si[1]*si[1] + 2*si[2]*si[0]; if(delta>=0) si[3] = (-si[1] + sqrt(delta))/si[2]; } } break;
case 5: case 6: case 7: case 8: case 9:
if(calcVar==0) si[0] = si[1] * si[2]; else if(calcVar==1) si[1] = si[0] / (si[2]!=0?si[2]:1); else if(calcVar==2) si[2] = si[0] / (si[1]!=0?si[1]:1); break;
case 10:
if(calcVar==0) si[0] = si[1] / (si[2]!=0?si[2]:1); else if(calcVar==1) si[1] = si[0] * si[2]; else if(calcVar==2) si[2] = si[1] / (si[0]!=0?si[0]:1); break;
case 11:
if(calcVar==0) si[0] = 0.5 * si[1] * si[2] * si[2]; else if(calcVar==1) si[1] = (2*si[0]) / (si[2]*si[2]!=0?si[2]*si[2]:1); else if(calcVar==2) si[2] = sqrt(abs((2*si[0]) / (si[1]!=0?si[1]:1))); break;
case 12:
if(calcVar==0) si[0] = si[1] * si[2] * si[3]; else if(calcVar==1) si[1] = si[0] / (si[2]*si[3]!=0?si[2]*si[3]:1); else if(calcVar==2) si[2] = si[0] / (si[1]*si[3]!=0?si[1]*si[3]:1); else if(calcVar==3) si[3] = si[0] / (si[1]*si[2]!=0?si[1]*si[2]:1); break;
case 13:
if(calcVar==0) si[0] = si[1] * si[2] * si[2] / (si[3]!=0?si[3]:1); else if(calcVar==1) si[1] = si[0] * si[3] / (si[2]*si[2]!=0?si[2]*si[2]:1); else if(calcVar==2) si[2] = sqrt(abs(si[0] * si[3] / (si[1]!=0?si[1]:1))); else if(calcVar==3) si[3] = si[1] * si[2] * si[2] / (si[0]!=0?si[0]:1); break;
}
varVals[calcVar] = si[calcVar] / uMults[vTypes[mode][calcVar]][varUnitIdx[calcVar]];
}
void loop() {
// ==========================================
// XỬ LÝ NÚT BẤM
// ==========================================
if (systemState == 0) {
if (digitalRead(BTN_EDIT) == LOW) { menuIndex = (menuIndex == 0) ? 1 : 0; needUpdate = true; delay(300); }
if (digitalRead(BTN_START) == LOW) { systemState = menuIndex + 1; needUpdate = true; delay(300); }
}
else if (systemState == 1) {
if (digitalRead(BTN_MODE) == LOW) { mode++; if(mode >= TOTAL_MODES) { systemState = 0; mode = 0; } calcVar = 0; cursorVar = 1; needUpdate = true; delay(300); }
if (digitalRead(BTN_EDIT) == LOW) { cursorVar++; if (cursorVar >= numVarsInMode[mode]) cursorVar = 0; if (cursorVar == calcVar) { cursorVar++; if(cursorVar >= numVarsInMode[mode]) cursorVar = 0; } needUpdate = true; delay(200); }
if (digitalRead(BTN_START) == LOW && mode != 1) { calcVar++; if (calcVar >= numVarsInMode[mode]) calcVar = 0; if (cursorVar == calcVar) { cursorVar++; if(cursorVar >= numVarsInMode[mode]) cursorVar = 0; } needUpdate = true; delay(250); }
if (digitalRead(BTN_UP) == LOW) { adjustVal(0.1); delay(100); }
if (digitalRead(BTN_DOWN) == LOW) { adjustVal(-0.1); delay(100); }
if (digitalRead(BTN_PLUS1) == LOW) { adjustVal(1.0); delay(150); }
if (digitalRead(BTN_PLUS1_DOWN) == LOW) { adjustVal(-1.0); delay(150); }
if (digitalRead(BTN_PLUS10) == LOW) { int t = vTypes[mode][cursorVar]; varUnitIdx[cursorVar]++; if(varUnitIdx[cursorVar] >= numUnits[t]) varUnitIdx[cursorVar] = 0; needUpdate = true; delay(250); }
// Bấm giờ
if (mode == 1 && digitalRead(BTN_START) == LOW) { if (!running) { startTime = millis(); running = true; } else { varVals[2] = (millis() - startTime) / 1000.0; running = false; } needUpdate = true; delay(300); }
if (mode == 1 && running) { varVals[2] = (millis() - startTime) / 1000.0; needUpdate = true; } // Cập nhật liên tục khi chạy
solveMath();
}
else if (systemState == 2) {
if (digitalRead(BTN_MODE) == LOW) { systemState = 0; needUpdate = true; delay(300); }
if (digitalRead(BTN_EDIT) == LOW) { convType++; if (convType > 19) convType = 0; needUpdate = true; delay(250); }
if (digitalRead(BTN_UP) == LOW) { convInput += 0.01; needUpdate = true; delay(100); }
if (digitalRead(BTN_DOWN) == LOW) { convInput -= 0.01; needUpdate = true; delay(100); }
if (digitalRead(BTN_PLUS1) == LOW) { convInput += 1.0; needUpdate = true; delay(150); }
if (digitalRead(BTN_PLUS1_DOWN) == LOW) { convInput -= 1.0; needUpdate = true; delay(150); }
if (digitalRead(BTN_PLUS10) == LOW) { convInput += 10.0; needUpdate = true; delay(150); }
switch (convType) {
case 0: convOutput = convInput / 3.6; break; case 1: convOutput = convInput * 3.6; break;
case 2: convOutput = convInput / 100.0; break; case 3: convOutput = convInput / 1000.0; break;
case 4: convOutput = convInput * 1000.0; break; case 5: convOutput = convInput / 1000.0; break;
case 6: convOutput = convInput / 1000.0; break; case 7: convOutput = convInput * 1000.0; break;
case 8: convOutput = convInput * 1000.0; break; case 9: convOutput = convInput * 60.0; break;
case 10: convOutput = convInput * 3600.0; break; case 11: convOutput = convInput * 60.0; break;
case 12: convOutput = convInput / 60.0; break; case 13: convOutput = convInput * 3.14159 / 180.0; break;
case 14: convOutput = convInput * 180.0 / 3.14159; break; case 15: convOutput = convInput * 1000.0; break;
case 16: convOutput = convInput / 1000.0; break; case 17: convOutput = convInput * 1000.0; break;
case 18: convOutput = convInput * 4.184; break; case 19: convOutput = convInput * 746.0; break;
}
}
// ==========================================
// VẼ LÊN MÀN HÌNH (Chỉ vẽ khi có thay đổi)
// ==========================================
if (needUpdate) {
tft.fillScreen(ILI9341_BLACK); // Xoá màn hình
if (systemState == 0) {
tft.setTextSize(2);
tft.setCursor(70, 30); tft.print("- CHON CHE DO -");
tft.setCursor(40, 90); tft.print(menuIndex == 0 ? "> 1. TINH TOAN" : " 1. TINH TOAN");
tft.setCursor(40, 140); tft.print(menuIndex == 1 ? "> 2. DOI DON VI" : " 2. DOI DON VI");
tft.setCursor(10, 210); tft.print("D25: Chon | START: vao");
}
else if (systemState == 1) {
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.print(mTitles[mode]);
tft.drawFastHLine(0, 35, 320, ILI9341_WHITE);
int y = 50;
tft.setTextSize(3); // Chữ to, dễ nhìn trên TFT
for(int i = 0; i < numVarsInMode[mode]; i++) {
tft.setCursor(10, y);
if (calcVar == i && mode != 1) tft.print("? ");
else if (cursorVar == i) tft.print("> ");
else tft.print(" ");
tft.print(vNames[mode][i]); tft.print(": ");
if(varVals[i] < 10) tft.print(" "); else if (varVals[i] < 100) tft.print(" ");
tft.print(varVals[i], 2);
int typeIdx = vTypes[mode][i];
if (typeIdx != 0) {
tft.print(" ["); tft.print(uNames[typeIdx][varUnitIdx[i]]); tft.print("]");
}
y += 45; // Khoảng cách giãn dòng
}
}
else if (systemState == 2) {
String uI = "", uO = "";
switch (convType) {
case 0: uI="km/h"; uO="m/s"; break; case 1: uI="m/s"; uO="km/h"; break;
case 2: uI="cm"; uO="m"; break; case 3: uI="mm"; uO="m"; break;
case 4: uI="km"; uO="m"; break; case 5: uI="m"; uO="km"; break;
case 6: uI="g"; uO="kg"; break; case 7: uI="kg"; uO="g"; break;
case 8: uI="Tan"; uO="kg"; break; case 9: uI="Phut"; uO="s"; break;
case 10: uI="Gio"; uO="s"; break; case 11: uI="Gio"; uO="Phut"; break;
case 12: uI="s"; uO="Phut"; break; case 13: uI="Do";uO="Rad"; break;
case 14: uI="Rad"; uO="Do"; break; case 15: uI="kJ"; uO="J"; break;
case 16: uI="J"; uO="kJ"; break; case 17: uI="kN"; uO="N"; break;
case 18: uI="Cal"; uO="J"; break; case 19: uI="HP"; uO="W"; break;
}
tft.setTextSize(2); tft.setCursor(10, 10);
tft.print("D:"); tft.print(convType); tft.print(" "); tft.print(uI); tft.print(" -> "); tft.print(uO);
tft.drawFastHLine(0, 35, 320, ILI9341_WHITE);
tft.setCursor(10, 70); tft.setTextSize(4); tft.print(convInput, 2);
tft.setTextSize(2); tft.print(" "); tft.print(uI);
tft.setCursor(10, 130); tft.print("Bang:");
tft.setCursor(10, 170); tft.setTextSize(4);
if (convOutput > 9999) tft.setTextSize(3);
tft.print(convOutput, 2);
tft.setTextSize(2); tft.print(" "); tft.print(uO);
}
needUpdate = false; // Đã vẽ xong, tắt cờ báo
}
}