#include #ifdef U8X8_HAVE_HW_SPI #include #endif #ifdef U8X8_HAVE_HW_I2C #include #endif // Initialize OLED display U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // Pin definitions const int MQ3_PIN = A0; const int BUTTON_PIN = D10; const int BUZZER_PIN = D9; const int FAN1_PIN = D7; const int FAN2_PIN = D6; // State variables int state = 1; unsigned long startMillis; unsigned long lastSerialMillis = 0; // Timer for serial updates bool buttonPressed = false; int baseline = 0; // Baseline for MQ3 sensor // Timer constants const unsigned long HEATING_TIME = 30000; // 30 seconds const unsigned long BLOW_TIME = 5000; // 5 seconds const unsigned long PROCESSING_TIME = 5000; const unsigned long PURGE_TIME = 30000; // 30 seconds // Function prototypes void displayOffState(); void displaySpiroSniffWithWaveIcon(); void countdownWithMessage(int seconds); void displayBlowNowPrompt(); void displayPleaseWait(); void displayPurging(); void displayBAC(float bac); void playTone(int pin, int frequency, int duration); void activateFans(); void deactivateFans(); float calculateBAC(); void setup() { // Initialize hardware u8g2.begin(); pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(BUZZER_PIN, OUTPUT); pinMode(FAN1_PIN, OUTPUT); pinMode(FAN2_PIN, OUTPUT); digitalWrite(FAN1_PIN, LOW); digitalWrite(FAN2_PIN, LOW); Serial.begin(9600); // Initialize Serial communication displayOffState(); } void loop() { static bool isButtonPressed = false; // Read MQ3 sensor and display in Serial Monitor every second if (millis() - lastSerialMillis >= 1000) { lastSerialMillis = millis(); int mq3Value = analogRead(MQ3_PIN); float voltage = mq3Value * (3.3 / 4095.0); Serial.print("Analog Reading: "); Serial.print(mq3Value); Serial.print(", Voltage: "); Serial.println(voltage); } // Button handling if (digitalRead(BUTTON_PIN) == LOW && !isButtonPressed) { isButtonPressed = true; if (state == 1) { state = 2; startMillis = millis(); playTone(BUZZER_PIN, 261, 1000); displaySpiroSniffWithWaveIcon(); // Show logo for 5 seconds startMillis = millis(); // Reset the start time for heating } else { state = 1; displayOffState(); } } else if (digitalRead(BUTTON_PIN) == HIGH) { isButtonPressed = false; } switch (state) { case 1: break; case 2: { if (millis() - startMillis < HEATING_TIME) { countdownWithMessage((HEATING_TIME - (millis() - startMillis)) / 1000); baseline = analogRead(MQ3_PIN); // Store the baseline value } else { state = 3; startMillis = millis(); } break; } case 3: if (millis() - startMillis < BLOW_TIME) { displayBlowNowPrompt(); playTone(BUZZER_PIN, 261, 5000); } else { state = 4; startMillis = millis(); } break; case 4: if (millis() - startMillis < PROCESSING_TIME) { displayPleaseWait(); } else { float avgBAC = calculateBAC(); displayBAC(avgBAC); // Updated to accept float state = 5; startMillis = millis(); } break; case 5: if (millis() - startMillis > 20000) { state = 6; startMillis = millis(); activateFans(); } break; case 6: if (millis() - startMillis < PURGE_TIME) { displayPurging(); } else { deactivateFans(); state = 1; displayOffState(); } break; } } // Function definitions void displayOffState() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_10x20_tf); // Slightly larger font int16_t text_width = u8g2.getStrWidth("OFF"); u8g2.drawStr((128 - text_width) / 2, 32, "OFF"); u8g2.sendBuffer(); } void displaySpiroSniffWithWaveIcon() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); const char* text = "SpiroSniff"; int16_t text_width = u8g2.getStrWidth(text); int16_t x = (128 - text_width) / 2; u8g2.drawStr(x, 10, text); int wave_x = (128 - 24) / 2; int wave_y = 28; for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { u8g2.drawPixel(wave_x + j * 6, wave_y + i * 8); u8g2.drawPixel(wave_x + 3 + j * 6, wave_y + 2 + i * 8); u8g2.drawPixel(wave_x + 6 + j * 6, wave_y + i * 8); } } u8g2.sendBuffer(); delay(5000); // Hold for 5 seconds } void countdownWithMessage(int seconds) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_fub20_tf); char buffer[3]; sprintf(buffer, "%d", seconds); int16_t countdown_width = u8g2.getStrWidth(buffer); u8g2.drawStr((128 - countdown_width) / 2, 20, buffer); u8g2.setFont(u8g2_font_6x10_tf); u8g2.drawStr(15, 55, "Heating - Wait"); u8g2.sendBuffer(); } void displayBlowNowPrompt() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_fub20_tf); int16_t dash_width = u8g2.getStrWidth("- -"); u8g2.drawStr((128 - dash_width) / 2, 20, "- -"); u8g2.setFont(u8g2_font_6x10_tf); int16_t blow_now_width = u8g2.getStrWidth("Blow Now"); u8g2.drawStr((128 - blow_now_width) / 2, 55, "Blow Now"); u8g2.sendBuffer(); } void displayPleaseWait() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x10_tf); u8g2.drawStr(30, 32, "Please Wait..."); u8g2.sendBuffer(); } void displayPurging() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x10_tf); u8g2.drawStr(40, 32, "Purging..."); u8g2.sendBuffer(); } void displayBAC(float bac) { // Updated to accept float u8g2.clearBuffer(); u8g2.setFont(u8g2_font_10x20_tf); // Slightly larger font u8g2.drawStr(20, 30, "Your BAC is:"); u8g2.setCursor(20, 50); u8g2.print(bac, 2); // Display BAC with 2 decimal places u8g2.sendBuffer(); } void playTone(int pin, int frequency, int duration) { tone(pin, frequency, duration); delay(duration); noTone(pin); } void activateFans() { digitalWrite(FAN1_PIN, HIGH); digitalWrite(FAN2_PIN, HIGH); } void deactivateFans() { digitalWrite(FAN1_PIN, LOW); digitalWrite(FAN2_PIN, LOW); } float calculateBAC() { unsigned long startTime = millis(); int sum = 0; int count = 0; while (millis() - startTime < 3000) { int mq3Value = analogRead(MQ3_PIN); sum += mq3Value; count++; delay(100); // Read every 100ms } int mq3 = sum / count; // Calculate the average MQ3 sensor reading // Calculate BAC using the provided formula float BAC = 0.0000187 * (baseline - mq3); // Round BAC to two decimal places BAC = round(BAC * 100) / 100.0; return BAC; // Return the rounded BAC }