#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// OLED pins
#define OLED_SDA 8
#define OLED_SCL 9

// Stopwatch pins
#define START_STOP_PIN 1
#define LAP_PIN 2
#define RESET_PIN 5

// Speedometer pin
#define PROXIMITY_PIN 6

// Create display object
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// SEMAR Bitmap
static const unsigned char PROGMEM image_data_Saraarray[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xf0, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x03, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0xff, 0xff, 0x00, 0x1f, 0xfe, 0x3f, 0xf8, 0x3f, 0xff, 0xc1, 0xff, 0xc1, 0xff, 0x80, 
0x00, 0x07, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0x7f, 0xfc, 0x7f, 0xff, 0xe3, 0xff, 0xe7, 0xff, 0xc0, 
0x00, 0x1f, 0xff, 0xc0, 0x00, 0x7f, 0xfe, 0xff, 0xfc, 0xff, 0xff, 0xe1, 0xff, 0xef, 0xff, 0x80, 
0x00, 0x3f, 0xfe, 0x00, 0x00, 0x78, 0x00, 0xf0, 0x39, 0xe1, 0xe0, 0xe0, 0x03, 0xcf, 0x00, 0x00, 
0x00, 0x7f, 0xf8, 0x00, 0x00, 0xf0, 0x01, 0xe0, 0x7b, 0xc1, 0xc1, 0xe0, 0x03, 0x9e, 0x00, 0x00, 
0x01, 0xff, 0xe0, 0x00, 0x00, 0xff, 0xe1, 0xff, 0xf3, 0x83, 0x83, 0xcf, 0xff, 0xbc, 0x00, 0x00, 
0x03, 0xff, 0x80, 0x00, 0x00, 0xff, 0xf3, 0xff, 0xe7, 0x87, 0x83, 0x9f, 0xff, 0x38, 0x00, 0x00, 
0x07, 0xfe, 0x00, 0x00, 0x00, 0x7f, 0xf7, 0xff, 0xcf, 0x07, 0x07, 0xbf, 0xfe, 0x78, 0x00, 0x00, 
0x0f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x0e, 0x0e, 0x0f, 0x78, 0x1e, 0xf0, 0x00, 0x00, 
0x0f, 0xe0, 0x00, 0x00, 0x00, 0x01, 0xef, 0x00, 0x1e, 0x1e, 0x0e, 0x70, 0x3c, 0xe0, 0x00, 0x00, 
0x1f, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xcf, 0xff, 0x3c, 0x1c, 0x1e, 0xff, 0xf9, 0xe0, 0x00, 0x00, 
0x1f, 0xf8, 0x00, 0x00, 0x0f, 0xff, 0x8f, 0xff, 0xb8, 0x38, 0x1c, 0xff, 0xf9, 0xc0, 0x00, 0x00, 
0x1f, 0xff, 0x80, 0x00, 0x07, 0xff, 0x07, 0xff, 0x10, 0x18, 0x08, 0x7f, 0xe0, 0x80, 0x00, 0x00, 
0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x3f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40, 0x08, 0x00, 0x00, 0x80, 
0x00, 0x01, 0xff, 0xff, 0xff, 0x80, 0x00, 0x02, 0xec, 0xdd, 0x02, 0x40, 0x54, 0x6a, 0xe5, 0x58, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfb, 0x4c, 0xc7, 0x29, 0xf4, 0xea, 0x6f, 0x78, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x00, 0x42, 0x09, 0x00, 0x00, 0x80, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Stopwatch variables
bool running = false;
unsigned long startTime = 0;
unsigned long elapsedTime = 0;
unsigned long lapTime = 0;
int lapCount = 0;

// Debounce variables
const unsigned long debounceDelay = 1000; // 1000ms debounce delay
unsigned long lastDebounceTime[] = {0, 0, 0}; // For start/stop, lap, and reset buttons

// Speedometer variables
const float wheelCircumference = 0.785; // Wheel circumference in meters
volatile unsigned long lastInterruptTime = 0; // Last interrupt time
volatile float speed = 0.0; // Speed in km/h

void IRAM_ATTR startStop() {
  unsigned long currentMillis = millis();
  if (currentMillis - lastDebounceTime[0] > debounceDelay) {
    running = !running;
    if (running) {
      startTime = millis() - elapsedTime;
    } else {
      elapsedTime = millis() - startTime;
    }
    lastDebounceTime[0] = currentMillis;
  }
}

void IRAM_ATTR lap() {
  unsigned long currentMillis = millis();
  if (currentMillis - lastDebounceTime[1] > debounceDelay) {
    if (running) {
      lapTime = millis() - startTime;
      lapCount++;
    }
    lastDebounceTime[1] = currentMillis;
  }
}

void IRAM_ATTR reset() {
  unsigned long currentMillis = millis();
  if (currentMillis - lastDebounceTime[2] > debounceDelay) {
    running = false;
    elapsedTime = 0;
    lapTime = 0;
    startTime = millis();
    lapCount = 0;
    lastDebounceTime[2] = currentMillis;
  }
}

void IRAM_ATTR updateSpeed() {
  unsigned long currentTime = millis();
  unsigned long timeDifference = currentTime - lastInterruptTime;

  lastInterruptTime = currentTime;

  // Calculate speed in km/h
  if (timeDifference > 0) {
    speed = (wheelCircumference / (timeDifference / 1000.0)) * 3.6;
  }
}

void resetSpeedIfNeeded() {
  unsigned long currentTime = millis();
  if (currentTime - lastInterruptTime > 3000) { // If no interrupt for 3 seconds
    noInterrupts();
    speed = 0.0;
    interrupts();
  }
}

void setup() {
  Serial.begin(115200);

  // Initialize OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  display.clearDisplay();

  // Initialize stopwatch pins
  pinMode(START_STOP_PIN, INPUT_PULLUP);
  pinMode(LAP_PIN, INPUT_PULLUP);
  pinMode(RESET_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(START_STOP_PIN), startStop, RISING);
  attachInterrupt(digitalPinToInterrupt(LAP_PIN), lap, RISING);
  attachInterrupt(digitalPinToInterrupt(RESET_PIN), reset, RISING);

  // Initialize speedometer pin
  pinMode(PROXIMITY_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PROXIMITY_PIN), updateSpeed, FALLING);



  // Welcome message
  // Text under image
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(20, 47);
  
  // Display static text
  display.println("speedo.by proto");
  
  // Draw bitmap on the screen
  display.drawBitmap(0, 8, image_data_Saraarray, 128, 37, 1);

  display.display();
  delay(2000);
  display.clearDisplay();
}

