이 매뉴얼은 주식회사 제이케이이엠씨(JK EMC) 에 의해서 번역, 수정, 작성 되었고 소유권 또한
주 식회사 제이케이이엠씨(JK EMC)의 것입니다. 소유권자의 허가를 받지 않고 무단으로 수정, 삭제하거나 배포 할 수 없습니다.

 

 

ADXL345 3축 Accelerometer 가속도 GY-80 3.3/5V 레귤레이터 내장형 센서 메뉴얼


  * Update history

- 2016.8.3 : 프로세싱 코드 추가
- 2016.7.15 : 초기 Release


 
1. ADXL345 센서 소개
2. 아 두이노와 같이 사용하기
    2.1 센서 회로도및 외곽 치수
    2.2 아두이노 UNO R3 배선도
    2.3 아두이노 스케치 코드
3. 프로세싱을 이용해서 3차원 그래픽 표시
    3.1 프로세싱 설치 하기
    3.2 아두이노 UNO R3 배선도
    3.3 아두이노 스케치 코드
    3.4 프로세싱 코드



 

1. ADXL345 센서 소개

SPI와 I2C 인터페이스를 지원하는 아날로그 디바이스사의 저전력 디지털 3축 가속도 센서 이다. 아날로그 출력이 나오는 ADXL335센서보다 비교적 쉽게 사용이 가능하다.


Characteristic Value Unit
Supply Voltage 2.0 ~ 3.6 V
Supply Current 140 uA
Measuring range(Selectable) ±2, ±4, ±8, ±16 dps(deg/s)
Sensitivity FPS = ±2g 256 LSB/g
FPS = ±4g 128
FPS = ±6g 64
FPS = ±8g 30
Measuring Axis X, Y, Z  
Operating Temperature -40 ~ 85 °C



2. 아두이노와 같이 사용하기

2.1 센서 회로도및 외곽 치수

