예전에 OS개발30일프로젝트 책보고 어느정도 진행하다가 커서띄우기?까지인가 하다가 말았던적이 있다.

os만들길할까 언어만들기해볼까 고민하다가 

가장 생각없이 할수있을것같은 책이어서 이 책을 진행해보려고함.

 

근데 이 책이 15년도 전에 나온 책이라 자료들링크가 안된다.

 

이 링크에 들어가도 안나오는데

http://www.zob.ne.jp/~c.mos/soft/bz.html

 

다행이 우리나라분이 따로블로그에 파일올려둠

https://m.blog.naver.com/websearch/70029495287

 

바이너리 에디터 (2진수 편집기)

"OS구조와 원리" 란 책을 읽다가 알게된 프로그램인데 바이너리 파일 작성 및 수정에 좋은 프로그램인 것...

blog.naver.com

 

나도 백업삼아서 여기다 추가로도올림

Bz162.zip
0.05MB

 

 

바이너리 헥스 에디터인데

키보드입력하면 좌측에는 헥스값이 우측에는 실제값이 나온다.

숫자 1 하면 31

0은 30

a는 61

b는 62식으로 찍힘

 

다시 돌아와서 책이 시키느데로 다시찍어보자

 

일단 000037까지 적긴했는데

90이후부터 168000까지 0누르면된다고한다.

그러면 37~90까진 뭘까?

 

별 내용이없으니 그냥 복붙으로 168000까지내려왓다.

 

 

중간에 0001F0과 001400에 00을하면안된다고해서 그부분들좀 고쳐주자

 

 

뭔진모르겟지만 디스크이미지를 만들었다!

설명대로 크기는 1,474,560 바이트(1440 x1024)

이름은 helloos.img로 저장

 

원래같으면 플로피디스크에다가 이걸 넣어서 부팅시켜 확인해야하는데 플로피디스켓이 없다.

예전에 내가 어떻게 했나 다시보면

 

https://throwexception.tistory.com/628

 

os만들기 - 2. 에뮬레이터에서 돌리기 삽질기 feat.msys

자료에서보면 boshs에 방금 만든 이미지를 돌리더라 바로 돌릴수 있는줄 알고 해봤지만 - 부팅가능한 이미지가 아니라고 아무것도 뜨지 않는다. 다시 잘보니 toolset으로 부팅 가능 이미지로 만들

throwexception.tistory.com

 

 

대충 이 사람이 만든걸 썻던것같다.

 

https://github.com/fakefish/OSASK/tree/master

 

GitHub - fakefish/OSASK: 30天自制操作系统的学习

30天自制操作系统的学习. Contribute to fakefish/OSASK development by creating an account on GitHub.

github.com

 

 

 

위 깃헙 파일 tolset 폴더에

helloos0 폴더만들고 내가 만든 이미지 복사

z_new_w에 있는 cons_9x와 cons_nt.bat 가져오고

 

install.bat과 run.bat 작성

 

 

 

 

 

cons_nt.bat 실행후 run 명령을 주면

qemu가 돌아가는데 이미지를 잘못 만들었는지 not a bootable disk가 뜬다. 

 

 

 

 

내가 뭘 잘못했는가 이해가안갓는데

위에서 아까 안쓴부분이 문제인것같았다.

다행이 이전에 내가 뭘보고 만든건진 모르겟지만 전체 작성한 기록이 있어서 참고해서 따라만듬

 

https://throwexception.tistory.com/629

 

os만들기 - 3. 글자찍히는 부팅가능한 바이너리 이미지 만들기

os만들기 - 2에서 한 모든 일들은 다 쓸모없는 짓이었다. 나중에 다시보니 지금 하는 바이너리 파일 만드는건 모든 바이너리 코드들을 내 마음대로가 아니라 똑같이 만들어야 부팅가능한 이미지

throwexception.tistory.com

 

 

 

 

 

 

 

다행이 이전 링크에 내가 처음 만든 이미지파일도 올려놔서 따라서 보고 뭐가 문젠지 찾아 고ㅕ쳤다.

바이너리 에디터로 부팅하는 이미지 만듦

helloos.img
1.41MB

 

 

 

 

import sys
import math
import random
import pygame
from pygame.locals import QUIT, KEYDOWN, K_LEFT, K_RIGHT, Rect

class Block:
    def __init__(self, col, rect, speed = 0):
        self.col = col
        self.rect = rect
        self.speed = speed
        self.dir = random.randint(-45, 45) + 270
    
    def move(self):
        self.rect.centerx += math.cos(math.radians(self.dir)) * self.speed
        self.rect.centery -= math.sin(math.radians(self.dir)) * self.speed
    
    def draw(self):
        if self.speed == 0:
            pygame.draw.rect(SURFACE, self.col, self.rect)
        else:
            pygame.draw.ellipse(SURFACE, self.col, self.rect)

def tick():
    global BLOCKS
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == KEYDOWN:
            if event.key == K_LEFT:
                PADDLE.rect.centerx -= 10
            elif event.key == K_RIGHT:
                PADDLE.rect.centerx += 10
    if BALL.rect.centery < 1000:
        BALL.move()

    prev_len = len(BLOCKS)
    BLOCKS = [
        x for x in BLOCKS
        if not x.rect.colliderect(BALL.rect)
    ]
    if len(BLOCKS) != prev_len:
        BALL.dir *= -1
    
    if PADDLE.rect.colliderect(BALL.rect):
        BALL.dir = 90 + (PADDLE.rect.centerx - BALL.rect.centerx) / PADDLE.rect.width * 80
    
    if BALL.rect.centerx < 0 or BALL.rect.centerx > 600:
        BALL.dir = 180 - BALL.dir
    if BALL.rect.centery < 0:
        BALL.dir = -BALL.dir
        BALL.speed = 15



pygame.init()
pygame.key.set_repeat(5, 5)
SURFACE = pygame.display.set_mode((600, 800))
FPSCLOCK = pygame.time.Clock()
BLOCKS = []
PADDLE = Block((242, 242, 0), Rect(300, 700, 100, 30))
BALL = Block((242, 242, 0), Rect(300, 400, 20, 20), 10)

def main():
    myfont = pygame.font.SysFont(None, 80)
    msg_clear = myfont.render("Cleared!", True, (255, 255, 0))
    msg_over = myfont.render("Game over!", True, (255, 255, 0))
    fps = 30
    colors = [(255, 0, 0), (255, 165, 0), (242, 242, 0),
              (0, 128,0), (128, 0, 128), (0, 0, 255)]
    for y_pos, color in enumerate(colors, start = 0):
        for x_pos in  range(0, 5):
            BLOCKS.append(Block(color, Rect(x_pos * 100 + 60, y_pos * 50 + 40, 80, 30)))
    
    while True:
        tick()
        SURFACE.fill((0, 0, 0))
        BALL.draw()
        PADDLE.draw()
        for block in BLOCKS:
            block.draw()
        if len(BLOCKS) == 0:
            SURFACE.blit(msg_clear, (200, 400))
        if BALL.rect.centery > 800 and len(BLOCKS) > 0:
            SURFACE.blit(msg_over, (150, 400))
        
        pygame.display.update()
        FPSCLOCK.tick(fps)

if __name__ == "__main__":
    main()

 

 

 

 

import sys
import random
import pygame
from pygame.locals import QUIT, KEYDOWN, K_LEFT, K_RIGHT, K_UP, K_DOWN, Rect

pygame.init()
pygame.key.set_repeat(5, 5) # control how held keys are repeated
SURFACE = pygame.display.set_mode([600, 600])
FPSCLOCK = pygame.time.Clock()

class Snake:
    def __init__(self, pos):
        self.bodies = [pos]
    
    def move(self, key):
        x_pos, y_pos = self.bodies[0]

        if key == K_LEFT:
            x_pos -= 1
        elif key == K_RIGHT:
            x_pos += 1
        elif key == K_UP:
            y_pos -= 1
        elif key == K_DOWN:
            y_pos += 1
        head = (x_pos, y_pos)

        is_game_over = head in self.bodies or \
            head[0] < 0 or head[0] >= W or \
            head[1] < 0 or head[1] >= H

        self.bodies.insert(0, head)
        if head in FOODS:
            i = FOODS.index(head)
            del FOODS[i]
            add_food(self)
        else:
            self.bodies.pop()
        return is_game_over
    
    def draw(self):
        for body in self.bodies:
            pygame.draw.rect(SURFACE, (0, 255, 255),
                             Rect(body[0] * 30, body[1] * 30, 30, 30))

FOODS = []
(W, H) = (20, 20)

def add_food(snake):
    while True:
        pos = (random.randint(0, W-1), random.randint(0, H - 1))
        if pos in FOODS or pos in snake.bodies:
            continue
        FOODS.append(pos)
        break

def paint(snake, msg):
    SURFACE.fill((0, 0, 0))
    snake.draw()

    for food in FOODS:
        pygame.draw.ellipse(SURFACE, (0, 255, 0),
                            Rect(food[0] * 30, food[1] * 30, 30, 30))
    for index in range(20):
        pygame.draw.line(SURFACE, (64, 64, 64),
                         (index*30, 0), (index*30, 600))
        pygame.draw.line(SURFACE, (64, 64, 64),
                         (0, index*30), (600, index* 30))
    
    if msg != None:
        SURFACE.blit(msg, (150, 300))
    pygame.display.update()

def main():
    myfont = pygame.font.SysFont(None, 80)
    key = K_DOWN
    msg = None
    game_over = False
    snake = Snake((int(W/2), int(H/2)))
    for _ in range(10):
        add_food(snake)
    
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == KEYDOWN:
                key = event.key
        
        if game_over:
            msg = myfont.render("Game Over!", True, (255, 255, 0))
        else:
            game_over = snake.move(key)
        
        paint(snake, msg)
        FPSCLOCK.tick(10)

if __name__ == "__main__":
    main()

 

 

 

 

 

 

 