void loop() {
  resetSpeedIfNeeded();

  // Stopwatch logic
  unsigned long displayTime;
  if (running) {
    displayTime = millis() - startTime;
  } else {
    displayTime = elapsedTime;
  }

  int seconds = (displayTime / 1000) % 60;
  int minutes = (displayTime / 60000) % 60;
  int milliseconds = (displayTime % 1000) / 10;

  // Display logic
  display.clearDisplay();

  // Display stopwatch
  display.setTextSize(2);
  display.setCursor(16, 2);
  display.printf("%02d:%02d:%02d", minutes, seconds, milliseconds);

  if (lapTime >= 0) {
    int lapSeconds = (lapTime / 1000) % 60;
    int lapMinutes = (lapTime / 60000) % 60;
    int lapMilliseconds = (lapTime % 1000) / 10;
    display.setTextSize(2);
    display.setCursor(16, 18);
    display.printf("L%d:%02d:%02d", lapCount, lapMinutes, lapSeconds);
  }

  // Display speed
  display.setTextSize(4);
  display.setCursor(17, 34);
  display.printf("%.2f", speed);

  display.display();
  delay(50);
} 
$abcdeabcde151015202530fghijfghij
$abcdeabcde151015202530fghijfghij
Loading
esp32-s3-devkitc-1
NOCOMNCVCCGNDINLED1PWRRelay Module