Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:prj2025:cmoarcas:adrian.ariton [2025/05/13 11:30]
adrian.ariton
pm:prj2025:cmoarcas:adrian.ariton [2025/05/25 22:21] (current)
adrian.ariton
Line 2: Line 2:
 ===== Introducere ===== ===== Introducere =====
  
 +<​html><​iframe width="​560"​ height="​315"​ src="​https://​www.youtube.com/​embed/​TrdBkxlq2JE?​si=ej5ocPEVzuya_28Z"​ title="​YouTube video player"​ frameborder="​0"​ allow="​accelerometer;​ autoplay; clipboard-write;​ encrypted-media;​ gyroscope; picture-in-picture;​ web-share"​ referrerpolicy="​strict-origin-when-cross-origin"​ allowfullscreen></​iframe></​html>​
 +
 +https://​github.com/​adrianariton/​PMProj
 +
 +Updated project:
 +https://​youtube.com/​shorts/​TrdBkxlq2JE?​feature=share
 +
 +Old version: ​
 +https://​youtube.com/​shorts/​uZILq4yrm3E?​feature=share
  
 Proiectul **"​Bomb Defusal Challenge"​** este un joc fizic interactiv inspirat de "Keep Talking and Nobody Explodes"​. ​ Proiectul **"​Bomb Defusal Challenge"​** este un joc fizic interactiv inspirat de "Keep Talking and Nobody Explodes"​. ​
Line 94: Line 103:
  
  
-===== Schema Logica Hardware ​=====+==== Schema Logica Hardware ====
  
 {{pm:​prj2025:​cmoarcas:​adi_schema.png?​500 | Schema HW}} {{pm:​prj2025:​cmoarcas:​adi_schema.png?​500 | Schema HW}}
  
-===== PCB =====+==== PCB ====
  
 {{pm:​prj2025:​cmoarcas:​pcbadi.png?​500 | PCB}} {{pm:​prj2025:​cmoarcas:​pcbadi.png?​500 | PCB}}
Line 105: Line 114:
  
 - U1: BMP190 (Temp + Presiune) I2C - U1: BMP190 (Temp + Presiune) I2C
 +
 - U3: MPU6500 (Giroscop) I2C - U3: MPU6500 (Giroscop) I2C
  