import sys
from math import floor
from random import randint
import pygame
from pygame.locals import QUIT, MOUSEBUTTONDOWN

WIDTH = 20
HEIGHT = 15
SIZE = 50
NUM_OF_BOMBS = 20
EMPTY = 0
BOMB = 1
OPENED = 2
OPENED_COUNT = 0
CHECKED = [
    [
        0 for _ in range(WIDTH)
    ]
    for _ in range(HEIGHT)
]

pygame.init()
SURFACE = pygame.display.set_mode(([WIDTH * SIZE, HEIGHT * SIZE]))
FPSCLOCK = pygame.time.Clock()

def num_of_bomb(field, x_pos, y_pos):
    cnt = 0
    for y_off in range(-1, 2):
        for x_off in range(-1, 2):
            xpos, ypos = (x_pos + x_off, y_pos + y_off)
            if 0 <= xpos < WIDTH and 0 <= ypos < HEIGHT and\
                field[ypos][xpos] == BOMB:
                cnt +=1
    return cnt

def open_tile(field, x_pos, y_pos):
    global OPENED_COUNT
    if CHECKED[y_pos][x_pos]:
        return
    CHECKED[y_pos][x_pos] = True

    for y_off in range(-1, 2):
        for x_off in range(-1, 2):
            xpos, ypos = (x_pos + x_off, y_pos + y_off)
            if 0 <= xpos < WIDTH and 0 <= ypos < HEIGHT and\
                field[ypos][xpos] == EMPTY:
                field[ypos][xpos] = OPENED
                OPENED_COUNT += 1
                cnt = num_of_bomb(field, xpos, ypos)
                if cnt == 0 and not (xpos == x_pos and ypos == y_pos):
                    open_tile(field, xpos, ypos)

def main():
    small_font = pygame.font.SysFont(None, 36)
    large_font = pygame.font.SysFont(None, 72)
    msg_clear = large_font.render("!!CLEARED!!", True, (0, 255, 255))
    msg_over = large_font.render("GAME OVER!!", True, (0, 255, 255))
    msg_rect = msg_clear.get_rect()
    msg_rect.center = (WIDTH*SIZE/2, HEIGHT*SIZE/2)
    game_over = False

    field = [
        [
            EMPTY for x_pos in range(WIDTH)
        ]
        for y_pos in range(HEIGHT)
        ]
    cnt = 0
    while cnt < NUM_OF_BOMBS:
        x_pos, y_pos = randint(0, WIDTH-1), randint(0, HEIGHT - 1)
        if field[y_pos][x_pos] == EMPTY:
            field[y_pos][x_pos] = BOMB
            cnt +=1
    
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            if event.type == MOUSEBUTTONDOWN and event.button == 1:
                x_pos, y_pos = floor(event.pos[0] / SIZE),\
                            floor(event.pos[1] / SIZE)
                if field[y_pos][x_pos] == BOMB:
                    game_over = True
                else:
                    open_tile(field, x_pos, y_pos)
        

        SURFACE.fill((0, 0, 0))
        for y_pos in range(HEIGHT):
            for x_pos in range(WIDTH):
                tile = field[y_pos][x_pos]
                rect = (x_pos * SIZE, y_pos * SIZE, SIZE, SIZE)
                if tile == EMPTY or tile == BOMB:
                    pygame.draw.rect(SURFACE, (192, 192, 192), rect)
                    if game_over and tile == BOMB:
                        pygame.draw.ellipse(SURFACE, (225, 225, 0), rect)
                elif tile == OPENED:
                    cnt = num_of_bomb(field, x_pos, y_pos)
                    if cnt > 0:
                        num_img = small_font.render(
                            "{}".format(cnt), True, (255, 255, 0)
                        )
                        SURFACE.blit(num_img, (x_pos * SIZE + 10, y_pos * SIZE + 10))

        for idx in range(0, WIDTH*SIZE, SIZE):
            pygame.draw.line(SURFACE, (96, 96, 96), (idx, 0), (idx, HEIGHT * SIZE))
        for idx in range(0, HEIGHT*SIZE, SIZE):
            pygame.draw.line(SURFACE, (96, 96, 96), (0, idx), (WIDTH*SIZE, idx))
        
        if OPENED_COUNT == WIDTH*HEIGHT - NUM_OF_BOMBS:
            SURFACE.blit(msg_clear, msg_rect.topleft)
        elif game_over:
            SURFACE.blit(msg_over, msg_rect.topleft)
        
        pygame.display.update()
        FPSCLOCK.tick(15)
    
if __name__ == "__main__":
    main()

계속 손에 안잡히고 시작 못해서

간단하게 파이게임 진행해보려고함.

 

 

 

 

파이게임 단순 화면띄우기 예제

import sys
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))
pygame.display.set_caption("just window")

def main():
    while True:
        SURFACE.fill((255, 255, 255))

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
        
        pygame.display.update()

if __name__ == "__main__":
    main()

 

 

이벤트

- 이벤트 큐에 저장

- 큐 맨앞에 이벤트 꺼내 처리

 

 

 

간단한 글자 띄우기

import sys
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))

def main():
    sysfont = pygame.font.SysFont(None, 36)
    counter = 0
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
        counter += 1
        SURFACE.fill((0, 0, 0))
        count_image = sysfont.render("count is {}".format(counter), True, (255, 255, 255))
        SURFACE.blit(count_image, (50, 50))
        pygame.display.update()

if __name__ == "__main__":
    main()

 

 

fps 설정 1

- pygame.time.Clock() tick(10) 1초에 10번 루프

 

import sys
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))
FPSCLOCK = pygame.time.Clock()


def main():
    sysfont = pygame.font.SysFont(None, 36)
    counter = 0
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
        counter += 1
        SURFACE.fill((0, 0, 0))
        count_image = sysfont.render("count is {}".format(counter), True, (255, 255, 255))
        SURFACE.blit(count_image, (50, 50))
        pygame.display.update()

        FPSCLOCK.tick(10)

if __name__ == "__main__":
    main()

 

 

 

상자 그리기

 

import sys
import pygame
from pygame.locals import QUIT, Rect

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))
FPSCLOCK = pygame.time.Clock()


def main():
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit
        SURFACE.fill((255, 255, 255))

        pygame.draw.rect(SURFACE, (255, 0, 0), (10, 20, 100, 50))
        pygame.draw.rect(SURFACE, (255, 0, 0), (150, 10, 100, 30), 3)
        pygame.draw.rect(SURFACE, (0, 255, 0), ((100, 80), (80, 50)))

        rect0 = Rect((200, 60), (140, 80))
        pygame.draw.rect(SURFACE, (0, 0, 255), rect0)
        rect1 = Rect((30, 160), (100, 50))
        pygame.draw.rect(SURFACE, (255, 255, 0), rect1)

        pygame.display.update()
        FPSCLOCK.tick(3)

if __name__ == "__main__":
    main()

 

 

import sys
import pygame
from pygame.locals import QUIT, Rect, Circle

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))
FPSCLOCK = pygame.time.Clock()


def main():
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit
        SURFACE.fill((255, 255, 255))

        pygame.draw.circle(SURFACE, (255, 0, 0), (50, 50), 20)
        pygame.draw.circle(SURFACE, (255, 0, 0), (150, 50), 20, 10)


        pygame.draw.circle(SURFACE, (0, 255, 0), (50, 150), 10)
        pygame.draw.circle(SURFACE, (0, 255, 0), (150, 150), 20)
        pygame.draw.circle(SURFACE, (0, 255, 0), (250, 150), 30)

        pygame.draw.ellipse(SURFACE, (255, 0, 0), (50, 50, 140, 60))
        pygame.draw.ellipse(SURFACE, (255, 0, 0), (250, 30, 90, 90))

        pygame.display.update()
        FPSCLOCK.tick(3)

if __name__ == "__main__":
    main()

 

 

 

원, 타원 그리기

import sys
import pygame
from pygame.locals import QUIT, Rect

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))
FPSCLOCK = pygame.time.Clock()


def main():
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit
        SURFACE.fill((255, 255, 255))

        pygame.draw.circle(SURFACE, (255, 0, 0), (50, 50), 20)
        pygame.draw.circle(SURFACE, (255, 0, 0), (150, 50), 20, 10)


        pygame.draw.circle(SURFACE, (0, 255, 0), (50, 150), 10)
        pygame.draw.circle(SURFACE, (0, 255, 0), (150, 150), 20)
        pygame.draw.circle(SURFACE, (0, 255, 0), (250, 150), 30)

        pygame.draw.ellipse(SURFACE, (255, 0, 0), (50, 50, 140, 60))
        pygame.draw.ellipse(SURFACE, (255, 0, 0), (250, 30, 90, 90))

        pygame.display.update()
        FPSCLOCK.tick(3)

if __name__ == "__main__":
    main()

 

선긋기

 

import sys
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((400, 220))
FPSCLOCK = pygame.time.Clock()

def main():
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
        
        SURFACE.fill((255, 255, 255))
        pygame.draw.line(SURFACE, (255, 0, 0), (10, 80), (200, 80))
        pygame.draw.line(SURFACE, (255, 0, 0), (10, 150), (200, 150), 15)
        pygame.draw.line(SURFACE, (0, 255, 0), (250, 30), (250, 200))

        start_pos = (300, 30)
        end_pos = (380, 200)
        pygame.draw.line(SURFACE, (0, 0, 255), start_pos, end_pos, 10)

        pygame.display.update()
        FPSCLOCK.tick(3)

if __name__ == "__main__":
    main()

 

 

 

 

그리드 만들기

 

import sys
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))
FPSCLOCK = pygame.time.Clock()

def main():
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        SURFACE.fill((0, 0, 0))

        for xpos in range(0, 400, 25):
            pygame.draw.line(SURFACE, 0xFFFFFF, (xpos, 0), (xpos,300))
        for ypos in range(0, 300, 25):
            pygame.draw.line(SURFACE, 0xFFFFFF, (0, ypos), (400, ypos))
        pygame.display.update()
        FPSCLOCK.tick(3)
    

