본문 바로가기

ESP32 Project

esp32-c6 가속도 센서 ADXL345 Interrupt 처리

반응형

아 쓰기 싫다.

 

중요한 부분은 ADXL345 INT1 핀을 esp32-c6 보드의 4번에 연결했다는 사실과 코드에서 가속도 센서 인터럽트 설정하는 과정에 주의하면 무리없이 잘 된다.

 

 

 

 

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

#include "esp_sleep.h" // 딥슬립 관련 헤더

// --- 설정 및 상수 ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define BUZZER_PIN 1
#define ACCEL_INT_PIN 4  // ADXL345의 INT1 연결 핀
//#define TOTAL_TIME_MS 180000 // 3분
//#define INACTIVITY_LIMIT 10000 // 종료 후 10초 뒤 슬립 모드

#define TOTAL_TIME_MS 20000 // 20초
#define INACTIVITY_LIMIT 5000 // 5초 후 슬립 모드

// ESP32-C6 I2C 핀
#define I2C_SDA 6
#define I2C_SCL 7

// --- 객체 생성 ---
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);

// --- 전역 상태 변수 ---
static bool grid[SCREEN_WIDTH][SCREEN_HEIGHT];
unsigned long startTime;
int totalSandCount = 0;
bool isFinished = false;
enum Direction { DIR_DOWN, DIR_UP, DIR_LEFT, DIR_RIGHT };
Direction gravityDir = DIR_DOWN;

// --- 함수 프로토타입 ---
void initHardware();
void setupADXL345Interrupt();
void updatePhysics();
void drawSand();
void playAlarm();
void goDeepSleep();

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA, I2C_SCL);
  Serial.println("I2C 시작!");

  initHardware();
  
  // 딥슬립에서 깨어난 원인 확인
  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) {
    Serial.println("Movement detected! Starting Timer...");
  }

  startTime = millis();
  memset(grid, 0, sizeof(grid));

  Serial.println("setup end!");

}

void loop() {
  unsigned long elapsed = millis() - startTime;

  // 1. 센서로부터 중력 방향 읽기
  sensors_event_t event;
  accel.getEvent(&event);
  if (abs(event.acceleration.x) > abs(event.acceleration.y)) {
    gravityDir = (event.acceleration.x > 3.0) ? DIR_LEFT : DIR_RIGHT;
  } else {
    gravityDir = (event.acceleration.y > 3.0) ? DIR_UP : DIR_DOWN;
  }

  // 2. 타이머 및 모래 생성 로직
  if (!isFinished) {
    if (elapsed < TOTAL_TIME_MS) {
      int targetSand = (elapsed * (long)(SCREEN_WIDTH * SCREEN_HEIGHT / 4)) / TOTAL_TIME_MS; // 1/4 정도만 채워도 가득 차 보임
      while (totalSandCount < targetSand) {
        int rx = random(SCREEN_WIDTH);
        if (!grid[rx][0]) { grid[rx][0] = true; totalSandCount++; }
      }
    } else {
      isFinished = true;
      playAlarm();
    }
  }

  // 3. 물리 시뮬레이션 및 그리기
  updatePhysics();
  drawSand();

  // 4. 슬립 모드 진입 조건 (종료 후 10초간 반응 없으면)
  if (isFinished && (millis() - (startTime + TOTAL_TIME_MS) > INACTIVITY_LIMIT)) {
    goDeepSleep();
  }
}

// --- 모듈화된 함수들 ---

void initHardware() {
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) while(1);
  if(!accel.begin()) while(1);
  accel.setRange(ADXL345_RANGE_2_G);
  setupADXL345Interrupt_1();
  
  pinMode(BUZZER_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);
  display.clearDisplay();
}

