MPU6050 센서로 좌회전, 우회전 하기 (with ESP32)

GAON PARK·2023년 11월 23일
0
post-custom-banner

MPU6050 간단 소개

분류설명
모듈 보드명GY-521
IC 이름MPU6050

하나의 센서에 두 가지 센싱이 가능하다.

  1. Gyroscope : 3축 측정
  2. Accelerometer : 3축 측정

Gyroscope (자이로스코프)

각의 속도를 알 수 있다.
ex) 초당 각도의 변화량 측정

Accelerometer (가속도)

기울기를 감지할 때 사용한다.
ex) 핸들 돌렸을 때 인지

회로 연결

mpu6050esp32 pin
VCC3.3V
GNDGND
SCLI2C SCL (GPIO22)
SDAI2C SDA (GPIO21)

Arduino 라이브러리

EspMQTTClient : mqtt 용 라이브러리

Adafruite MPU6050 : MPU6050 라이브러리

앞으로 기능을 구현하면서 또 다른 센서에서 값을 지속적으로 읽어와야 할 수 있기 때문에, thread로 만들기 위해 ArduinoThread 도 받아준다.

ESP32 코드

mpu class

  • sensors_vec_t 구조체에 담긴 값을 읽어 왼쪽으로 틀어져있는지, 오른쪽으로 틀어져있는지 확인한다.
  • 상수값은 범위를 나누어 다섯가지 의미를 나타내도록 했다.
    - 왼쪽으로 살짝 꺾기
    - 왼쪽으로 많이 꺾기
    - 오른쪽으로 살짝 꺾기
    - 오른쪽으로 많이 꺾기
    - 중간 상태 유지하기
  • Thread를 상속받아서 run()을 구현했다. 실제 스레드가 시작될 때 수행되는 구간이다. Thread 코드 내부를 보니 default는 0인 듯했다.
    - Thread(void (*callback)(void) = NULL, unsigned long _interval = 0);
  • COMMAND_XXX는 값이 바뀌지 않는 한 mqtt 송신을 한 번만 보내도록 하기 위해 만든 bool 변수로 volatile 키워드를 추가해 과하게 똑똑한 컴파일러의 방해(!)를 차단했다.
  • command_direction_all_false() 를 통해 함수 하나를 호출하면서 상태 bool 값을 모두 false로 만들고, 입력된 커맨드에 따라 하나씩 수정하는 식으로 재사용했다.
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include "Thread.h"
#include "ThreadController.h"
#include <Arduino.h>

// 한 번만 보내기 위한 flg 변수
volatile bool COMMAND_LEFT_MIN = false,
              COMMAND_LEFT_MAX = false,
              COMMAND_RIGHT_MIN = false,
              COMMAND_RIGHT_MAX = false,
              COMMAND_MID = false;

void command_direction_all_false(void) {
  left_to_false();
  right_to_false();
  COMMAND_MID = false;
}

class MPUThread : public Thread {
public:
  sensors_event_t a, g, temp;
  void cmd_mpu_check(sensors_vec_t ac) {
    if (ac.y > 3) {
      if (ac.y > 8 && !COMMAND_LEFT_MAX) {
        // Serial.println("left_max");
        tx(CMD_TOPIC, "left_max");
        command_direction_all_false();
        COMMAND_LEFT_MAX = true;
      }
      else if (ac.y <= 6 && !COMMAND_LEFT_MIN) {
        // Serial.println("left_min");
        tx(CMD_TOPIC, "left_min");
        command_direction_all_false();
        COMMAND_LEFT_MIN = true;
      }
    }
    else if (ac.y < -3) {
      if (ac.y < -8 && !COMMAND_RIGHT_MAX) {
        // Serial.println("right_max");
        tx(CMD_TOPIC, "right_max");
        command_direction_all_false();
        COMMAND_RIGHT_MAX = true;
      } 
      else if (ac.y >= -6 && !COMMAND_RIGHT_MIN) {
        // Serial.println("right_min");
        tx(CMD_TOPIC, "right_min");
        command_direction_all_false();
        COMMAND_RIGHT_MIN = true;
      }
    }
    else if (-2 < ac.y && ac.y < 2 && !COMMAND_MID) {
      tx(CMD_TOPIC, "mid");
      command_direction_all_false();
      COMMAND_MID = true;
    }
  }

  void run() {
    mpu.getEvent(&a, &g, &temp);
    cmd_mpu_check(a.acceleration);
    runned();
  }
};

setup & loop

  • Example 에 나와있는 코드를 Thread 에 맞춰 조금 수정했다.
  • MQTT 송신용 tx() 메서드를 구현해서 재사용성을 높였다.
  • topic 은 command로 설정했다. -> 라즈베리파이에서 설정한 topic 과 같아야 함
  • thread controller 에 thread 상속받은 MPUThread 객체를 추가한다.
  • loop 에서 controller.run() 을 해주면 등록된 스레드 객체가 모두 start (run) 된다.
#include <Wire.h>
#include "EspMQTTClient.h"

EspMQTTClient client(
  "wifi SSID",
  "wifi password",     
  "MQTT Broker server ip or aws iot endpoint",  
  "MQTT Client ID",
  "MQTT Client PW",
  "MQTT Client Name",
  1883);

char *CMD_TOPIC = "command";

// mqtt 송신용 tx()
void tx(char *topic, char *cmd) {
  client.publish(topic, cmd);  //topic , cmd
}

// create thread
MPUThread mpu_th = MPUThread();
ThreadController controller = ThreadController();

// This is the callback for the Timer
void timerCallback() {
  controller.run();
}

void setup(void) {
  Serial.begin(115200); // serial monitor begin
  // mqtt client enable 
  client.enableHTTPWebUpdater(); 
  client.enableOTA();

  controller.add(&mpu_th);

  while (!Serial)
    delay(10);  // will pause Zero, Leonardo, etc until serial console opens

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
  delay(100);
}

void onConnectionEstablished() {
  //client.loop() 에 의해 호출되는 API
}

void loop() {
  controller.run();
  client.loop();
}
post-custom-banner

0개의 댓글