if __name__ == "__main__":
    main()

 

 

 

 

 

 

이은선

 

import sys
import random
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))
FPSCLOCK = pygame.time.Clock()

def main():
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        SURFACE.fill((0, 0, 0))

        pointlist = []
        for _ in range(10):
            xpos = random.randint(0, 400)
            ypos = random.randint(0, 300)
            pointlist.append((xpos, ypos))
        
        pygame.draw.lines(SURFACE, (255, 255, 255), True, pointlist, 5)
        pygame.display.update()
        FPSCLOCK.tick(3)

if __name__ == "__main__":
    main()

 

 

 

텍스트 쓰기

import sys
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((400, 200))
FPSCLOCK = pygame.time.Clock()

def main():
    sysfont = pygame.font.SysFont(None, 72)
    msg = sysfont.render("hello python", True, (0, 128, 128))
    msg_rect = msg.get_rect()
    msg_rect.center = (200, 100)


    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
        SURFACE.fill((255, 255, 255))
        SURFACE.blit(msg, msg_rect)
        pygame.display.update()
        FPSCLOCK.tick(3)

if __name__ == "__main__":
    main()

 

 

 

 

 

 

텍스트 줌 회전

import sys
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((400, 200))
FPSCLOCK = pygame.time.Clock()

def main():
    sysfont = pygame.font.SysFont(None, 72)
    msg = sysfont.render("hello python", True, (0, 128, 128))
    msg_rect = msg.get_rect()
    theta = 0
    scale = 1


    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
        SURFACE.fill((255, 255, 255))

        theta += 5
        scale = (theta % 360) / 180
        tmp_msg = pygame.transform.rotozoom(msg, theta, scale)
        tmp_rect = tmp_msg.get_rect()
        tmp_rect.center = (200,150)


        SURFACE.blit(tmp_msg, tmp_rect)
        pygame.display.update()
        FPSCLOCK.tick(30)

if __name__ == "__main__":
    main()

 

 

 

마우스 클릭 이벤트

import sys
import pygame
from pygame.locals import QUIT, MOUSEBUTTONDOWN

pygame.init()
SURFACE = pygame.display.set_mode((400, 300))
FPSCLOCK = pygame.time.Clock()

def main():
    mousepos = []
    while True:
        SURFACE.fill((255, 255, 255))
        
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == MOUSEBUTTONDOWN:
                mousepos.append(event.pos)
        
        for pos in mousepos:
            pygame.draw.circle(SURFACE, (0, 255, 0), pos, 5)
        
        pygame.display.update()
        FPSCLOCK.tick(10)

if __name__ == "__main__":
    main()

 

 

요 며칠새 블랜더도 손에 안잡히고 

센서 다루는것도 손에 안잡혀서 계속 놀고 있었다.

 

 

mpu6050 3개에서 ypr 가져오도록 하긴했는데

평평한 곳에 같은 방향으로 놓아도 yaw값이 다 다르게 나와서

mpu6050에 가속조 자이로는 없어도 지자기가 없다길래

mpu9250이라도 새로 사서 해야하나 좌절하고 있었다.

 

 

오랜만에 imu 가지고 트래킹하는걸 찾다가

 

내가 생각한거랑 비슷한걸 한 사람을 찾았다.

이 사람의 경우 블랜더에서 하는걸 보여주는데

 

계속 센서 위치를 어떻게 놓는게 좋을까 고민하던중에 보게되었다.

 

 

 

https://www.youtube.com/watch?v=F1IdRtIDdIs

 

 

더불어 소스코드도 같이 올려줬는데

이 친구는 ypr이 아닌 쿼터니언을 뽑아오도록 하더라

 

근데 찝찝한게 내 mpu6050은 같은 방향을 보도록 해도 yaw가 다르던데

쿼터니언으로 안한게 문제였을까?

https://github.com/T-K-233/Dual-MPU6050-Motion-Sync/blob/main/sensors/Arduino_dual_MPU6050_joint_capture/Arduino_dual_MPU6050_joint_capture.ino

 

mpu6050_dmp 코드를 보면

오일러, 쿼터니언, ypr, 가속도 등 출력해내도록 있던데

ypr이나 오일러각이랑 비슷한거 아닌가?

https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/MPU6050/examples/MPU6050_DMP6/MPU6050_DMP6.ino

 

 

 

 

 

싶어서 구글링 좀하면서 해매다가

이글도 처음에 대충 지나가면서  이해가 안갔는데

 

https://taeyoung96.github.io/mathematics/Euler/

 

Euler Angle 과 Roll Pitch Yaw

Euler Angle(오일러 각) 과 Roll Pitch Yaw에 대해서 알아보자.

taeyoung96.github.io

 

 

이 글이랑 영상 몇개보고

이해갔다.

roll pitch yaw는 고정된 초기 축에 따라서 회전학 각도고

오일러각은 회전하면서 회전기준축도 이동한다.

 

위 국내 글에 그림을 잘보면 이 내용이 있는데 제대로 안봐서 못보고 지나갓엇다.

https://analyticalgraphics.my.site.com/faqs/articles/Knowledge/What-is-the-difference-between-YPR-and-Euler-attitude-in-STK

 

 

 

 

아무튼 내 mpu6050도 쿼터니언으로 뽑으면 좀 나으려나?

 

안그래도 이전에 만들면서도 오프셋 고쳐야할탠대 싶었는데

위 영상 만든사람도 오프셋을 고쳐서 하라고한다.

 

그런데 보다보니까

내가 갖고잇는 dmp 코드랑 뭔가 조금 다르더라.

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu68.setXGyroOffset(0);
  mpu68.setYGyroOffset(0);
  mpu68.setZGyroOffset(0);
  mpu68.setXAccelOffset(0);
  mpu68.setYAccelOffset(0);
  mpu68.setZAccelOffset(0);
  
  mpu69.setXGyroOffset(0);
  mpu69.setYGyroOffset(0);
  mpu69.setZGyroOffset(0);
  mpu69.setXAccelOffset(0);
  mpu69.setYAccelOffset(0);
  mpu69.setZAccelOffset(0);

  
  // Calibration Time: generate offsets and calibrate our MPU6050
  mpu68.CalibrateAccel(6);
  mpu68.CalibrateGyro(6);
  
  mpu69.CalibrateAccel(6);
  mpu69.CalibrateGyro(6);

 

 

 

 

 

 

이게 2019년도에 업데이트된 최신 코드같은데

https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/MPU6050/examples/MPU6050_DMP6_using_DMP_V6.12/MPU6050_DMP6_using_DMP_V6.12.ino

 

 

 

 

 

 

 

내가 사용한 mpu6050 라이브러리는

2013년에 만들어진 아주 오래된 코드를 기반으로 하고있었다 --..

// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0)
// 6/21/2012 by Jeff Rowberg <jeff@rowberg.net>
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
//
// Changelog:
//      2013-05-08 - added seamless Fastwire support
//                 - added note about gyro calibration
//      2012-06-21 - added note about Arduino 1.0.1 + Leonardo compatibility error
//      2012-06-20 - improved FIFO overflow handling and simplified read process
//      2012-06-19 - completely rearranged DMP initialization code and simplification
//      2012-06-13 - pull gyro and accel data from FIFO packet instead of reading directly
//      2012-06-09 - fix broken FIFO read sequence and change interrupt detection to RISING
//      2012-06-05 - add gravity-compensated initial reference frame acceleration output
//                 - add 3D math helper file to DMP6 example sketch
//                 - add Euler output and Yaw/Pitch/Roll output formats
//      2012-06-04 - remove accel offset clearing for better results (thanks Sungon Lee)
//      2012-06-01 - fixed gyro sensitivity to be 2000 deg/sec instead of 250
//      2012-05-30 - basic DMP initialization working