void setupADXL345Interrupt_1() {
  // 1. 센서를 대기 모드로 전환 (설정 변경을 위해)
  accel.writeRegister(ADXL345_REG_POWER_CTL, 0x00); 

  // 2. 활동 감지 감도 설정 (0~255)
  // 62.5mg/LSB 이므로, 10~15 정도로 낮춰서 아주 작은 움직임에도 깨어나게 해봅니다.
  accel.writeRegister(ADXL345_REG_THRESH_ACT, 20); 

  // 3. 활동 감지 축 설정 (AC 결합 모드 권장)
  // AC 모드(bit 7)를 켜면 중력 가속도를 무시하고 '변화'만 감지하므로 벽걸이 장치에 유리합니다.
  // 0xFF: AC coupled, 모든 축(X, Y, Z) 활성화
  accel.writeRegister(ADXL345_REG_ACT_INACT_CTL, 0xFF); 

  // 4. 인터럽트 핀 맵 설정
  // Activity 인터럽트(bit 4)를 0으로 설정하면 INT1 핀으로 출력됩니다.
  accel.writeRegister(ADXL345_REG_INT_MAP, 0x00); 

  // 5. 인터럽트 활성화
  // Activity 인터럽트 활성화 (0x10)
  accel.writeRegister(ADXL345_REG_INT_ENABLE, 0x10); 

  // 6. 중요: 센서를 다시 측정 모드(Measure Mode)로 전환
  accel.writeRegister(ADXL345_REG_POWER_CTL, 0x08); 

  // 7. 잔여 인터럽트 클리어 (초기화 시 한 번 읽어줌)
  accel.readRegister(ADXL345_REG_INT_SOURCE);
}


void updatePhysics() {
  // 간단하지만 정교한 모래 낙하 물리 (DOWN 방향 기준 예시)
  for (int y = SCREEN_HEIGHT - 2; y >= 0; y--) {
    for (int x = 0; x < SCREEN_WIDTH; x++) {
      if (grid[x][y]) {
        if (!grid[x][y+1] && y+1 < SCREEN_HEIGHT) {
          grid[x][y+1] = true; grid[x][y] = false;
        } else if (x > 0 && !grid[x-1][y+1] && y+1 < SCREEN_HEIGHT) {
          grid[x-1][y+1] = true; grid[x][y] = false;
        } else if (x < SCREEN_WIDTH-1 && !grid[x+1][y+1] && y+1 < SCREEN_HEIGHT) {
          grid[x+1][y+1] = true; grid[x][y] = false;
        }
      }
    }
  }
}

void drawSand() {
  display.clearDisplay();
  for (int x = 0; x < SCREEN_WIDTH; x++) {
    for (int y = 0; y < SCREEN_HEIGHT; y++) {
      if (grid[x][y]) display.drawPixel(x, y, SSD1306_WHITE);
    }
  }
  if (isFinished) {
    display.setCursor(20, 25);
    display.setTextSize(2);
    display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // 반전 텍스트
    display.print("  DONE!  ");
  }
  display.display();
}

void playAlarm() {
  for(int i=0; i<3; i++) {
    digitalWrite(BUZZER_PIN, HIGH); delay(200);
    digitalWrite(BUZZER_PIN, LOW); delay(200);
  }
}

void goDeepSleep() {
  display.clearDisplay();
  display.display();

  // 슬립 들어가기 전 인터럽트 상태를 읽어서 클리어합니다. (매우 중요)
  accel.readRegister(ADXL345_REG_INT_SOURCE);
  //pinMode(4, INPUT_PULLDOWN); // 센서가 신호를 주지 않을 때 LOW를 유지하도록 함

  Serial.println("Entering Deep Sleep...");
  
  // GPIO 10번 핀이 HIGH가 되면 깨어나도록 설정 (ADXL345 인터럽트)
  esp_deep_sleep_enable_gpio_wakeup(1 << ACCEL_INT_PIN, ESP_GPIO_WAKEUP_GPIO_HIGH);  
  esp_deep_sleep_start();
}

 

 

 

별 도움도 되는 참고문서 - AN-1077: ADXL345 빠른 시작 가이드 

GY-291 ADXL345 i2c SPI 가속도 센서 

 

 

 

반응형

캐어랩 고객 지원

취업, 창업의 막막함, 외주 관리, 제품 부재!

당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약, 아이디어는 있지만 구현할 기술이 없는 막막함.

우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.

이제 고민을 멈추고, 캐어랩을 만나세요!

코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.

제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!

귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.

지난 30년 여정, 캐어랩이 얻은 모든 것을 함께 나누고 싶습니다.

카카오 채널 추가하기

카톡 채팅방에서 무엇이든 물어보세요

당신의 성공을 위해 캐어랩과 함께 하세요.

캐어랩 온라인 채널 바로가기

캐어랩