Line 117: Line 127:
   * (etapa 3) surse şi funcţii implementate   * (etapa 3) surse şi funcţii implementate
 </​note>​ </​note>​
 +
 +=== Descriere ===
 +
 +Deignuit pe Platformio: VSCode.
 +
 +Biblioteci:
 +
 +* driver/​dac.h - pt sunet
 +
 +* TFT_eSPI.h - pt display
 +
 +* Arduino.h - pt ft putine lucruri (setup)
 +
 +=== Code Snippets ===
 +
 +Timer loop:
 +
 +Timer loopul se triggeruieste la fiecare tick, si mentine starea jocului prin urmatorul loop:
 +
 +  void IRAM_ATTR onTimer() {
 +    portENTER_CRITICAL_ISR(&​timerMux);​
 +    myMillis++;
 +    if (myMillis % 200 == 0) {
 +      if (is_active(GAME_SIMON_SAYS)) {
 +        if (simon_can_do_next_round){
 +          simon_last_round_begin_milis = myMillis;
 +          simon_can_do_next_round = false;
 +          simon_round_ongoing = true;
 +        } 
 +        int deltaMillisTilStart = (myMillis - simon_last_round_begin_milis);​
 +        if ((myMillis - simon_last_round_begin_milis) <= simon_round_intro_time_delay_milis) {
 +          // round pending
 +          if (deltaMillisTilStart % 1000 == 0)
 +            simonOnRoundPrepareTick((simon_round_intro_time_delay_milis - deltaMillisTilStart) / 1000);
 +        } else {
 +          if (!simon_semaphore_started){
 +            simonOnRoundStart();​
 +            simon_semaphore_started = true;
 +          }
 +          int time_since_round_semaphore_start = deltaMillisTilStart - simon_round_intro_time_delay_milis;​
 +          int tick_cnt = time_since_round_semaphore_start / simon_time_delay_between_colors;​
 +          if (time_since_round_semaphore_start % simon_time_delay_between_colors == 0) {
 +            if (tick_cnt <= simon_size_of_colors)
 +              simonOnRoundStartTick(tick_cnt);​
 +            else {
 +              if (!simon_can_begin_pressing){
 +                simonOnGameEnd();​
 +              }
 +              simon_can_begin_pressing = true;
 +            }
 +          }
 +        }
 +      } else if (is_active(GAME_WIRE_CUTTING)) {
 +        if (!wire_cut_game_started){
 +          wireCutOnStart();​
 +          wire_cut_game_started = true;
 +          wire_cut_game_start_time = myMillis;
 +        }
 +        if (wire_cut_game_started) {
 +          int deltaMilisSinceWireCutStart = (myMillis - wire_cut_game_start_time);​
 +          if (deltaMilisSinceWireCutStart % 1000 == 0) {
 +            int ticks = deltaMilisSinceWireCutStart / 1000;
 +            if (ticks < wire_cut_game_duration_seconds)
 +              wireCutOnGameTick(wire_cut_game_duration_seconds - ticks);
 +            else if (!wire_cut_game_ended) {
 +              wire_cut_game_ended = true;
 +              wire_cut_game_won = false;
 +            }
 +          }
 +        }
 +      } else if (is_active(GAME_PASS_UNLOCK)) {
 +        if (!pass_game_started) {
 +          pass_game_started = true;
 +          pass_game_start_time = myMillis;
 +          passUnlockOnStart();​
 +        }
 +        if (pass_game_started) {
 +          int dm = (myMillis - pass_game_start_time);​
 +          if (dm % 1000 == 0) {
 +            int ticks = dm / 1000;
 +            if (ticks < pass_game_time_limit_seconds)
 +              passUnlockOnGameTick(pass_game_time_limit_seconds - ticks);
 +            else if (!pass_game_ended) {
 +              pass_game_ended = true;
 +                pass_game_won = false;
 +              }
 +            }
 +          }
 +        }
 +    }
 +    portEXIT_CRITICAL_ISR(&​timerMux);​
 +  }
 +
 +
 +
 +* Fiecare callback este implemetat:
 +
 +- Simon Games callbacks defns & vars:
 +
 +  int simon_size_of_colors = 3;
 +  char simonBufferSemaphoreSize = 0;
 +  char simonBufferCompareIdx = 0;
 +  void simonOnRoundPrepareTick(int tick_count);​
 +  void simonOnRoundStart();​
 +  void simonOnRoundStartTick(int tick_count);​
 +  void simonOnGameEnd();​
 +
 +- Wire Cut callbacks defns & vars:
 +
 +  volatile int wire_cut_game_duration_seconds = 20;
 +  volatile int wire_cut_game_target_wire = -1;
 +  void wireCutOnStart();​
 +  void wireCutOnGameTick(int ticks_left);​
 +  void wireCutOnEnd(bool won);
 +  ​
 +- Finger scan callbacks defns & vars:
 +
 +
 +  int pass_env_temperatures_read_interv_millis = 1000; // pt calibrarea temperaturii de mediu
 +  const int pass_env_temperatures_read_limit = 10;
 +  const float pass_temp_threshold_celsius = 0.5;
 +  volatile int pass_game_time_limit_seconds = 10;
 +  void passUnlockOnStart();​
 +  void passUnlockOnGameTick(int ticks_left);​
 +  void passUnlockOnEnd(bool won);
 +
 +! Singura folosire a delay-ului implementat nu de mine (custom) e in functia de playNote care ajuta la audio.
 +
 +  const int dacPin = 25;    // DAC1 - GPIO25
 +  const int frequency = 1000;  // tone frequency Hz
 +  const int duration = 100;   // duration in ms
 +
 +  void playTone(int freq, int dur_ticks=1) {
 +    int samplesPerCycle = 100;          // number of samples per waveform cycle
 +    int sampleDelayUs = 1000000 / (freq * samplesPerCycle);​
 +    int amplitude = 64;                // 8-bit DAC mid-range (0-255)
 +    int dur_ms = dur_ticks * duration;
 +    unsigned long endTime = millis() + dur_ms;
 +    while (millis() < endTime) {
 +      for (int i = 0; i < samplesPerCycle;​ i++) {
 +        // Generate a sine wave sample (scaled 0-255)
 +        float sineVal = sin(2 * PI * i / samplesPerCycle);​
 +        uint8_t dacVal = (uint8_t)(amplitude + amplitude * sineVal);
 +        dac_output_voltage(DAC_CHANNEL_1,​ dacVal);
 +        delayMicroseconds(sampleDelayUs);​
 +      } 
 +    }
 +    dac_output_voltage(DAC_CHANNEL_1,​ 128); // center output (silence)
 +  }
 +  ​
 +care nu e inclus in cele 3 laboratoare.
 +
 +I2C ul se face pe pinii 26 si 27, pt ca pinul 25 e folosit pt DAC.
 +Acesta e conectat atat la senzorl giroscopic cat si la cel de temperatura.
 +
 +Senzorii de giroscop si temperatura sunt implementati cu drivere programate de mine dupa datasheets.
 +
 +E.g. (girocop):
 +
 +
 +  // Calculate B5 value from the datasheet
 +  int32_t computeB5(int32_t UT) {
 +    int32_t X1 = (UT - (int32_t)ac6) * ((int32_t)ac5) >> 15;
 +    int32_t X2 = ((int32_t)mc << 11) / (X1 + (int32_t)md);​
 +    return X1 + X2;
 +  }
 +  ​
 +  float readTemperature() {
 +    int32_t UT = readRawTemperature();​
 +    int32_t B5 = computeB5(UT);​
 +    ​
 +    // Temperature in units of 0.1 deg C
 +    float temp = ((B5 + 8) >> 4);
 +    ​
 +    // Convert to degrees C
 +    return temp / 10.0;
 +  }
 +  ​
 +  uint32_t readRawPressure() {
 +    write8(BMP180_CONTROL,​ BMP180_READPRESSURECMD + (oversampling << 6));
 +    ​
 +    // Wait for conversion based on oversampling setting
 +    if (oversampling == BMP180_ULTRALOWPOWER)
 +      delay(5);
 +    else if (oversampling == BMP180_STANDARD)
 +      delay(8);
 +    else if (oversampling == BMP180_HIGHRES)
 +      delay(14);
 +    else
 +      delay(26);
 +    ​
 +    uint32_t MSB = read8(BMP180_PRESSUREDATA);​
 +    uint32_t LSB = read8(BMP180_PRESSUREDATA + 1);
 +    uint32_t XLSB = read8(BMP180_PRESSUREDATA + 2);
 +    ​
 +    // Combine readings with proper shifting based on oversampling
 +    uint32_t raw = ((MSB << 16) + (LSB << 8) + XLSB) >> (8 - oversampling);​
 +    return raw;
 +  }
 +  ​
 +  int32_t readPressure() {
 +    // Read raw temperature value first
 +    int32_t UT = readRawTemperature();​
 +    // Then read raw pressure value
 +    int32_t UP = readRawPressure();​
 +    ​
 +    // Temperature compensation
 +    int32_t B5 = computeB5(UT);​
 +    ​
 +    // Do pressure calculations (straight from Adafruit code)
 +    int32_t B6 = B5 - 4000;
 +    int32_t X1 = ((int32_t)b2 * ((B6 * B6) >> 12)) >> 11;
 +    int32_t X2 = ((int32_t)ac2 * B6) >> 11;
 +    int32_t X3 = X1 + X2;
 +    int32_t B3 = ((((int32_t)ac1 * 4 + X3) << oversampling) + 2) / 4;
 +    ​
 +    X1 = ((int32_t)ac3 * B6) >> 13;
 +    X2 = ((int32_t)b1 * ((B6 * B6) >> 12)) >> 16;
 +    X3 = ((X1 + X2) + 2) >> 2;
 +    uint32_t B4 = ((uint32_t)ac4 * (uint32_t)(X3 + 32768)) >> 15;
 +    uint32_t B7 = ((uint32_t)UP - B3) * (uint32_t)(50000UL >> oversampling);​
 +    ​
 +    int32_t p;
 +    if (B7 < 0x80000000) {
 +      p = (B7 * 2) / B4;
 +    } else {
 +      p = (B7 / B4) * 2;
 +    }
 +    ​
 +    X1 = (p >> 8) * (p >> 8);
 +    X1 = (X1 * 3038) >> 16;
 +    X2 = (-7357 * p) >> 16;
 +    p = p + ((X1 + X2 + (int32_t)3791) >> 4);
 +    ​
 +    return p; // Pressure in Pa
 +  }
 +  ​
 +  // Calculate altitude based on atmospheric pressure
 +  float readAltitude(float seaLevelPressure) {
 +    float pressure = readPressure();​
 +    float altitude = 44330 * (1.0 - pow(pressure / seaLevelPressure,​ 0.1903));
 +    return altitude;
 +  }
 +
 +
 +== Dificultati:​ ==
 +
 +* am setat frecventa la ceasul de transmisie i2c mai jos pt a compensa rezistentele de pullup puse in paralel
 +
 +
  
 ===== Rezultate Obţinute ===== ===== Rezultate Obţinute =====
Line 123: Line 383:
 Care au fost rezultatele obţinute în urma realizării proiectului vostru. Care au fost rezultatele obţinute în urma realizării proiectului vostru.
 </​note>​ </​note>​
 +
 +* Jocuri calibrabile si modificabile
 +
 +* Un workflow nedependent de main loop
  
 ===== Concluzii ===== ===== Concluzii =====
Line 134: Line 398:
 </​note>​ </​note>​
  
 +
 +https://​github.com/​adrianariton/​PMProj
 ===== Jurnal ===== ===== Jurnal =====
  
Line 142: Line 408:
 Laburi folosite: Laburi folosite:
  
-- Timere +- GPIO 
-- Intreruperi + 
-- I2C+- Timere ​(flow-ul jocului) 
 + 
 +- Intreruperi ​(butoane/​fire) 
 + 
 +- I2C (senzorii de temp si de giroscop comunica in tandem pe magistrala SDA SCL conectate la Pinii 25 si 26)
  
 ===== Bibliografie/​Resurse ===== ===== Bibliografie/​Resurse =====
pm/prj2025/cmoarcas/adrian.ariton.1747125006.txt.gz · Last modified: 2025/05/13 11:30 by adrian.ariton
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0