/* ============================================

 

 

 

 

최신 코드

체인지로그

// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v6.12)
// 6/21/2012 by Jeff Rowberg <jeff@rowberg.net>
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
//
// Changelog:
//      2019-07-10 - Uses the new version of the DMP Firmware V6.12
//                 - Note: I believe the Teapot demo is broken with this versin as
//                 - the fifo buffer structure has changed
//      2016-04-18 - Eliminated a potential infinite loop
//      2013-05-08 - added seamless Fastwire support
//                 - added note about gyro calibration
//      2012-06-21 - added note about Arduino 1.0.1 + Leonardo compatibility error
//      2012-06-20 - improved FIFO overflow handling and simplified read process
//      2012-06-19 - completely rearranged DMP initialization code and simplification
//      2012-06-13 - pull gyro and accel data from FIFO packet instead of reading directly
//      2012-06-09 - fix broken FIFO read sequence and change interrupt detection to RISING
//      2012-06-05 - add gravity-compensated initial reference frame acceleration output
//                 - add 3D math helper file to DMP6 example sketch
//                 - add Euler output and Yaw/Pitch/Roll output formats
//      2012-06-04 - remove accel offset clearing for better results (thanks Sungon Lee)
//      2012-06-01 - fixed gyro sensitivity to be 2000 deg/sec instead of 250
//      2012-05-30 - basic DMP initialization working

/* ============================================

 

 

3/6 작성

---------------------------------

3/12 작성

 

계속 개으름 피우다가 다시 시작

 

이 mpu6050 ypr 추출 코드 작성한 사람 깃헙을 보면

https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050

 

모션앱뒤에 20, 41, 612가 붙어있는데

이게 뭔가 보니

 

 

 

 

DMP 펌웨어 버전을 말하는듯하다.

 

https://invensense.tdk.com/products/motion-tracking/6-axis/mpu-6050/

 

MPU-6050 | TDK InvenSense

NOT RECOMMENDED FOR NEW DESIGNS MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking™ Devices   The MPU-6050™ parts are the world’s first MotionTracking devices designed for the low power, low cost, and high-performance requirements of smart

invensense.tdk.com

 

 

// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v6.12)
// 6/21/2012 by Jeff Rowberg <jeff@rowberg.net>
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
//
// Changelog:
//      2019-07-10 - Uses the new version of the DMP Firmware V6.12
//                 - Note: I believe the Teapot demo is broken with this versin as
//                 - the fifo buffer structure has changed

 

 

 

일단 이 예제코드로 동작시켜봄

 

https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/MPU6050/examples/MPU6050_DMP6_using_DMP_V6.12/MPU6050_DMP6_using_DMP_V6.12.ino

#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps612.h"
//#include "MPU6050.h" // not necessary if using MotionApps include file

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif


MPU6050 mpu;

#define OUTPUT_READABLE_YAWPITCHROLL



#define INTERRUPT_PIN 2  // use pin 2 on Arduino Uno & most boards
#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
bool blinkState = false;

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 gy;         // [x, y, z]            gyro sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, '\r', '\n' };



// ================================================================
// ===               INTERRUPT DETECTION ROUTINE                ===
// ================================================================

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
  mpuInterrupt = true;
}



// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================

void setup() {
  // join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
#endif

  // initialize serial communication
  // (115200 chosen because it is required for Teapot Demo output, but it's
  // really up to you depending on your project)
  Serial.begin(115200);
  while (!Serial); // wait for Leonardo enumeration, others continue immediately

  // NOTE: 8MHz or slower host processors, like the Teensy @ 3.3V or Arduino
  // Pro Mini running at 3.3V, cannot handle this baud rate reliably due to
  // the baud timing being too misaligned with processor ticks. You must use
  // 38400 or slower in these cases, or use some kind of external separate
  // crystal solution for the UART timer.

  // initialize device
  Serial.println(F("Initializing I2C devices..."));
  mpu.initialize();
  pinMode(INTERRUPT_PIN, INPUT);

  // verify connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // wait for ready
  Serial.println(F("\nSend any character to begin DMP programming and demo: "));
  while (Serial.available() && Serial.read()); // empty buffer
  while (!Serial.available());                 // wait for data
  while (Serial.available() && Serial.read()); // empty buffer again

  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(51);
  mpu.setYGyroOffset(8);
  mpu.setZGyroOffset(21);
  mpu.setXAccelOffset(1150);
  mpu.setYAccelOffset(-50);
  mpu.setZAccelOffset(1060);
  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
    // Calibration Time: generate offsets and calibrate our MPU6050
    mpu.CalibrateAccel(6);
    mpu.CalibrateGyro(6);
    Serial.println();
    mpu.PrintActiveOffsets();
    // turn on the DMP, now that it's ready
    Serial.println(F("Enabling DMP..."));
    mpu.setDMPEnabled(true);

    // enable Arduino interrupt detection
    Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
    Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
    Serial.println(F(")..."));
    attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();

    // set our DMP Ready flag so the main loop() function knows it's okay to use it
    Serial.println(F("DMP ready! Waiting for first interrupt..."));
    dmpReady = true;

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
    // ERROR!
    // 1 = initial memory load failed
    // 2 = DMP configuration updates failed
    // (if it's going to break, usually the code will be 1)
    Serial.print(F("DMP Initialization failed (code "));
    Serial.print(devStatus);
    Serial.println(F(")"));
  }

  // configure LED for output
  pinMode(LED_PIN, OUTPUT);
}



// ================================================================
// ===                    MAIN PROGRAM LOOP                     ===
// ================================================================

void loop() {
  // if programming failed, don't try to do anything
  if (!dmpReady) return;
  // read a packet from FIFO
  if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Latest packet 

#ifdef OUTPUT_READABLE_QUATERNION
    // display quaternion values in easy matrix form: w x y z
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    Serial.print("quat\t");
    Serial.print(q.w);
    Serial.print("\t");
    Serial.print(q.x);
    Serial.print("\t");
    Serial.print(q.y);
    Serial.print("\t");
    Serial.println(q.z);
#endif

#ifdef OUTPUT_READABLE_EULER
    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetEuler(euler, &q);
    Serial.print("euler\t");
    Serial.print(euler[0] * 180 / M_PI);
    Serial.print("\t");
    Serial.print(euler[1] * 180 / M_PI);
    Serial.print("\t");
    Serial.println(euler[2] * 180 / M_PI);
#endif

#ifdef OUTPUT_READABLE_YAWPITCHROLL
    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    Serial.print("ypr\t");
    Serial.print(ypr[0] * 180 / M_PI);
    Serial.print("\t");
    Serial.print(ypr[1] * 180 / M_PI);
    Serial.print("\t");
    Serial.print(ypr[2] * 180 / M_PI);
    /*
      mpu.dmpGetAccel(&aa, fifoBuffer);
      Serial.print("\tRaw Accl XYZ\t");
      Serial.print(aa.x);
      Serial.print("\t");
      Serial.print(aa.y);
      Serial.print("\t");
      Serial.print(aa.z);
      mpu.dmpGetGyro(&gy, fifoBuffer);
      Serial.print("\tRaw Gyro XYZ\t");
      Serial.print(gy.x);
      Serial.print("\t");
      Serial.print(gy.y);
      Serial.print("\t");
      Serial.print(gy.z);
    */
    Serial.println();

#endif

#ifdef OUTPUT_READABLE_REALACCEL
    // display real acceleration, adjusted to remove gravity
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetAccel(&aa, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
    Serial.print("areal\t");
    Serial.print(aaReal.x);
    Serial.print("\t");
    Serial.print(aaReal.y);
    Serial.print("\t");
    Serial.println(aaReal.z);
#endif

#ifdef OUTPUT_READABLE_WORLDACCEL
    // display initial world-frame acceleration, adjusted to remove gravity
    // and rotated based on known orientation from quaternion
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetAccel(&aa, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
    mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
    Serial.print("aworld\t");
    Serial.print(aaWorld.x);
    Serial.print("\t");
    Serial.print(aaWorld.y);
    Serial.print("\t");
    Serial.println(aaWorld.z);
#endif

#ifdef OUTPUT_TEAPOT
    // display quaternion values in InvenSense Teapot demo format:
    teapotPacket[2] = fifoBuffer[0];
    teapotPacket[3] = fifoBuffer[1];
    teapotPacket[4] = fifoBuffer[4];
    teapotPacket[5] = fifoBuffer[5];
    teapotPacket[6] = fifoBuffer[8];
    teapotPacket[7] = fifoBuffer[9];
    teapotPacket[8] = fifoBuffer[12];
    teapotPacket[9] = fifoBuffer[13];
    Serial.write(teapotPacket, 14);
    teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
#endif

    // blink LED to indicate activity
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);
  }
}

 

 

잘 나오기는 한데 euler에서는 중력을 안쓰고 ypr 계산시에 중력을 사용하는 이유가 뭘까

실제 코드 dmpgetypr 들어가면 gravity 사용해서 계산하긴한다.

#ifdef OUTPUT_READABLE_QUATERNION
    // display quaternion values in easy matrix form: w x y z
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    Serial.print("quat\t");
    Serial.print(q.w);
    Serial.print("\t");
    Serial.print(q.x);
    Serial.print("\t");
    Serial.print(q.y);
    Serial.print("\t");
    Serial.println(q.z);
#endif

#ifdef OUTPUT_READABLE_EULER
    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetEuler(euler, &q);
    Serial.print("euler\t");
    Serial.print(euler[0] * 180 / M_PI);
    Serial.print("\t");
    Serial.print(euler[1] * 180 / M_PI);
    Serial.print("\t");
    Serial.println(euler[2] * 180 / M_PI);
#endif

#ifdef OUTPUT_READABLE_YAWPITCHROLL
    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
uint8_t MPU6050::dmpGetQuaternion(int16_t *data, const uint8_t* packet) {
    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
    if (packet == 0) packet = dmpPacketBuffer;
    data[0] = ((packet[0] << 8) | packet[1]);
    data[1] = ((packet[4] << 8) | packet[5]);
    data[2] = ((packet[8] << 8) | packet[9]);
    data[3] = ((packet[12] << 8) | packet[13]);
    return 0;
}
uint8_t MPU6050::dmpGetQuaternion(Quaternion *q, const uint8_t* packet) {
    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
    int16_t qI[4];
    uint8_t status = dmpGetQuaternion(qI, packet);
    if (status == 0) {
        q -> w = (float)qI[0] / 16384.0f;
        q -> x = (float)qI[1] / 16384.0f;
        q -> y = (float)qI[2] / 16384.0f;
        q -> z = (float)qI[3] / 16384.0f;
        return 0;
    }
    return status; // int16 return value, indicates error if this line is reached
}


uint8_t MPU6050::dmpGetEuler(float *data, Quaternion *q) {
    data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1);   // psi
    data[1] = -asin(2*q -> x*q -> z + 2*q -> w*q -> y);                              // theta
    data[2] = atan2(2*q -> y*q -> z - 2*q -> w*q -> x, 2*q -> w*q -> w + 2*q -> z*q -> z - 1);   // phi
    return 0;
}

#ifdef USE_OLD_DMPGETYAWPITCHROLL
uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) {
    // yaw: (about Z axis)
    data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1);
    // pitch: (nose up/down, about Y axis)
    data[1] = atan(gravity -> x / sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z));
    // roll: (tilt left/right, about X axis)
    data[2] = atan(gravity -> y / sqrt(gravity -> x*gravity -> x + gravity -> z*gravity -> z));
    return 0;
}
#else 
uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) {
    // yaw: (about Z axis)
    data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1);
    // pitch: (nose up/down, about Y axis)
    data[1] = atan2(gravity -> x , sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z));
    // roll: (tilt left/right, about X axis)
    data[2] = atan2(gravity -> y , gravity -> z);
    if (gravity -> z < 0) {
        if(data[1] > 0) {
            data[1] = PI - data[1]; 
        } else { 
            data[1] = -PI - data[1];
        }
    }
    return 0;
}
#endif

 

 

 

