Gimbal Assistant
아두이노를 통해 짐벌 어시스턴트를 만들 계획을 잡았으니, 세부적으로 뭘 해야 하는지에 대한 계획이 필요할 것 같습니다. 우선 필요한 기능은 크게 아래의 5가지로 나누어질것 같습니다. 아마...5번 기능은 구현하기 어려울 것 같다는 생각이 자꾸만 듭니다만.. 제가 NT 모듈의 기능을 완벽히 이해하고 있는 상황도 아니고, 괜히 아두이노를 NT 모듈로 개조한다 하더라도 배선이 복잡해질 여지가 있어서 5번 기능은 고민을 좀 해봐야 할 것 같습니다.
*짐벌에 필요한 기능들:
1. 촬영 경과 시간 표시하기
2. 배터리 전압 표시하기
3. 배터리 잔량 퍼센티지로 표시하기
4. 조이스틱 방향 표시하기
5. 2nd imu의 값을 받아 nt로 bgc에 전달하기
이번 포스팅에서는 가장 기본 중의 기본이 되는 촬영 경과 시간을 표시해보도록 하겠습니다. 아두이노는 확장성이 좋은 보드이기에 필요에 따른 기능을 가지고 있는 모듈과 센서들을 쉽게 찾아볼 수 있습니다. 아두이노에서 시간에 관해 논할 때 빠지지 않는 것이 바로 "RTC(Real-Time-Clock)" 모듈인데요. 이 모듈은 모듈에 수은 배터리가 장착되어 있어 시간을 계속해서 저장해주는 역할을 합니다. 보드가 꺼져 있어도 오랜 기간동안 시간을 잃지 않는 셈이지요. 컴퓨터에 수은 배터리가 장착되어 있어 CMOS 세팅을 저장하고 있는 것과도 비슷한 맥락이라 볼 수 있습니다. 생각해보면 제가 만드는 짐벌에 굳이 시간 정보까지 표현할 필요는 없는 것 같습니다. 0.91인치의 작은 스크린에 경과 시간을 띄우는 것만으로도 벅찰 것 같습니다. 그래서 RTC모듈을 사용하지는 않을 겁니다. 대신, 아두이노의 내장 함수 millis()를 사용해 간단한 스톱워치를 만들어 짐벌이 켜진 상태부터 경과된 시간을 스크린에 표시하는 코드를 작성해보겠습니다.
millis()?
millis()함수는 아두이노의 내장함수로, 아두이노가 실행된 이후부터의 시간을 ms 단위로 반환하는 함수입니다. 아두이노가 켜진 후 1초가 지났다면 millis()의 반환값은 1000이 되겠죠? 이 함수를 이용하면 (아두이노가 꺼지지 않는 한) 시간을 다룰 수 있기 때문에 잘 활용한다면 delay의 역할을 하면서 스레드 개념이 없는 아두이노에서 동시 작업 역시 가능하도록 만들어볼 수도 있습니다. millis()를 통해서 표현할 수 있는 시간은 약 50일 정도(unsigned long자료형 사용)로 알려져 있으며, 이 이상을 넘어가면 over-flow가 나서 오류가 발생하게 됩니다.
스톱워치 만들기
millis함수를 활용해 우선 밀리초를 초로 변환하겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | int sec = 0; int min = 0; int hour =0; int counter =0; void setup(){ Serial.begin(9600); } void loop(){ sec = millis()/1000-60*counter; if(sec ==60) {counter++; min++;} if(min ==60) {min = 0; hour++;} Serial.print(hour); Serial.print(" "); Serial.print(min); Serial.print(" "); Serial.println(sec); } | cs |
위 코드대로 스케치를 작성하면 시리얼 모니터에 초 단위의 시간이 발생되는 것을 볼 수 있습니다. 60초가 넘어가면 초는 0초로 리셋되어야 하고, 1분이 늘어나야겠습니다. 조건문을 추가해주고, sec을 초기화시키기 위한 counter 변수를 추가해줍니다. 마찬가지로 60분이 넘어가면 분을 리셋해주고, 1시간을 추가해야겠죠. 이를 위한 조건문을 하나 더 추가시켜주면 스톱워치로서는 손색이 없습니다. 여기에 아두이노를 진짜 스톱워치로 쓸 예정이라면 시작과 정지 버튼을 추가해서 시작을 눌렀을 때의 millis() 값을 저장하고, 정지 버튼을 누를 때의 millis() 값을 받아와 두 값의 차를 구하면 아마도 경과 시간이 구해질 것 같습니다. 이런 식으로도 응용할 수 있겠죠?
ㅡ
저는 주 목적이 어디까지나 짐벌 어시스턴트를 위한 촬영 시간 확인에 지나지 않기 때문에 랩타임이나 시작/정지의 기능이 그다지 필요치 않습니다. 단순히 시간만 확인하면 되는 겁니다. 기능적 요소보다 더 중요한 것은 심미성이라 생각합니다. 텍스트를 배치하고, 디자인적 통일감을 주는 것이 더 중요한 것 같습니다. 우선 스톱워치로서의 기능 구현에는 성공했으니 128*64 OLED 스크린에 뿌리는 작업을 하겠습니다. 저는 OLED 스크린 사용에 SSD1306 라이브러리를 사용하고 있으며 u8glib를 사용하실 분들의 경우 나중에 올라올 포스팅을 참조하시면 될 것 같습니다.
SSD1306라이브러리에서 데이터를 스크린에 뿌리는 방법과 순서가 절대적이지는 않지만 분명 존재합니다. 우선 텍스트를 뿌리는 순서는 다음과 같습니다.
1. 글자 색 설정(setTextColor)
2. 글자 크기 설정(setTextSize)
3. 시작 위치 설정(setCursor)
4. 글자뿌리기(print, println)
5. 스크린에 표시(display)
*괄호 안의 단어들은 모두 메소드를 표현합니다.
위의 순서를 어느정도 지켜서 살을 덧붙여 OLED스크린에 뿌릴 코드를 짜보았습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> int sec = 0; int _min = 0; int hour =0; int counter =0; #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); void setup(){ display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); } void loop(){ sec = millis()/1000-60*counter; if(sec ==60) {counter++; _min++;} if(_min ==60) {_min = 0; hour++;} display.setTextColor(WHITE); display.setTextSize(1); display.setCursor(35,15); display.println("Stablizing "); delay(5); display.setTextSize(2); display.setCursor(18,30); if(hour<10) display.print("0"); display.print(hour); display.print(":"); if(_min<10) display.print("0"); display.print(_min); display.print(":"); if(sec<10) display.print("0"); display.println(sec); display.display(); display.clearDisplay(); } | cs |
이렇게 간단히(...) 코드를 짜고 업로드하면 경과 시간이 스크린에 뿌려지는 것을 볼 수 있습니다. 다른 것보다 첫번째 줄의 문구를 어떻게 해야할지가 정말 고민입니다. 원래 적었던 문구는 Recording이 좋을까요, Stablizing이 좋을까요 아니면 처음에 생각했던 대로 Time Elapsed가 좋을까요? 생각지도 못했던 고민이 생겼습니다. 아니면 5분 간격으로 세 문구를 바꿔서 표현하는 것도 재밌을 것 같습니다. 일단 이 문제는 보류해도 좋을 것 같습니다.
기본 인터페이스 구성하기
인터페이스라 해야 크게 유의미한 것은 없습니다. 위에서 말했던 것처럼 배터리 잔량과 전압, 조이스틱의 위치표시가 주 목적이 될 예정이고 또 5번의 목표가 성공하게 된다면 2nd imu(바디)의 기울기값 역시 표시할 수 있을 겁니다.일단 여기까지는 바라지도 않고, 배터리 잔량과 전압을 표시하기 위한 코드를 추가하겠습니다.
1 2 3 4 5 6 7 8 | //Batteray Percentage display.setTextSize(1); display.setCursor(3,3); display.println("8.75V"); display.setCursor(100,3); display.println("100%"); | cs |
이것도 마찬가지로 별 특별한 것은 없습니다... 다만 한가지 눈여겨 보실 게 있다면 조이스틱 이동방향 표시를 위해 외부이동 경로를 1픽셀씩 남겨줬다는 점 정도 있겠습니다.
ㅡ
아두이노를 활용해 짐벌 어시스턴트 프로그램을 제작중에 있습니다. 이번 포스팅에서는 아두이노가 켜진 상태에서 흘러간 시간을 측정하는 간단한 스톱워치 기능과 추후 배터리 데이터가 아두이노를 통해 들어올 것을 대비해 배터리 잔량과 전압을 표시하는 기본적인 인터페이스를 구성해 보았습니다. 누누히 말했듯 지금 제작중인 짐벌 어시스턴트가 2nd imu를 NT로 바꿔준다든지 하는 큰 부분을 차지하지 않기 때문에 짐벌 운용에 있어서 큰 역할을 하지는 않습니다. 그냥 쉬어가는 느낌, 추억 되살리는 느낌으로 프로그램 제작중입니다. 모델링보다 마음만은 한결 더 가볍군요. 다음 프로젝트를 뭘로 해야할지 어느정도 감도 잡히게 되는 좋은 계기가 되고 있습니다. 프린팅이 완성되면 그때부터는 아두이노를 본격적으로 활용할 수 있을 것만같아 벌써부터 기분이 좋습니다.
*짐벌 제작과정을 잠시 확인해 봤는데, camera_pitch와 pitch-roll 부분의 팔(Arm)의 강성은 여전히 떨어지지만, 우선은 사용할 수 있을 정도로 보입니다. roll-yaw 부분의 팔(Arm)은 강성이 충분히 확보되어 크게 걱정되지 않습니다. 강성이 걱정되는 부분들이 생각보다 좋은 강성을 보여줘서 다행이고, 나머지 부품들은 강성 걱정은 없으니 깔끔하게만 뽑아져서 수고로움을 덜었으면 하는 바램입니다(왠지 rod부분 서포트때문에 배터리가 안들어갈것만 같은 불안한 느낌이 지워지질 않습니다.)
감사합니다.