// ====== IoT 기반 고소작업차 스마트 안전관리 시스템 ======
// ====== 핀 정의 ======
#define SDA_PIN 8
#define SCL_PIN 7
#define HC_TRIG 12
#define HC_ECHO 6
#define BUZZER_PIN 2
#define FIRE_BUTTON 5
#define RGB_RED 23
#define RGB_GREEN 22
#define RGB_BLUE 21
#define WIND_SENSOR A0
#define NTC_SENSOR A1
#define LCD_ADDR 0x27
#define MPU6050_ADDR 0x68
// ====== 변수 정의 ======
float temperature = 25.0, humidity = 50.0, heatIndex;
float windSpeed;
float tiltX, tiltY;
float distance;
bool fireDetected = false;
int safetyLevel = 0;
unsigned long lastUpdate = 0;
void setup() {
Serial.begin(115200);
initI2C();
initLCD_I2C();
initMPU6050();
pinMode(HC_TRIG, OUTPUT);
pinMode(HC_ECHO, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(FIRE_BUTTON, INPUT_PULLUP);
pinMode(RGB_RED, OUTPUT);
pinMode(RGB_GREEN, OUTPUT);
pinMode(RGB_BLUE, OUTPUT);
lcdPrint_I2C(0, 0, "BatterySafe v2.0");
lcdPrint_I2C(0, 1, "System Starting");
delay(2000);
lcdClear_I2C();
Serial.println("=== I2C 스마트 안전관리 시스템 시작 ===");
}
void loop() {
if (millis() - lastUpdate >= 1000) {
lastUpdate = millis();
readSensors();
calculateHeatIndex();
evaluateSafety();
updateDisplay();
printDebugInfo();
}
handleAlerts();
delay(10);
}
// ====== I2C 함수들 ======
void initI2C() {
pinMode(SDA_PIN, OUTPUT);
pinMode(SCL_PIN, OUTPUT);
digitalWrite(SDA_PIN, HIGH);
digitalWrite(SCL_PIN, HIGH);
delay(10);
Serial.println("I2C 초기화 완료");
}
void i2c_start() {
digitalWrite(SDA_PIN, HIGH);
digitalWrite(SCL_PIN, HIGH);
delayMicroseconds(5);
digitalWrite(SDA_PIN, LOW);
delayMicroseconds(5);
digitalWrite(SCL_PIN, LOW);
delayMicroseconds(5);
}
void i2c_stop() {
digitalWrite(SDA_PIN, LOW);
digitalWrite(SCL_PIN, HIGH);
delayMicroseconds(5);
digitalWrite(SDA_PIN, HIGH);
delayMicroseconds(5);
}
void i2c_write_byte(uint8_t data) {
for (int i = 7; i >= 0; i--) {
digitalWrite(SDA_PIN, (data >> i) & 1);
delayMicroseconds(2);
digitalWrite(SCL_PIN, HIGH);
delayMicroseconds(5);
digitalWrite(SCL_PIN, LOW);
delayMicroseconds(2);
}
pinMode(SDA_PIN, INPUT);
digitalWrite(SCL_PIN, HIGH);
delayMicroseconds(5);
digitalWrite(SCL_PIN, LOW);
pinMode(SDA_PIN, OUTPUT);
}
// ====== LCD I2C 함수들 ======
void initLCD_I2C() {
delay(50);
lcdCommand_I2C(0x33);
lcdCommand_I2C(0x32);
lcdCommand_I2C(0x28);
lcdCommand_I2C(0x0C);
lcdCommand_I2C(0x06);
lcdClear_I2C();
Serial.println("LCD I2C 초기화 완료");
}
void lcdCommand_I2C(uint8_t cmd) {
i2c_start();
i2c_write_byte(LCD_ADDR << 1);
i2c_write_byte(0x00 | (cmd & 0xF0) | 0x04);
i2c_write_byte(0x00 | (cmd & 0xF0));
i2c_write_byte(0x00 | ((cmd << 4) & 0xF0) | 0x04);
i2c_write_byte(0x00 | ((cmd << 4) & 0xF0));
i2c_stop();
delayMicroseconds(100);
}
void lcdData_I2C(uint8_t data) {
i2c_start();
i2c_write_byte(LCD_ADDR << 1);
i2c_write_byte(0x01 | (data & 0xF0) | 0x04);
i2c_write_byte(0x01 | (data & 0xF0));
i2c_write_byte(0x01 | ((data << 4) & 0xF0) | 0x04);
i2c_write_byte(0x01 | ((data << 4) & 0xF0));
i2c_stop();
delayMicroseconds(100);
}
void lcdClear_I2C() {
lcdCommand_I2C(0x01);
delay(2);
}
void lcdSetCursor_I2C(int col, int row) {
int row_offsets[] = {0x00, 0x40};
lcdCommand_I2C(0x80 | (col + row_offsets[row]));
}
void lcdPrint_I2C(int col, int row, String text) {
lcdSetCursor_I2C(col, row);
for (int i = 0; i < text.length() && i < 16; i++) {
lcdData_I2C(text.charAt(i));
}
}
// ====== MPU6050 함수들 ======
void initMPU6050() {
i2c_start();
i2c_write_byte(MPU6050_ADDR << 1);
i2c_write_byte(0x6B);
i2c_write_byte(0x00);
i2c_stop();
delay(10);
Serial.println("MPU6050 초기화 완료");
}
void readMPU6050(float &ax, float &ay, float &az) {
ax = (random(-1000, 1000) / 1000.0);
ay = (random(-1000, 1000) / 1000.0);
az = 1.0 + (random(-100, 100) / 1000.0);
}
// ====== 센서 읽기 함수들 ======
void readSensors() {
int ntcValue = analogRead(NTC_SENSOR);
// 수정된 온도 매핑 (더 정확함)
temperature = map(ntcValue, 0, 4095, -10, 80); // 선형 매핑
// 추가 보정 (Wokwi NTC 센서 특성에 맞춤)
if (temperature > 50) {
temperature = temperature * 0.8; // 고온 보정
}
Serial.print("NTC Raw: "); Serial.print(ntcValue);
Serial.print(" -> Temp: "); Serial.println(temperature);
// 나머지 센서 코드는 동일...
humidity = 40 + sin(millis() / 10000.0) * 15 + random(-5, 5);
humidity = constrain(humidity, 20, 80);
float ax, ay, az;
readMPU6050(ax, ay, az);
tiltX = atan2(ay, az) * 180 / PI;
tiltY = atan2(ax, az) * 180 / PI;
digitalWrite(HC_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(HC_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(HC_TRIG, LOW);
long duration = pulseIn(HC_ECHO, HIGH, 30000);
if (duration > 0 && duration < 25000) {
distance = duration * 0.034 / 2;
} else {
distance = 999;
}
int windValue = analogRead(WIND_SENSOR);
windSpeed = map(windValue, 0, 4095, 0, 300) / 10.0;
windSpeed = constrain(windSpeed, 0, 30);
fireDetected = !digitalRead(FIRE_BUTTON);
}
void calculateHeatIndex() {
if (temperature >= 27 && humidity >= 40) {
float T = temperature;
float H = humidity;
heatIndex = T + 0.5 * ((H/100.0) * (T - 26.7));
if (windSpeed > 3) {
heatIndex -= (windSpeed - 3) * 0.4;
}
} else {
heatIndex = temperature;
}
heatIndex = constrain(heatIndex, temperature - 5, temperature + 15);
Serial.print("실제온도: "); Serial.print(temperature);
Serial.print("C, 체감온도: "); Serial.print(heatIndex);
Serial.println("C");
}
void evaluateSafety() {
float riskScore = 0;
if (heatIndex >= 38) {
riskScore += 0.4;
} else if (heatIndex >= 35) {
riskScore += 0.3;
} else if (heatIndex >= 32) {
riskScore += 0.2;
} else if (heatIndex >= 28) {
riskScore += 0.1;
}
if (temperature <= 0) {
riskScore += 0.3;
} else if (temperature <= 5) {
riskScore += 0.2;
} else if (temperature <= 10) {
riskScore += 0.1;
}
if (windSpeed >= 20) {
riskScore += 0.3;
} else if (windSpeed >= 15) {
riskScore += 0.2;
} else if (windSpeed >= 10) {
riskScore += 0.1;
}
if (fireDetected) {
riskScore += 0.3;
}
if (riskScore >= 0.8) safetyLevel = 3;
else if (riskScore >= 0.6) safetyLevel = 2;
else if (riskScore >= 0.3) safetyLevel = 1;
else safetyLevel = 0;
Serial.print("위험점수: "); Serial.print(riskScore);
Serial.print(", 안전레벨: "); Serial.println(safetyLevel);
}
void updateDisplay() {
lcdClear_I2C();
String line1 = "T:" + String(temperature, 1) + "C HI:" + String((int)heatIndex);
lcdPrint_I2C(0, 0, line1);
String status;
switch(safetyLevel) {
case 0: status = "SAFE "; break;
case 1: status = "WARN "; break;
case 2: status = "ALERT "; break;
case 3: status = "DANGER "; break;
}
String line2 = status + "W:" + String(windSpeed, 1) + "m/s";
lcdPrint_I2C(0, 1, line2);
}
void handleAlerts() {
unsigned long currentTime = millis();
switch(safetyLevel) {
case 0:
setRGB(0, 255, 0);
noTone(BUZZER_PIN);
break;
case 1:
setRGB(255, 255, 0);
if (currentTime % 2000 < 100) tone(BUZZER_PIN, 1000, 100);
break;
case 2:
setRGB(255, 128, 0);
if (currentTime % 1000 < 200) tone(BUZZER_PIN, 1500, 200);
break;
case 3:
setRGB(255, 0, 0);
if (currentTime % 500 < 250) tone(BUZZER_PIN, 2000, 250);
break;
}
bool workStopCondition = (heatIndex >= 38) ||
(heatIndex >= 35 && windSpeed < 3) ||
fireDetected;
if (workStopCondition) {
if (currentTime % 200 < 100) {
setRGB(255, 0, 0);
tone(BUZZER_PIN, 3000, 100);
} else {
setRGB(100, 0, 0);
}
}
}
void setRGB(int red, int green, int blue) {
analogWrite(RGB_RED, red);
analogWrite(RGB_GREEN, green);
analogWrite(RGB_BLUE, blue);
}
void printDebugInfo() {
Serial.println("=== I2C 스마트 안전관리 시스템 상태 ===");
Serial.print("실제온도: "); Serial.print(temperature, 1); Serial.println("°C");
Serial.print("습도: "); Serial.print(humidity, 1); Serial.println("%");
Serial.print("체감온도: "); Serial.print(heatIndex, 1); Serial.println("°C");
Serial.print("풍속: "); Serial.print(windSpeed, 1); Serial.println(" m/s");
Serial.print("기울기 X: "); Serial.print(tiltX, 1); Serial.println("°");
Serial.print("기울기 Y: "); Serial.print(tiltY, 1); Serial.println("°");
Serial.print("거리: ");
if (distance < 999) {
Serial.print(distance, 1); Serial.println(" cm");
} else {
Serial.println("측정불가");
}
Serial.print("화재감지: "); Serial.println(fireDetected ? "감지됨!" : "정상");
Serial.print("안전레벨: ");
switch(safetyLevel) {
case 0: Serial.print("안전 (초록)"); break;
case 1: Serial.print("주의 (노랑)"); break;
case 2: Serial.print("경고 (주황)"); break;
case 3: Serial.print("위험 (빨강)"); break;
}
Serial.println();
if (heatIndex >= 38) {
Serial.println("*** 체감온도 38도 이상 - 즉시 작업 중지! ***");
} else if (heatIndex >= 35 && windSpeed < 3) {
Serial.println("*** 체감온도 35도 이상 + 무풍 - 14-17시 작업 중지 권고 ***");
}
if (fireDetected) {
Serial.println("*** 화재 감지됨 - 즉시 대피하세요! ***");
}
Serial.println("=====================================");
}