위 계산에 대한 해설은 다음 링크에 있다.

https://oduerr.github.io/gesture/ypr_calculations.html

 

YawPitchRollCalculation

This document describes how the orientation of the IMU in terms of yaw,pitch, and roll is calculated from the information provided by the IMU (we use the MPU-9250). Calculations as done on the Arduino The result of the calculations in the Digital Motion Pr

oduerr.github.io

 

 

ypr 계산시 왜 gravity 백터를 사용하는지 궁금해서 구글링하다

국내에 mpu 사용한 글들 몇가지 찾아봄

 

https://blog.naver.com/ysahn2k/221410391235

 

[MEMS] MPU6050 + DMP6 기본동작

이번 블로그에서는 지난 상보필터에 대한 블로그에서 잠깐 언급한 Jeff Rowberg님의 DMP6 프로그램에 ...

blog.naver.com

 

https://m.blog.naver.com/yuninjae1234/220935189584

 

자이로센서 roll, pitch, yaw 데이터 처리 구조 (AHRS)

드론을 만들기위해 자이로 센서를 공부하면서 많은 예제들을 보고 공부하였지만 드문드문 있는 자료들을 머...

blog.naver.com

 

 

 

ypr 대신 그냥 쿼터니언과 오일러만 출력시켜보니

초기 시점 기준으로 회전한 만큼 각이 바뀐다.

중력벡터를 사용하면서 실제 중력 기준으로 각도를 변환해주는 거구나.

중력벡터를 안쓰고 하면 초기 위치 기준으로 값들이 출력된다.

 

왜 맨 위에 블렌더에서 실험한 사람은 yaw pitch roll을 안쓰고 바로 쿼터니언을 사용한것인지 이제 이해가 간다.

아래는 필요없는 부분 제거해서 좀더 간략하게 줄인 코드

#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps612.h"
//#include "MPU6050.h" // not necessary if using MotionApps include file

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif


MPU6050 mpu;

//#define OUTPUT_READABLE_YAWPITCHROLL
#define OUTPUT_READABLE_EULER

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 gy;         // [x, y, z]            gyro sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector


// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================

void setup() {


#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
#endif

  Serial.begin(115200);
  while (!Serial); // wait for Leonardo enumeration, others continue immediately


  // initialize device
  Serial.println(F("Initializing I2C devices..."));
  mpu.initialize();

  // verify connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(51);
  mpu.setYGyroOffset(8);
  mpu.setZGyroOffset(21);
  mpu.setXAccelOffset(1150);
  mpu.setYAccelOffset(-50);
  mpu.setZAccelOffset(1060);
  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
    // Calibration Time: generate offsets and calibrate our MPU6050
    mpu.CalibrateAccel(6);
    mpu.CalibrateGyro(6);
    Serial.println();
    mpu.PrintActiveOffsets();
    // turn on the DMP, now that it's ready
    Serial.println(F("Enabling DMP..."));
    mpu.setDMPEnabled(true);

    mpuIntStatus = mpu.getIntStatus();

    // set our DMP Ready flag so the main loop() function knows it's okay to use it
    Serial.println(F("DMP ready! Waiting for first interrupt..."));
    dmpReady = true;

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
    Serial.print(F("DMP Initialization failed (code "));
    Serial.print(devStatus);
    Serial.println(F(")"));
  }

}



// ================================================================
// ===                    MAIN PROGRAM LOOP                     ===
// ================================================================

void loop() {
  // if programming failed, don't try to do anything
  if (!dmpReady) return;
  // read a packet from FIFO
  if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Latest packet 

#ifdef OUTPUT_READABLE_QUATERNION
    // display quaternion values in easy matrix form: w x y z
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    Serial.print("quat\t");
    Serial.print(q.w);
    Serial.print("\t");
    Serial.print(q.x);
    Serial.print("\t");
    Serial.print(q.y);
    Serial.print("\t");
    Serial.println(q.z);
#endif

#ifdef OUTPUT_READABLE_EULER
    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetEuler(euler, &q);
    Serial.print("euler\t");
    Serial.print(euler[0] * 180 / M_PI);
    Serial.print("\t");
    Serial.print(euler[1] * 180 / M_PI);
    Serial.print("\t");
    Serial.println(euler[2] * 180 / M_PI);
#endif

#ifdef OUTPUT_READABLE_YAWPITCHROLL
    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    Serial.print("ypr\t");
    Serial.print(ypr[0] * 180 / M_PI);
    Serial.print("\t");
    Serial.print(ypr[1] * 180 / M_PI);
    Serial.print("\t");
    Serial.print(ypr[2] * 180 / M_PI);
    Serial.println();

#endif
  }
}

 

 

 

 

 

지난글에 esp32 mpu6050 보조베터리 블루투스로 

ypr 여러개 보내도록 했고

언리얼에서 동작시키게 하려하는데 계속 개으름피우고 놀고 있었다.

 

근데 데이터 가져와서 쓰자니

어떻게 분할시킬까가 고민이었는데

구분해서 보려고 대충

핀:yrp/핀:ypr/핀:ypr  \n

이런식으로 출력되게 만들었지만

 

언리얼에서 구분하기 귀찬을거같아서

ypr:ypr:ypr/

 

각 mpy간에는 :로 구분하고

다음 데이터와는 /로 구분하는 식으로수정해서 다루려고함.

 

 

esp32 업로드 코드

마지막에 있는 구분자 코드만 수정

 

 

#include "I2Cdev.h"
#include <map>

#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

MPU6050 mpu;

#define OUTPUT_READABLE_YAWPITCHROLL

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}


#include "BluetoothSerial.h"
String device_name = "ESP32-BT-Slave";

// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
  #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
  #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif

BluetoothSerial BTSerial;
int mpuPinNums[] = {32, 33, 25};
int mpuNum = sizeof(mpuPinNums) / sizeof(int);

struct YPR
{
    int ypr[3];
};
std::map<int, YPR> yprMap;


void InitMPU(int pinNum);
void GetData(int pinNum);
void SendData();

void setup() {
  //Serial.begin(115200);
  BTSerial.begin(device_name); //Bluetooth device name

  YPR zeroYpr = {0, 0, 0};
  for(int i = 0; i < mpuNum; i++)
    yprMap.insert(std::pair<int,YPR>(mpuPinNums[i], zeroYpr));

  BTSerial.print("start init mpuNum : ");
  BTSerial.println(mpuNum);

  for (int i = 0; i < mpuNum;i++)
  {
    BTSerial.print("set pin output : ");
    BTSerial.println(mpuPinNums[i]);
    pinMode(mpuPinNums[i], OUTPUT);

  }

  // join I2C bus (I2Cdev library doesn't do this automatically)
  #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
      Wire.begin();
      Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
  #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
      Fastwire::setup(400, true);
  #endif

  for (int i = 0; i < mpuNum;i++)
    InitMPU(mpuPinNums[i]);
}

void loop() {
  for (int i = 0; i < mpuNum;i++)
    GetData(mpuPinNums[i]);

  SendData();
}




void InitMPU(int pinNum)
{
  for (int i = 0; i < mpuNum; i++)
  {
    if (mpuPinNums[i] == pinNum)
      digitalWrite(mpuPinNums[i], LOW);  //set selected mpu6050 addr 0x68
    else
      digitalWrite(mpuPinNums[i], HIGH);  //set other mpu6050 addr 0x69
  }

  // initialize device
  mpu.initialize();

  // verify connection
  BTSerial.println(F("Testing device connections..."));
  BTSerial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  BTSerial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788); // 1688 factory default for my test chip

  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
      // turn on the DMP, now that it's ready
      BTSerial.println(F("Enabling DMP..."));
      mpu.setDMPEnabled(true);

      // enable Arduino interrupt detection
      BTSerial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
      attachInterrupt(0, dmpDataReady, RISING);
      //attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);

      mpuIntStatus = mpu.getIntStatus();

      // set our DMP Ready flag so the main loop() function knows it's okay to use it
      BTSerial.println(F("DMP ready! Waiting for first interrupt..."));
      dmpReady = true;

      // get expected DMP packet size for later comparison
      packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
      BTSerial.print(F("DMP Initialization failed (code "));
      BTSerial.print(devStatus);
      BTSerial.println(F(")"));
  }
}


void GetData(int pinNum)
{
  for (int i = 0; i < mpuNum; i++)
  {
    if (mpuPinNums[i] == pinNum)
      digitalWrite(mpuPinNums[i], LOW);  //set selected mpu6050 addr 0x68
    else
      digitalWrite(mpuPinNums[i], HIGH);  //set other mpu6050 addr 0x69
  }

  // if programming failed, don't try to do anything
  if (!dmpReady) return;

  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
      // reset so we can continue cleanly
      mpu.resetFIFO();
  // otherwise, check for DMP data ready interrupt (this should happen frequently)
  } else if (mpuIntStatus & 0x02) {
    // wait for correct available data length, should be a VERY short wait
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    
    // track FIFO count here in case there is > 1 packet available
    fifoCount -= packetSize;

    #ifdef OUTPUT_READABLE_YAWPITCHROLL
      // display Euler angles in degrees
      mpu.dmpGetQuaternion(&q, fifoBuffer);
      mpu.dmpGetGravity(&gravity, &q);
      mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
      
      YPR tmpYpr;
      tmpYpr.ypr[0] = int(ypr[0] * 180/M_PI);
      tmpYpr.ypr[1] = int(ypr[1] * 180/M_PI);
      tmpYpr.ypr[2] = int(ypr[2] * 180/M_PI);

      yprMap[pinNum] = tmpYpr;
    #endif
  }
}


void SendData()
{
  for (int i = 0; i < mpuNum; i++)
  {
    BTSerial.print(yprMap[mpuPinNums[i]].ypr[0]);
    BTSerial.print(",");
    BTSerial.print(yprMap[mpuPinNums[i]].ypr[1]);
    BTSerial.print(",");
    BTSerial.print(yprMap[mpuPinNums[i]].ypr[2]);
    if(i == mpuNum - 1)
      BTSerial.print("/");
    else
      BTSerial.print(":");
  }
}

 

 