(1) ADXL345 센서 회로도
     - ADXL345 PDF 회로도 다운로드
     - ADXL345 데이터시트 다운로드(http://www.jkelec.co.kr)
     - ADXL345 DXF 캐드 파일 다운로드

mpu9250

(2) ADXL345 센서 외곽(mm단위) 치수

      atmega128

2.2 아두이노 UNO R3 배선도

(1) 아두이노 UNO R3와 연결해서 가속도, 자이로 센터 데이터 출력 하기
본 제품은 내부에 회로적으로 3.3V전원 레듈레이터와 I2C 레벨쉬프터를 내장하고 있어서 5.0V와 3.3V 에서 모두 사용이 가능 하다. STM32, 라즈베리파이 등과 같이 3.3V 전원을 사용하는 프로세서에서 사용할 경우에는 VCC 대신에 3.3V 핀을 통해서 전원을 바로 연결해 주면 된다.


     - Fritzing 파일 다운로드

mpu9250

 



2.3 아두이노 스케치 코드

단순히 센서의 데이터를 시리얼 데이터로 출력하는 예제 이다.

(1) 아두이노 스케치 코드
     - ADXL345 raw 데이터 표시 스캐치 파일 다운로드

// I2C device class (I2Cdev) demonstration Arduino sketch for ADXL345 class
// 10/7/2011 by Jeff Rowberg <jeff@rowberg.net>
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
//
// Changelog:
// 2011-10-07 - initial release
/* ============================================
I2Cdev device library code is placed under the MIT license
Copyright (c) 2011 Jeff Rowberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================
*/
// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#include "Wire.h"
// I2Cdev and ADXL345 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "ADXL345.h"
// class default I2C address is 0x53
// specific I2C addresses may be passed as a parameter here
// ALT low = 0x53 (default for SparkFun 6DOF board)
// ALT high = 0x1D
ADXL345 accel;
int16_t ax, ay, az;
#define LED_PIN 13 // (Arduino is 13, Teensy is 6)
bool blinkState = false;
void setup() {
// join I2C bus (I2Cdev library doesn't do this automatically)
Wire.begin();
// initialize serial communication
// (38400 chosen because it works as well at 8MHz as it does at 16MHz, but
// it's really up to you depending on your project)
Serial.begin(9600);
// initialize device
Serial.println("Initializing I2C devices...");
accel.initialize();
// verify connection
Serial.println("Testing device connections...");
Serial.println(accel.testConnection() ? "ADXL345 connection successful" : "ADXL345 connection failed");
// configure LED for output
pinMode(LED_PIN, OUTPUT);
}
void loop() {
// read raw accel measurements from device
accel.getAcceleration(&ax, &ay, &az);
// display tab-separated accel x/y/z values
Serial.print("accel:\t");
Serial.print(ax); Serial.print("\t");
Serial.print(ay); Serial.print("\t");
Serial.println(az);
// blink LED to indicate activity
blinkState = !blinkState;
digitalWrite(LED_PIN, blinkState);
}

ADXL345의 Slave Address는 기본적으로 0x53이고 본 제품에서도 AD0핀이 회로적으로 풀 다운 되어있다. 만약 ADXL345의 AD0핀을 풀업(VCC 3.3V 에 연결)을 시킨다면 Slave Address를 0x1D로 수정 하여야 한다.

#define ADXL345_ADDRESS_ALT_LOW 0x53

ADXL345 센서를 아두이노나 다른 마이크로 프로세서에서 동시에 2개를 사용해야 한다면 1개는 풀다운 그대로 사용을 하고 나머지 한개를 풀업 시켜서 Slave Address를 다르게 해서 사용을 하면 된다.

(2) 실행 결과 
ADXL345과의 I2C 통신 상태를 검사하고 3축의 센서 출력 값을 반복해서 보여준다.

mpu9250

본 메뉴얼의 아두이노 코드는 아래 https://github.com/jrowberg/i2cdevlib 의 내용을 참조하여 작성 하였다.

3. 프로세싱을 이용해서 3차원 그래픽 표시

3.1 프로세싱 설치 하기

아두이노를 이용해서 센서에서 데이터를 추출해서 시리얼 데이터로 출력을 해서 데이터 값을 확인을 쉽게 할 수 있었다. 그렇다면 텍스트 데이터가 아니라 조금더 시각적인 표현 방법이 없을까? 그것도 아주 쉽게.. 
프로세싱을 이용하면 아두이노 스케치 코드를 입력하듯이 아주 쉽게 시각적인 표현이 가능하다. 우선 프로세싱 개발 환경을 설치하는것부터 해보자.

(1) 프로세싱 설치
      - 프로세싱 개발환경 설치 하기(새창)

3.2 아두이노 UNO R3 배선도

배선도는 이전 예제와 동일하다.

mpu6050

3.3 아두이노 스케치 코드(3D)

(1) 아두이노 프로세싱 코드
프로세싱 코드에서 한가지 주의 해야할 사항은 시리통 통신의 속도를 9600bps 로 설정을 하였다. 그렇기 때문에 아두이노의 시리얼 모니터창에서도 동일한 통신속도를 맞추어 주어야 한다.

     - ADXL345 아두이노, 프로세싱 스캐치 파일 다운로드(3D)


/********************************************************************************
* ADXL345 Library Examples- pitch_roll.ino *
* *
* Copyright (C) 2012 Anil Motilal Mahtani Mirchandani(anil.mmm@gmail.com) *
* *
* License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> *
* This is free software: you are free to change and redistribute it. *
* There is NO WARRANTY, to the extent permitted by law. *
* *
*********************************************************************************/
#include <Wire.h>
#include "adxl345.h"
const float alpha = 0.5;
double fXg = 0;
double fYg = 0;
double fZg = 0;
ADXL345 acc;
void setup()
{
acc.begin();
Serial.begin(9600);
delay(100);
}
void loop()
{
double pitch, roll, Xg, Yg, Zg;
acc.read(&Xg, &Yg, &Zg);
//Low Pass Filter
fXg = Xg * alpha + (fXg * (1.0 - alpha));
fYg = Yg * alpha + (fYg * (1.0 - alpha));
fZg = Zg * alpha + (fZg * (1.0 - alpha));
//Roll & Pitch Equations
roll = (atan2(-fYg, fZg)*180.0)/M_PI;
pitch = (atan2(fXg, sqrt(fYg*fYg + fZg*fZg))*180.0)/M_PI;
Serial.print(pitch);
Serial.print(":");
Serial.println(roll);
delay(10);
}

아두이노 시리얼 모니터창에서 반드시 보드레이트(Baudate)를 9600 으로 수정 해야 한다. 그렇지 않으면 시리얼 모니터 창에 아무런 데이터가 표시되지 않거나 잘못된 데이터가 표시될 것이다. 프로세싱 데이터를 처리하기 위한 데이터 이므로 데이터를 바로 판독하기는 어렵다. 데이터가 올바르게 표시되는것을 확인 하였다면 프로세싱에서 동일한 시리얼포트를 사용해야 하기 때문에 아두이노 시리얼 모니텅 창을 닫도록 하자.

3.4 프로세싱 코드(3D)

프로세싱 코드에서 한가지 주의 해야할 사항은 시리얼 포트를 각자의 환경에 맞추어서 수정을 해주어야 한다는 것이다.
아래 프로세싱 코드에서 "[0]" 의 0이라는 숫자는 장치관리자에서 COM포트의 번호가 아니라 포트의 순서라는것에 주의 해야 한다.

mpu6050

위의 장치관리자 화면에서 예를 든다면 다음과 같다.

Serial.list()[0]; // --> COM31
Serial.list()[1]; // --> COM5

프로세싱 코드를 실행할때 테스트하는 PC의 COM 포트 상태에 따라서 "Serial.list()[0]" 의 숫자를 바꾸어 주어야 한다.

(1) 프로세싱 코드

/********************************************************************************
* ADXL345 Library Examples- pitch_roll.pde *
* *
* Copyright (C) 2012 Anil Motilal Mahtani Mirchandani(anil.mmm@gmail.com) *
* *
* License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> *
* This is free software: you are free to change and redistribute it. *
* There is NO WARRANTY, to the extent permitted by law. *
* *
*********************************************************************************/
// If you are working with Arduino Mega
// sudo ln -s /dev/ttyACM0 /dev/ttyS8
import processing.serial.*;
Serial fd;
int pitch = 0;
int roll = 0;
void setup () {
size(640, 360, P3D); //Connect to the corresponding serial port
fd = new Serial(this, Serial.list()[0], 9600);
// Defer callback until new line
fd.bufferUntil('\n');
}
void draw () {
//Set background
background(0.5);
pushMatrix(); translate(width/2, height/2, -30); //Rotate
rotateX(((float)pitch)*PI/180.0); rotateZ(((float)roll)*PI/180.0); //Print data
print("Pitch: ");
print(pitch);
print(", Roll: ");
println(roll);
scale(90);
beginShape(QUADS);
fill(0, 255, 0); vertex(-1, 1, 1);
fill(0, 255, 0); vertex( 1, 1, 1);
fill(0, 255, 0); vertex( 1, -1, 1);
fill(0, 255, 0); vertex(-1, -1, 1);
fill(0, 255, 255); vertex( 1, 1, 1);
fill(0, 255, 255); vertex( 1, 1, -1);
fill(0, 255, 255); vertex( 1, -1, -1);
fill(0, 255, 255); vertex( 1, -1, 1);
fill(255, 0, 255); vertex( 1, 1, -1);
fill(255, 0, 255); vertex(-1, 1, -1);
fill(255, 0, 255); vertex(-1, -1, -1);
fill(255, 0, 255); vertex( 1, -1, -1);
fill(255, 255, 0); vertex(-1, 1, -1);
fill(255, 255, 0); vertex(-1, 1, 1);
fill(255, 255, 0); vertex(-1, -1, 1);
fill(255, 255, 0); vertex(-1, -1, -1);
fill(255, 0, 0); vertex(-1, 1, -1);
fill(255, 0, 0); vertex( 1, 1, -1);
fill(255, 0, 0); vertex( 1, 1, 1);
fill(255, 0, 0); vertex(-1, 1, 1);
fill(0, 0, 255); vertex(-1, -1, -1);
fill(0, 0, 255); vertex( 1, -1, -1);
fill(0, 0, 255); vertex( 1, -1, 1);
fill(0, 0, 255); vertex(-1, -1, 1);
endShape();
popMatrix(); }
void serialEvent (Serial fd) {
// get the ASCII string:
String rpstr = fd.readStringUntil('\n');
if (rpstr != null) {
String[] list = split(rpstr, ':');
pitch = ((int)float(list[0]));
roll = ((int)float(list[1]));
}
}

(2) 실행 결과



본 메뉴얼의 프로세싱 코드는 아래 URL의 내용을 참조하여 작성 하였다.
https://theccontinuum.com/2012/09/24/arduino-imu-pitch-roll-from-accelerometer/

3.5 아두이노 스케치 코드(2D)

(1) 아두이노 프로세싱 코드
프로세싱 코드에서 한가지 주의 해야할 사항은 시리통 통신의 속도를 115200bps 로 설정을 하였다. 그렇기 때문에 아두이노의 시리얼 모니터창에서도 동일한 통신속도를 맞추어 주어야 한다.

     - ADXL345 아두이노, 프로세싱 스캐치 파일 다운로드(2D)


/*
ADXL345 Triple Axis Accelerometer. Output for ADXL345_processing.pde
Read more: http://www.jarzebski.pl/arduino/czujniki-i-sensory/3-osiowy-akcelerometr-adxl345.html
GIT: https://github.com/jarzebski/Arduino-ADXL345
Web: http://www.jarzebski.pl
(c) 2014 by Korneliusz Jarzebski
*/
#include <Wire.h>
#include "adxl345.h"
ADXL345 accelerometer;
void setup(void) {
Serial.begin(115200);
// Initialize ADXL345
if (!accelerometer.begin())
{
delay(500);
}
// Set measurement range
// +/- 2G: ADXL345_RANGE_2G
// +/- 4G: ADXL345_RANGE_4G
// +/- 8G: ADXL345_RANGE_8G
// +/- 16G: ADXL345_RANGE_16G
accelerometer.setRange(ADXL345_RANGE_16G);
}
void loop(void) {
// Read normalized values
Vector norm = accelerometer.readNormalize();
// Low Pass Filter to smooth out data. 0.1 - 0.9
Vector filtered = accelerometer.lowPassFilter(norm, 0.15);
// Calculate Pitch & Roll
int pitch = -(atan2(norm.XAxis, sqrt(norm.YAxis*norm.YAxis + norm.ZAxis*norm.ZAxis))*180.0)/M_PI;
int roll = (atan2(norm.YAxis, norm.ZAxis)*180.0)/M_PI;
// Calculate Pitch & Roll (Low Pass Filter)
int fpitch = -(atan2(filtered.XAxis, sqrt(filtered.YAxis*filtered.YAxis + filtered.ZAxis*filtered.ZAxis))*180.0)/M_PI;
int froll = (atan2(filtered.YAxis, filtered.ZAxis)*180.0)/M_PI;
// Output
Serial.print(pitch);
Serial.print(":");
Serial.print(roll);
Serial.print(":");
// Output (filtered)
Serial.print(fpitch);
Serial.print(":");
Serial.print(froll);
Serial.println();
}

아두이노 시리얼 모니터창에서 반드시 보드레이트(Baudate)를 115200 으로 수정 해야 한다. 그렇지 않으면 시리얼 모니터 창에 아무런 데이터가 표시되지 않거나 잘못된 데이터가 표시될 것이다. 프로세싱 데이터를 처리하기 위한 데이터 이므로 데이터를 바로 판독하기는 어렵다. 데이터가 올바르게 표시되는것을 확인 하였다면 프로세싱에서 동일한 시리얼포트를 사용해야 하기 때문에 아두이노 시리얼 모니텅 창을 닫도록 하자.

3.4 프로세싱 코드(2D)

(1) 프로세싱 코드

/*
ADXL345 Triple Axis Accelerometer. Processing for ADXL345_processing.ino
Read more: http://www.jarzebski.pl/arduino/czujniki-i-sensory/3-osiowy-akcelerometr-adxl345.html
GIT: https://github.com/jarzebski/Arduino-ADXL345
Web: http://www.jarzebski.pl
(c) 2014 by Korneliusz Jarzebski
*/
import processing.serial.*;
Serial myPort;
// Data samples
int actualSample = 0;
int maxSamples = 400;
int sampleStep = 1;
boolean hasData = false;
// Charts
PGraphics pgChart;
int[] colors = { #ff4444, #33ff99, #5588ff };
String[] pyrSeries = { "Pitch", "Roll" };
String[] compareSeries = { "Normal", "Filtered" };
// Data for accelerometer Pitch, Roll
float[][] pyrValues = new float[2][maxSamples];
float[][] pyrValuesFiltered = new float[2][maxSamples];
// Data for compare
float[][] pitchValues = new float[2][maxSamples];
float[][] rollValues = new float[2][maxSamples];
// Artificial Horizon
PGraphics pgArtificialHorizon;
PGraphics pgArtificialHorizonRing;
PImage imgArtificialHorizon;
PImage imgArtificialHorizonRing;
int ahWidth = 0;
int ahHeight = 0;
int ahDiameter = 0;
int ahRadius = 0;
float ahKappa = 0.5522847498;
float ahRadiusKappa = 0;
void setup ()
{
size(755, 550, P2D);
background(0);
// Init
initArtificialHorizon();
// Serial
myPort = new Serial(this, Serial.list()[0], 115200);
myPort.bufferUntil(10);
}
void drawChart(String title, String[] series, float[][] chart, int x, int y, int h, boolean symmetric, boolean fixed, int fixedMin, int fixedMax, int hlines) {
int actualColor = 0;
int maxA = 0;
int maxB = 0;
int maxAB = 0;
int min = 0;
int max = 0;
int step = 0;
int divide = 0;
if (fixed)
{
min = fixedMin;
max = fixedMax;
step = hlines;
} else
{
if (hlines > 2)
{
divide = (hlines - 2);
} else
{
divide = 1;
}
if (symmetric)
{
maxA = (int)abs(getMin(chart));
maxB = (int)abs(getMax(chart));
maxAB = max(maxA, maxB);
step = (maxAB * 2) / divide;
min = -maxAB-step;
max = maxAB+step;
} else
{
min = (int)(getMin(chart));
max = (int)(getMax(chart));
if ((max >= 0) && (min <= 0)) step = (abs(min) + abs(max)) / divide; if ((max < 0) && (min < 0)) step = abs(min - max) / divide; if ((max > 0) && (min > 0)) step = (max - min) / divide; if (divide > 1)
{
min -= step;
max += step;
}
}
}
pgChart = createGraphics((maxSamples*sampleStep)+50, h+60);
pgChart.beginDraw();
// Draw chart area and title
pgChart.background(0);
pgChart.strokeWeight(1);
pgChart.noFill();
pgChart.stroke(50);
pgChart.rect(0, 0, (maxSamples*sampleStep)+49, h+59);
pgChart.text(title, ((maxSamples*sampleStep)/2)-(textWidth(title)/2)+40, 20);
// Draw chart description
String Description[] = new String[chart.length];
int DescriptionWidth[] = new int[chart.length];
int DesctiptionTotalWidth = 0;
int DescriptionOffset = 0;
for (int j = 0; j < chart.length; j++)
{
Description[j] = " "+series[j]+" = ";
DescriptionWidth[j] += textWidth(Description[j]+"+000.00");
Description[j] += nf(chart[j][actualSample-1], 0, 2)+" ";
DesctiptionTotalWidth += DescriptionWidth[j];
}
actualColor = 0;
for (int j = 0; j < chart.length; j++)
{
pgChart.fill(colors[actualColor]);
pgChart.text(Description[j], ((maxSamples*sampleStep)/2)-(DesctiptionTotalWidth/2)+DescriptionOffset+40, h+50);
DescriptionOffset += DescriptionWidth[j];
actualColor++;
if (actualColor >= colors.length) actualColor = 0;
}
// Draw H-Lines
pgChart.stroke(100);
for (float t = min; t <= max; t=t+step)
{
float line = map(t, min, max, 0, h);
pgChart.line(40, h-line+30, (maxSamples*sampleStep)+40, h-line+30);
pgChart.fill(200, 200, 200);
pgChart.textSize(12);
pgChart.text(int(t), 5, h-line+34);
}
// Draw data series
pgChart.strokeWeight(2);
for (int i = 1; i < actualSample; i++)
{
actualColor = 0;
for (int j = 0; j < chart.length; j++)
{
pgChart.stroke(colors[actualColor]);
float d0 = chart[j][i-1];
float d1 = chart[j][i];
if (d0 < min) d0 = min;
if (d0 > max) d0 = max;
if (d1 < min) d1 = min;
if (d1 > max) d1 = max;
float v0 = map(d0, min, max, 0, h);
float v1 = map(d1, min, max, 0, h);
pgChart.line(((i-1)*sampleStep)+40, h-v0+30, (i*sampleStep)+40, h-v1+30);
actualColor++;
if (actualColor >= colors.length) actualColor = 0;
}
}
pgChart.endDraw();
image(pgChart, x, y);
}
void initArtificialHorizon()
{
imgArtificialHorizon = loadImage("artificialHorizon.png");
imgArtificialHorizonRing = loadImage("artificialHorizonRing.png");
ahWidth = imgArtificialHorizon.width - 20;
ahHeight = imgArtificialHorizon.height - 20;
ahDiameter = min(ahWidth, ahHeight);
ahRadius = ahDiameter / 2;
ahRadiusKappa = ahRadius * ahKappa;
}
float getArtificialHorizon(float pitch)
{
return -sin(pitch)*ahRadius;
}
void drawScale(float offset, float[][] pyr)
{
float horizon;
// Ground side
horizon = getArtificialHorizon(radians(pyr[0][actualSample-1]) - offset * PI / 180);
pgArtificialHorizon.noFill();
pgArtificialHorizon.beginShape();
pgArtificialHorizon.vertex(ahRadius, 0);
pgArtificialHorizon.stroke(255);
pgArtificialHorizon.strokeWeight(2);
pgArtificialHorizon.bezierVertex(ahRadius, horizon * ahKappa, ahRadiusKappa, horizon, 0, horizon);
pgArtificialHorizon.bezierVertex( -ahRadiusKappa, horizon, -ahRadius, horizon * ahKappa, -ahRadius, 0);
pgArtificialHorizon.endShape();
// Sky side
horizon = getArtificialHorizon(radians(pyr[0][actualSample-1]) + offset * PI / 180);
pgArtificialHorizon.noFill();
pgArtificialHorizon.beginShape();
pgArtificialHorizon.vertex(ahRadius, 0);
pgArtificialHorizon.stroke(0);
pgArtificialHorizon.strokeWeight(2);
pgArtificialHorizon.bezierVertex(ahRadius, horizon * ahKappa, ahRadiusKappa, horizon, 0, horizon);
pgArtificialHorizon.bezierVertex( -ahRadiusKappa, horizon, -ahRadius, horizon * ahKappa, -ahRadius, 0);
pgArtificialHorizon.endShape();
}
void drawArtificialHorizon(int x, int y, float[][] pyr)
{
pgArtificialHorizon = createGraphics(ahWidth, ahHeight);
pgArtificialHorizonRing = createGraphics(ahWidth+20, ahHeight+20); float horizon = getArtificialHorizon(radians(pyr[0][actualSample-1]));
// pgArtificialHorizon.clear();
pgArtificialHorizon.beginDraw();
// Ground
pgArtificialHorizon.translate(ahRadius, ahRadius);
pgArtificialHorizon.rotate(radians(-pyr[1][actualSample-1]));
pgArtificialHorizon.strokeWeight(0);
pgArtificialHorizon.fill(40, 40, 40);
pgArtificialHorizon.arc(0.0, 0.0, ahDiameter, ahDiameter, 0, 2 * PI);
// Sky
pgArtificialHorizon.beginShape();
pgArtificialHorizon.fill(200, 200, 250);
pgArtificialHorizon.strokeWeight(2);
pgArtificialHorizon.stroke(255);
pgArtificialHorizon.vertex(-ahRadius, 0);
pgArtificialHorizon.bezierVertex(-ahRadius, -ahRadius-20, ahRadius, -ahRadius-20, ahRadius, 0);
pgArtificialHorizon.bezierVertex(ahRadius, horizon * ahKappa, ahRadiusKappa, horizon, 0, horizon);
pgArtificialHorizon.bezierVertex(-ahRadiusKappa, horizon, -ahRadius, horizon * ahKappa, -ahRadius, 0);
pgArtificialHorizon.endShape();
// Scale
drawScale(60, pyr);
drawScale(50, pyr);
drawScale(40, pyr);
drawScale(30, pyr);
drawScale(20, pyr);
drawScale(10, pyr);
pgArtificialHorizon.endDraw();
image(pgArtificialHorizon, x+10, y+10);
image(imgArtificialHorizon, x, y);
// Draw ring
pgArtificialHorizonRing.beginDraw();
pgArtificialHorizonRing.clear();
pgArtificialHorizonRing.translate(130,130);
pgArtificialHorizonRing.rotate(radians(pyr[1][actualSample-1]));
pgArtificialHorizonRing.image(imgArtificialHorizonRing, -130, -130);
pgArtificialHorizonRing.endDraw();
image(pgArtificialHorizonRing, x, y);
}
float getMin(float[][] chart)
{
float minValue = 0;
float[] testValues = new float[chart.length];
float testMin = 0;
for (int i = 0; i < actualSample; i++)
{
for (int j = 0; j < testValues.length; j++)
{
testValues[j] = chart[j][i];
}
testMin = min(testValues);
if (i == 0)
{
minValue = testMin;
} else
{
if (minValue > testMin) minValue = testMin;
}
}
return ceil(minValue)-1; }
float getMax(float[][] chart)
{
float maxValue = 0;
float[] testValues = new float[chart.length];
float testMax = 0;
for (int i = 0; i < actualSample; i++)
{
for (int j = 0; j < testValues.length; j++)
{
testValues[j] = chart[j][i];
}
testMax = max(testValues);
if (i == 0)
{
maxValue = testMax;
} else
{
if (maxValue < testMax) maxValue = testMax;
}
}
return ceil(maxValue); }
void draw() {
if (!hasData) return;
background(0);
drawChart("Pitch [deg]", compareSeries, pitchValues, 10, 10, 200, true, true, -90, 90, 30);
drawChart("Roll [deg]", compareSeries, rollValues, 10, 280, 200, true, true, -90, 90, 30);
drawArtificialHorizon(480, 20, pyrValues);
drawArtificialHorizon(480, 290, pyrValuesFiltered);
}
void nextSample(float[][] chart)
{
for (int j = 0; j < chart.length; j++)
{
float last = chart[j][maxSamples-1];
for (int i = 1; i < maxSamples; i++)
{
chart[j][i-1] = chart[j][i];
}
chart[j][(maxSamples-1)] = last;
}
}
void serialEvent (Serial myPort)
{
String inString = myPort.readStringUntil(10);
if (inString != null)
{
inString = trim(inString);
String[] list = split(inString, ':');
String testString = trim(list[0]);
if (list.length != 4) return;
// Fill Pitch & Roll
pyrValues[0][actualSample] = (float(list[0]));
pyrValues[1][actualSample] = (float(list[1]));
// Fill Pitch & Roll (Filtered)
pyrValuesFiltered[0][actualSample] = (float(list[2]));
pyrValuesFiltered[1][actualSample] = (float(list[3]));
// Fill Pitch compare
pitchValues[0][actualSample] = (float(list[0]));
pitchValues[1][actualSample] = (float(list[2]));
// Fill Roll compare
rollValues[0][actualSample] = (float(list[1]));
rollValues[1][actualSample] = (float(list[3]));
if (actualSample > 1)
{
hasData = true;
}
if (actualSample == (maxSamples-1))
{
nextSample(pyrValues);
nextSample(pyrValuesFiltered);
nextSample(pitchValues);
nextSample(rollValues);
} else
{
actualSample++;
}
}
}

(2) 실행 결과



본 메뉴얼의 프로세싱 코드는 아래 URL의 내용을 참조하여 작성 하였다.
https://theccontinuum.com/2012/09/24/arduino-imu-pitch-roll-from-accelerometer/

* 다른 센서들의 아두이노와 프로세싱 메뉴얼 보기

(1) MPU6050 GY-521 센서 아두이노, 프로세싱 메뉴얼(새창)
(2) MPU9250 GY-9250 센서 아두이노, 프로세싱 메뉴얼(새창)
(3) L3G4200D GY-50 센서 아두이노, 프로세싱 메뉴얼(새창)
(4) HMC5883L GY-271 센서 아두이노, 프로세싱 메뉴얼(새창)
(5) BMP180 GY-68 센서 아두이노, 프로세싱 메뉴얼(새창)
(6) ADXL345 GY-80 센서 아두이노, 프로세싱 메뉴얼(새창)
(7) ADXL335 GY-61 센서 아두이노, 프로세싱 메뉴얼(새창)