생각한데로 잘 나오긴하는듯하다 

언리얼로 ㄱㄱ

 

 

 

 

 

이전에 언리얼 제어한다고 썻던 블루프린트 파일인데

 

맨 위에잇는 set actor rotation으로 액터 회전을 시켰엇다.

 

 

지금은 mpu6050 3개로 3개의 ypr 데이터를 동시에 가져오고 있으니

따로 회전시키려고

bp_model 3개를 만들어서 다루려고함.

 

 

 

 

 

블루투스 시리얼 데이터를

/로 구분시켜놨는데

ypr 데이터가 3개가 있어 길어졋으므로

이전에는 30개씩 잘라썻다면 중간에 어중간하게 잘려서 잘못 만들어질수 있으니

이번엔 길이를 넉넉하게 170씩 자르도록 수정

 

 

 

 

 

일단 firstyprstring을 그대로 출력시키면

값이 잘 나오긴한다.

* esp32를 쓰면서 com포트는 12, 보드레이트는 115200으로 변경

 

 

 

초기화 루틴에서

actor 가져오도록하고

 

YPR 문자열 가져온뒤에

:로 분할해서

액터 로테이션 설정해주도록 수정하면

 

 

 

보드레이트를 115200으로 해서그런가

빠르게 동작해서 그런지 보간을 안줘도 꽤 부드럽게 회전한다

 

 

 

이전 글에서 

MPU6050 YPR 가져와서 블루투스로 통신하는걸 했는데

 

우노보드때와 다르게

보조베터리 전원을 사용해도 정상적으로 데이터 송신하는걸 확인할수 있었다.

 

이번 글을 작성하면서 MPU6050 여러개 연결해서 해보려고하는데

AD0로 주소를 두가지만 선택할수 있다보니 이전에 사용한 방법을 그대로하려고한다.

 

 

아래 핀맵을 보면

GPIO 번호랑 흰색 박스에 번호가 따로있어서

GPIO 번호를 쓰면 되는지 햇갈리는데

 

 

다음 링크를 참고해보니 GPIO 번호를 사용해보면 될듯하다.

 

https://blog.naver.com/mapes_khkim/221905932214

 

ESP32 사용하기(ARDUINO IDE)

ESP32 사용하기(ARDUINO IDE) ESP32 이란? ESP8266은 WIFI가 가능한 꽤 성능이 좋...

blog.naver.com

 

 

 

근데 ESP32 38핀 짜리 GPIO는 번호가 순서대로 가느네 아니라 뒤죽박죽 되있어서 좀햇갈리긴한다.

 

 

 

 

아쉬운데로 파워포인트로 대충 선 그렸는데

이런 느낌으로 배선하고

(mpu6050 vcc, gnd 제외)

 

 

 

 

 

이전 코드 참고해서 만들었는데

 

#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
//MPU6050 mpu(0x69); // <-- use for AD0 high

#define OUTPUT_READABLE_YAWPITCHROLL

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}


#include "BluetoothSerial.h"
String device_name = "ESP32-BT-Slave";

// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
  #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
  #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif

BluetoothSerial BTSerial;
int mpuPinNums[] = {36, 39, 34};
int mpuNum = sizeof(mpuPinNums) / sizeof(int);

void InitMPU(int pinNum);
void SendData(int pinNum);


void setup() {
  Serial.begin(115200);
  BTSerial.begin(device_name); //Bluetooth device name

  for (int i = 0; i < mpuNum;i++)
    pinMode(mpuPinNums[i], OUTPUT);

  // join I2C bus (I2Cdev library doesn't do this automatically)
  #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
      Wire.begin();
      Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
  #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
      Fastwire::setup(400, true);
  #endif

  for (int i = 0; i < mpuNum;i++)
    InitMPU(mpuPinNums[i]);
}

void loop() {
  for (int i = 0; i < mpuNum;i++)
    SendData(mpuPinNums[i]);

  Serial.println("");


}




void InitMPU(int pinNum)
{
  for (int i = 0; i < mpuNum; i++)
  {
    if (mpuPinNums[i] == pinNum)
      digitalWrite(mpuPinNums[i], LOW);  //set selected mpu6050 addr 0x68
    else
      digitalWrite(mpuPinNums[i], HIGH);  //set other mpu6050 addr 0x69
  }
  Serial.print("start init pin ");
  Serial.println(pinNum);

  // initialize device
  mpu.initialize();

  // verify connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788); // 1688 factory default for my test chip

  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
      // turn on the DMP, now that it's ready
      Serial.println(F("Enabling DMP..."));
      mpu.setDMPEnabled(true);

      // enable Arduino interrupt detection
      Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
      attachInterrupt(0, dmpDataReady, RISING);
      //attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);

      mpuIntStatus = mpu.getIntStatus();

      // set our DMP Ready flag so the main loop() function knows it's okay to use it
      Serial.println(F("DMP ready! Waiting for first interrupt..."));
      dmpReady = true;

      // get expected DMP packet size for later comparison
      packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
      Serial.print(F("DMP Initialization failed (code "));
      Serial.print(devStatus);
      Serial.println(F(")"));
  }
}


void SendData(int pinNum)
{
  for (int i = 0; i < mpuNum; i++)
  {
    if (mpuPinNums[i] == pinNum)
      digitalWrite(mpuPinNums[i], LOW);  //set selected mpu6050 addr 0x68
    else
      digitalWrite(mpuPinNums[i], HIGH);  //set other mpu6050 addr 0x69
  }

  // if programming failed, don't try to do anything
  if (!dmpReady) return;

  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
      // reset so we can continue cleanly
      mpu.resetFIFO();
  // otherwise, check for DMP data ready interrupt (this should happen frequently)
  } else if (mpuIntStatus & 0x02) {
    // wait for correct available data length, should be a VERY short wait
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    
    // track FIFO count here in case there is > 1 packet available
    fifoCount -= packetSize;

    #ifdef OUTPUT_READABLE_YAWPITCHROLL
        // display Euler angles in degrees
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        Serial.print(pinNum);
        Serial.print(":");
        Serial.print(int(ypr[0] * 180/M_PI));
        Serial.print(",");
        Serial.print(int(ypr[1] * 180/M_PI));
        Serial.print(",");
        Serial.print(int(ypr[2] * 180/M_PI));
        Serial.print("/");
    #endif
  }
}

 

 

 

GPIO를 인풋모드로밖에 못쓴다면서

데이터가 가끔 조금 나왔다가 말고

제대로 안나온다.

 

 

 

왜이런가 싶어 구글링해보니 이쪽 핀들은 인풋으로 밖에 못쓴다고하네

https://www.upesy.com/blogs/tutorials/esp32-pinout-reference-gpio-pins-ultimate-guide

 

 

 

문제 고치고 시리얼로 출력해보면

#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
//MPU6050 mpu(0x69); // <-- use for AD0 high

#define OUTPUT_READABLE_YAWPITCHROLL

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}


#include "BluetoothSerial.h"
String device_name = "ESP32-BT-Slave";

// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
  #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
  #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif

BluetoothSerial BTSerial;
int mpuPinNums[] = {32, 33, 25};
int mpuNum = sizeof(mpuPinNums) / sizeof(int);

void InitMPU(int pinNum);
void SendData(int pinNum);


void setup() {
  Serial.begin(115200);
  BTSerial.begin(device_name); //Bluetooth device name

  Serial.print("start init mpuNum : ");
  Serial.println(mpuNum);

  for (int i = 0; i < mpuNum;i++)
  {
    Serial.print("set pin output : ");
    Serial.println(mpuPinNums[i]);
    pinMode(mpuPinNums[i], OUTPUT);

  }

  // join I2C bus (I2Cdev library doesn't do this automatically)
  #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
      Wire.begin();
      Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
  #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
      Fastwire::setup(400, true);
  #endif

  for (int i = 0; i < mpuNum;i++)
    InitMPU(mpuPinNums[i]);
}

void loop() {
  for (int i = 0; i < mpuNum;i++)
    SendData(mpuPinNums[i]);

  Serial.println("");


}




void InitMPU(int pinNum)
{
  for (int i = 0; i < mpuNum; i++)
  {
    if (mpuPinNums[i] == pinNum)
      digitalWrite(mpuPinNums[i], LOW);  //set selected mpu6050 addr 0x68
    else
      digitalWrite(mpuPinNums[i], HIGH);  //set other mpu6050 addr 0x69
  }

  // initialize device
  mpu.initialize();

  // verify connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788); // 1688 factory default for my test chip

  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
      // turn on the DMP, now that it's ready
      Serial.println(F("Enabling DMP..."));
      mpu.setDMPEnabled(true);

      // enable Arduino interrupt detection
      Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
      attachInterrupt(0, dmpDataReady, RISING);
      //attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);

      mpuIntStatus = mpu.getIntStatus();

      // set our DMP Ready flag so the main loop() function knows it's okay to use it
      Serial.println(F("DMP ready! Waiting for first interrupt..."));
      dmpReady = true;

      // get expected DMP packet size for later comparison
      packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
      Serial.print(F("DMP Initialization failed (code "));
      Serial.print(devStatus);
      Serial.println(F(")"));
  }
}


void SendData(int pinNum)
{
  for (int i = 0; i < mpuNum; i++)
  {
    if (mpuPinNums[i] == pinNum)
      digitalWrite(mpuPinNums[i], LOW);  //set selected mpu6050 addr 0x68
    else
      digitalWrite(mpuPinNums[i], HIGH);  //set other mpu6050 addr 0x69
  }

  // if programming failed, don't try to do anything
  if (!dmpReady) return;

  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
      // reset so we can continue cleanly
      mpu.resetFIFO();
  // otherwise, check for DMP data ready interrupt (this should happen frequently)
  } else if (mpuIntStatus & 0x02) {
    // wait for correct available data length, should be a VERY short wait
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    
    // track FIFO count here in case there is > 1 packet available
    fifoCount -= packetSize;

    #ifdef OUTPUT_READABLE_YAWPITCHROLL
        // display Euler angles in degrees
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        Serial.print(pinNum);
        Serial.print(":");
        Serial.print(int(ypr[0] * 180/M_PI));
        Serial.print(",");
        Serial.print(int(ypr[1] * 180/M_PI));
        Serial.print(",");
        Serial.print(int(ypr[2] * 180/M_PI));
        Serial.print("/");
    #endif
  }
}

 

 

 

 

 

잘 나오기는한데 뭔가 좀 이상하다.

그냥 대충 전역 딕셔너리 이용해서 쓰고싶은데

 

 

 

구글링으로 esp32에서 map 사용하는 내용 찾았다.

이걸 쓰기전에 먼저 \n이 왜 자주발생하는지부터 찾아서 해결하고 넘어가야겠다.

https://techtutorialsx.com/2021/11/02/esp32-the-cpp-map-container/

 

ESP32: The C++ map container - techtutorialsx

How to execute some basic operations on a std::map container. We will be using the ESP32 and the Arduino

techtutorialsx.com

 

 

중간에 핀번호랑 인터럽트 상태 등 출력하도록 잠깐추가

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  Serial.print(pinNum);
  Serial.print("pin dmpReady : ");
  Serial.print(", after mpuIntStatus : ");
  Serial.print(mpuIntStatus);
  Serial.print(", fifocount : ");
  Serial.println(fifoCount);

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
      // reset so we can continue cleanly
      mpu.resetFIFO();

 

 

 

지금은또 지저분하긴하지만 멀정하게 잘나오는듯하다.

 

 

지우고봐도 값은 정상적인거같은데

왜이렇게 println이 많이 적용된건진 모르겟다.

이렇게 많이 시키진않았다만

 

 

대충 값은 잘들어오니 그냥 맵에 저장하도록 수정

map 사용하려고하는데 value 자리에 int[3]이 잘안된다.

 

아래 링크에서 배열 대신 구조체 쓰는내용을 봐서 수정

https://stackoverflow.com/questions/2582529/how-can-i-use-an-array-as-map-value

 

How can I use an array as map value?

I'm trying to create a map, where the key is an int, and the value is an array as follows: int red[3] = {1,0,0}; int green[3] = {0,1,0}; int blue[3] = {0,0,1}; std::map<int, int[3]> colou...

stackoverflow.com

 

struct YPR
{
    int ypr[3];
};
std::map<int, YPR> yprMap;


void InitMPU(int pinNum);
void SendData(int pinNum);

void setup() {
  Serial.begin(115200);
  BTSerial.begin(device_name); //Bluetooth device name

  YPR zeroYpr = {0, 0, 0};
  for(int i = 0; i < mpuNum; i++)
    yprMap.insert(std::pair<int,YPR>(mpuPinNums[i], zeroYpr));

 

 

 

 

블루투스로 보내도록 전체코드 정리

#include "I2Cdev.h"
#include <map>

#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

MPU6050 mpu;

#define OUTPUT_READABLE_YAWPITCHROLL

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}


#include "BluetoothSerial.h"
String device_name = "ESP32-BT-Slave";

// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
  #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
  #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif

BluetoothSerial BTSerial;
int mpuPinNums[] = {32, 33, 25};
int mpuNum = sizeof(mpuPinNums) / sizeof(int);

struct YPR
{
    int ypr[3];
};
std::map<int, YPR> yprMap;


void InitMPU(int pinNum);
void GetData(int pinNum);
void SendData();

void setup() {
  //Serial.begin(115200);
  BTSerial.begin(device_name); //Bluetooth device name

  YPR zeroYpr = {0, 0, 0};
  for(int i = 0; i < mpuNum; i++)
    yprMap.insert(std::pair<int,YPR>(mpuPinNums[i], zeroYpr));

  BTSerial.print("start init mpuNum : ");
  BTSerial.println(mpuNum);

  for (int i = 0; i < mpuNum;i++)
  {
    BTSerial.print("set pin output : ");
    BTSerial.println(mpuPinNums[i]);
    pinMode(mpuPinNums[i], OUTPUT);

  }

  // join I2C bus (I2Cdev library doesn't do this automatically)
  #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
      Wire.begin();
      Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
  #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
      Fastwire::setup(400, true);
  #endif

  for (int i = 0; i < mpuNum;i++)
    InitMPU(mpuPinNums[i]);
}

void loop() {
  for (int i = 0; i < mpuNum;i++)
    GetData(mpuPinNums[i]);

  SendData();
}




void InitMPU(int pinNum)
{
  for (int i = 0; i < mpuNum; i++)
  {
    if (mpuPinNums[i] == pinNum)
      digitalWrite(mpuPinNums[i], LOW);  //set selected mpu6050 addr 0x68
    else
      digitalWrite(mpuPinNums[i], HIGH);  //set other mpu6050 addr 0x69
  }

  // initialize device
  mpu.initialize();

  // verify connection
  BTSerial.println(F("Testing device connections..."));
  BTSerial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  BTSerial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788); // 1688 factory default for my test chip

  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
      // turn on the DMP, now that it's ready
      BTSerial.println(F("Enabling DMP..."));
      mpu.setDMPEnabled(true);

      // enable Arduino interrupt detection
      BTSerial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
      attachInterrupt(0, dmpDataReady, RISING);
      //attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);

      mpuIntStatus = mpu.getIntStatus();

      // set our DMP Ready flag so the main loop() function knows it's okay to use it
      BTSerial.println(F("DMP ready! Waiting for first interrupt..."));
      dmpReady = true;

      // get expected DMP packet size for later comparison
      packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
      BTSerial.print(F("DMP Initialization failed (code "));
      BTSerial.print(devStatus);
      BTSerial.println(F(")"));
  }
}


void GetData(int pinNum)
{
  for (int i = 0; i < mpuNum; i++)
  {
    if (mpuPinNums[i] == pinNum)
      digitalWrite(mpuPinNums[i], LOW);  //set selected mpu6050 addr 0x68
    else
      digitalWrite(mpuPinNums[i], HIGH);  //set other mpu6050 addr 0x69
  }

  // if programming failed, don't try to do anything
  if (!dmpReady) return;

  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
      // reset so we can continue cleanly
      mpu.resetFIFO();
  // otherwise, check for DMP data ready interrupt (this should happen frequently)
  } else if (mpuIntStatus & 0x02) {
    // wait for correct available data length, should be a VERY short wait
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    
    // track FIFO count here in case there is > 1 packet available
    fifoCount -= packetSize;

    #ifdef OUTPUT_READABLE_YAWPITCHROLL
      // display Euler angles in degrees
      mpu.dmpGetQuaternion(&q, fifoBuffer);
      mpu.dmpGetGravity(&gravity, &q);
      mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
      
      YPR tmpYpr;
      tmpYpr.ypr[0] = int(ypr[0] * 180/M_PI);
      tmpYpr.ypr[1] = int(ypr[1] * 180/M_PI);
      tmpYpr.ypr[2] = int(ypr[2] * 180/M_PI);

      yprMap[pinNum] = tmpYpr;
    #endif
  }
}


void SendData()
{
  for (int i = 0; i < mpuNum; i++)
  {
    BTSerial.print(mpuPinNums[i]);
    BTSerial.print(":");
    BTSerial.print(yprMap[mpuPinNums[i]].ypr[0]);
    BTSerial.print(",");
    BTSerial.print(yprMap[mpuPinNums[i]].ypr[1]);
    BTSerial.print(",");
    BTSerial.print(yprMap[mpuPinNums[i]].ypr[2]);
    if(i == mpuNum - 1)
      BTSerial.println("");
    else
      BTSerial.print("/");
  }
}

 

 

 

 

 

esp32 보조베터리 전원으로 mpu6050 ypr 송신하기

좌측 중간 오른쪽

32    33    25

 

순서인데

중간(초록불)에 있는 mpu6050은 이번에 알리에서 산게아닌 전에 있던거라 그런지

y 값이 혼자 다르다.

어쨋든 보조베터리 블루투스로 전체 데이터 빠르게 송신하니 됫지

 

 

일단 이전 글에서 ESP32로 어떻게 블루투스 통신하는지는 살펴봤다.

 

이제 드뎌 MPU6050달아서 좀해보려고하는데

 

ESP32가 버전이 여러개있으면서 핀맵이 조금씩 다른것같다.

 

내가갖고있는건 아래거랑 비슷하지만 살짝다른데

 

 

 

 

 

핀 내용이 뒷면에 있어서 빵판에 꽂으니 뭐가 뭔핀인지 모르겟다.

저사진 보고 따라가도 되나싶기도하고

구매한 사이트에서 확인하니 맞는것같다.

 

 

잠깐 찾아보니

이 글에서 esp32 gpio 설명을 잘 적어주긴했는데

 

30핀 기준인것같아 내꺼랑 맞는지는 좀봐야겟다.

 

https://mmirann.github.io/iot/arduino/2021/02/08/ESP32_GPIO.html

 

[ESP32] ESP32 GPIO PIN 별 사용법 | Miran Lee 👩‍💻

ESP 모듈은 블루투스, 와이파이를 기본적으로 사용할 수 있으며 36개의 GPIO가 사용 가능하다. 또한 별도로 보드 라이브러리를 설치하기만 하면 아두이노 IDE로 업로드가 가능하다. NodeMCU 보드는 이

mmirann.github.io

 

 

 

ESP32 30핀짜리 핀맵

https://mmirann.github.io/iot/arduino/2021/02/08/ESP32_GPIO.html

 

 

 

ESP32 30핀짜리에는 GPIO21, GPIO22가 I2C 통신을 위해 사용되는데

 

 

 

 

 

앞서 찾은 38핀 보드 핀아웃에는 i2c 핀 내용이 안보여서

다른 38핀 핀아웃도 찾아봄

 

GPIO 21, 22가 맞나부다.

 

 

https://www.hwkitchen.cz/esp32-devkitc-38pin-espwroom32-wifi-bt/

 

https://www.studiopieters.nl/esp32-pinout/

 

 

 

일단 위 핀맵 참고해서

I2C 통신할수 있도록 연결했다.

 

3V3은 빨간색

GND 파랑,어두운색

SCL 주황

SDA 노랑

 

 

 

 

esp32에서 mpu6050어떻게 쓸까 찾아보다가

찾은 글을 보니

내가 썻던 mpu6050 코드랑 거의 비슷해서 기존걸 사용하려햇는데

https://dev.to/shilleh/measure-angles-with-mpu6050-and-esp32-part-2-3d-animation-1cmm

 

Measure Angles with MPU6050 and ESP32 (Part 2): 3D Animation

Learn how to visualize the MPU6050 angle data from the i2cdevlib in the Arduino Framework using...

dev.to

 

아두이노에서 사용할때랑 다르게

이 에러가 발생했다.

 

esp32는 avr이 아니니 뜨는게 당연하긴한데

구글링하니 어캐고치는지 찾음

 

https://github.com/TheThingsNetwork/arduino-device-lib/issues/241

 

 

 

그리고 아까 진행하다가

i2c 무슨 에러낫엇는데 캡처를 안하고 넘어가서 뭐엿는지 기억안난다.

 

여기서 시킨데로 i2cdev만 새로 다운받은걸로 교체

했더니 그냥 넘어가졋다.

 

https://blog.naver.com/PostView.nhn?blogId=heennavi1004&logNo=222342645959&parentCategoryNo=&categoryNo=86&viewDate=&isShowPopularPosts=false&from=postView

 

[ESP32S]ESP32S 에서 MPU6050 사용시 에러 발생

해결: 하단부의 결론 부분에 정리를 하였다. 그 부분만 보면 된다. -------------------------------------...

blog.naver.com

 

 

 

 

mpu6050 코드 업로드시키고 리셋했더니

자꾸 dmp 초기화 실패가 나온다.

 

 

 

 

원인 찾아보려고하는데

공급전압 문제라는 사람도있긴하지만

i2c scanner로 한번 스캐닝해보라는 글찾앗다.

 

https://stackoverflow.com/questions/26702283/mpu6050-dmp-does-not-work

 

 

대충 esp32 i2c scanner 찾아서 확인

 

https://wise-self.tistory.com/84

 

I2C Scanning on ESP32 using Arduino IDE

ESP32의 기본 I2C 핀은 다음과 같습니다. GPIO 22 (SCL), GPIO 21 (SDA) 이건 I2C 부품 주소 스캐닝 코드입니다. #include void setup() { Wire.begin(); Serial.begin(115200); Serial.println("\nI2C Scanner"); } void loop() { byte error, add

wise-self.tistory.com

 

 

i2c가 정상적으로 주소 찾긴했다.

 

 

 

 

 

내가 뭘 실수했을까 다시보다보니

setup에서 i2c 초기화 부분을 제대로 안넣어뒀더라--

  // join I2C bus (I2Cdev library doesn't do this automatically)
  #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
      Wire.begin();
      Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
  #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
      Fastwire::setup(400, true);
  #endif

 

 

 

다시 업로드하고 실행시켯는데

dmp 초기화는 잘된것같지만 자세 출력이 안된다..

 

 

 

 

 

코드 중간에 mpu interrupt 기다리는 내용이 있어

여기서 막힌것으로 보인다.

  // wait for MPU interrupt or extra packet(s) available
  while (!mpuInterrupt && fifoCount < packetSize) {
  }

  // reset interrupt flag and get INT_STATUS byte
  mpuInterrupt = false;

 

 

이 부분을 지우고 나면 잘 동작

어짜피 인터럽트 안써도 대충 원하는대로 동작하므로

 

 

대충 FIFO가 가득차서 리프레시 할떄 뺴고는 잘나온다.

#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
//MPU6050 mpu(0x69); // <-- use for AD0 high

#define OUTPUT_READABLE_YAWPITCHROLL

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };


volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}







#include "BluetoothSerial.h"

String device_name = "ESP32-BT-Slave";

// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
  #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
  #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif

BluetoothSerial BTSerial;

void setup() {
  // join I2C bus (I2Cdev library doesn't do this automatically)
  #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
      Wire.begin();
      Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
  #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
      Fastwire::setup(400, true);
  #endif


  BTSerial.begin(device_name); //Bluetooth device name
  Serial.begin(115200);



  // initialize device
  mpu.initialize();

  // verify connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788); // 1688 factory default for my test chip

  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
      // turn on the DMP, now that it's ready
      Serial.println(F("Enabling DMP..."));
      mpu.setDMPEnabled(true);

      // enable Arduino interrupt detection
      Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
      attachInterrupt(0, dmpDataReady, RISING);
      //attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);

      mpuIntStatus = mpu.getIntStatus();

      // set our DMP Ready flag so the main loop() function knows it's okay to use it
      Serial.println(F("DMP ready! Waiting for first interrupt..."));
      dmpReady = true;

      // get expected DMP packet size for later comparison
      packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
      Serial.print(F("DMP Initialization failed (code "));
      Serial.print(devStatus);
      Serial.println(F(")"));
  }





}

void loop() {


  // if programming failed, don't try to do anything
  if (!dmpReady) return;

  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();
  /*
  Serial.print(", after mpuIntStatus : ");
  Serial.print(mpuIntStatus);
  Serial.print(", fifocount : ");
  Serial.println(fifoCount);
  */
  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
      // reset so we can continue cleanly
      mpu.resetFIFO();

  // otherwise, check for DMP data ready interrupt (this should happen frequently)
  } else if (mpuIntStatus & 0x02) {
    // wait for correct available data length, should be a VERY short wait
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    
    // track FIFO count here in case there is > 1 packet available
    // (this lets us immediately read more without waiting for an interrupt)
    fifoCount -= packetSize;

    #ifdef OUTPUT_READABLE_YAWPITCHROLL
        // display Euler angles in degrees
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        //Serial.print(pinNum);
        //Serial.print(":");
        Serial.print(int(ypr[0] * 180/M_PI));
        Serial.print(",");
        Serial.print(int(ypr[1] * 180/M_PI));
        Serial.print(",");
        Serial.print(int(ypr[2] * 180/M_PI));
        Serial.print("/");
    #endif
  }
  delay(20);
}

 

 

 

 

 

 

 

 

이제 블루투스로 넘어가서 동작시켜보자

위 코드를 블루투스 시리얼로 고친결과 잘 나온다.

 

문제는 보조베터리 상태에서도 잘되느냐

 

 

 

 

와 이제 보조베터리 전원으로도 

블루투스 통해서 빠르게 ypr 값을 가져올수 있게됫다. 

 

 

 

ESP-WROOM-32 핀아웃

https://www.theengineeringprojects.com/2020/12/esp32-pinout-datasheet-features-applications.html

 

 

스펙

https://www.theengineeringprojects.com/2020/12/esp32-pinout-datasheet-features-applications.html

 

MPU6050은 원래 3.3V 사용하니까 

3v3 핀을 vcc로 주면될듯

 

 

처음 쓰므로 대충 다음 링크 참고

https://makerspace.steamedu123.com/entry/ESP32-ESP32-%EC%95%84%EB%91%90%EC%9D%B4%EB%85%B8-IDE-%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

[ESP32] ESP32 아두이노 IDE 에서 사용하기

본 절은 ESP32 를 사용하기 위해 알아야 할 내용과 실습 방법에 대해 설명한다. ESP32 특징, 동작원리, 사양, 연결 핀 배열, 출력 값, 주의사항을 알아본다. ESP32를 연결하고, 간단한 코딩으로 쉽게 실

makerspace.steamedu123.com

 

https://www.bneware.com/blogPost/esp32_arduino_ide

 

ESP32 아두이노 IDE 에서 사용하기

 

www.bneware.com

 

 

 

 

처음쓰면서 업로드 설정이 뭔가 복잡해보이는데

 ESP32 Dev Module에 장치관리자에 나온 COM10포트 지정해줫더니 

대충 올라가지긴한다.

 

 

 

 

간단하게

아래 링크참고해서 블루투스 예제 가져와서 사용해봄

https://m.blog.naver.com/heennavi1004/221799043375

 

[arduino] ESP32 Bluetooth 통신

[arduino] ESP32 Bluetooth 통신 20200206 ESP32와 arduino로 bluetooth 통신을 해본다. 예제의 Se...

blog.naver.com

 

업로드한 블루투스 예제

// This example code is in the Public Domain (or CC0 licensed, at your option.)
// By Evandro Copercini - 2018
//
// This example creates a bridge between Serial and Classical Bluetooth (SPP)
// and also demonstrate that SerialBT have the same functionalities of a normal Serial
// Note: Pairing is authenticated automatically by this device

#include "BluetoothSerial.h"

String device_name = "ESP32-BT-Slave";

// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
  #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
  #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(115200);
  SerialBT.begin(device_name); //Bluetooth device name
  //SerialBT.deleteAllBondedDevices(); // Uncomment this to delete paired devices; Must be called after begin
  Serial.printf("The device with name \"%s\" is started.\nNow you can pair it with Bluetooth!\n", device_name.c_str());
}

void loop() {
  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
  if (SerialBT.available()) {
    Serial.write(SerialBT.read());
  }
  delay(20);
}

 

 

 

hc-05랑 다르게 비번 설정없이 바로 BT로 연결가능

 

추가 블루투스 옵션에 들어가서

컴포트 확인해보면 COM12가 송신으로 되어있음

 

 

COM12

 

 

 

 

 

시리얼, 블루투스 둘다 115200으로 열어서

키입력하면 코드넣은대로

상대편에 보내는걸 확인할수 있다.

 

 

+ Recent posts