산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

부품 및 소모품

부품수량비고
아두이노 1011
AA 배터리14개 팩 사용
4xAA 배터리 홀더1
액세서리 실드 (Accessory Shield)1Waveshare 제품 등
Adafruit BMP180 기압 센서1
Adafruit Ultimate GPS 브레이크아웃1
Adafruit 3축 자기 센서 (HMC5883)1
Adafruit AM2320 습도 센서1
Adafruit 코인 셀 CR20122RTC 백업용
저항 10kΩ1
저항 3.9kΩ1배터리 전압 분압용

필요 도구 및 기계

앱 및 온라인 서비스

프로젝트 소개

하이킹 트래커란?

하이킹 트래커는 하이킹 중 등반자 주변 환경의 변화를 감지하는 장치입니다.

온도, 기압, 습도, 고도, 위치, 방향 등의 변화를 관찰해 하이킹에 대한 흥미로운 통찰을 제공합니다. 산에서는 춥고 건조하며 공기 밀도가 낮아 숨쉬기 힘들거나 피부 건조를 겪는 분들이 많은데, 이 장치로 자신의 한계를 알 수 있습니다. 고도 확인의 재미도 있습니다.

산뿐 아니라 습지, 사막, 숲 탐험 시 각 환경의 고유한 프로파일을 이동 중 모니터링하면 자연을 더 잘 이해할 수 있습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

1단계: 하드웨어 - 어떤 센서가 무슨 역할?

액세서리 실드에 내장된 I2C 장치들:

아두이노 우노(실제로는 아두이노 101) 직접 연결:

2단계: 하드웨어 수정 및 연결

아두이노 우노(101)를 4xAA 배터리 홀더에 나사 스탠드오프와 글루건으로 고정했습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합
아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

4xAA 배터리와 CR1220(RTC용)을 홀더에 장착.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합
아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

배터리 홀더와 아두이노 보드 사이에 기압, 습도, 자기, GPS 센서를 배치할 공간이 있습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합
아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

I2C 센서들을 프리보드에 납땜해 이 공간에 넣었습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합
아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

이 센서들은 아두이노 바닥면에서 연결했습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

4xAA 배터리 전압 측정을 위한 분압 회로를 추가했습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

AA 배터리 팩 전압 감지 라인은 ADC A1로 갑니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

실드의 조이스틱이 A1~A5 핀에 매핑되어 있어 조이스틱을 제거했습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

액세서리 실드의 일부 패드를 솔더 브리지로 연결해 D2(소프트 리셋), D3, D9, D10 핀을 XBee 인터페이스로 빼냈습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합
아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

부품 스택 구조:

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

연결 다이어그램:

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

3단계: 장치 프로그래밍

Arduino IDE Build 1.8.5 사용. 라이브러리 매니저에서 필요한 라이브러리들을 검색해 설치하거나 ZIP으로 추가했습니다.

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합
아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합
아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

각 센서의 예제 코드를 검토해 API를 파악한 뒤, 모든 라이브러리 헤더를 하나의 스케치에 포함시켰습니다.

헤더 파일 목록

math.h, inttypes.h, Wire.h, lm75.h, ADXL345.h, ChainableLED.h,
U8glib.h, ds3231.h, Adafruit_Sensor.h, Adafruit_AM2320.h,
Adafruit_BMP085_U.h, Adafruit_HMC5883_U.h, Adafruit_GPS.h

여러 번 편집, 컴파일, 디버깅(느슨한 연결 발견: BMP180이 VCC 없이도 I2C 핀 누설 전원으로 작동함) 끝에 코드 완성.

주의사항 (Caveats)

개선 가능 사항

펌웨어 측 개선:

하드웨어 측 개선:

결론

여행과 하이킹은 쿨합니다. 주변 환경 변화를 확인할 수 있으면 더 흥미롭습니다. 특히 하이킹 몇 시간 동안 고도, 습도, 기압, 온도 변화를 체크할 수 있어 좋습니다. 이 장치로 고산병을 느끼는 고도, 피부가 건조해지는 습도 등을 알 수 있습니다. 회로 레이아웃과 코드 개선 여지가 남아 있지만, 야외 활동 시 이런 가젯 하나 있으면 정말 쿨합니다!

외부 자료

https://www.waveshare.com/wiki/Accessory_Shield

더 나은 버전을 위한 추천 하드웨어

Rapid Prototyping Kit

코드

Hiking_Tracker.ino (C/C++)

/*/////////// NOTE //////////
// D2: 코드로 소프트 리셋 가능
// D3, D9, D10: XBee 인터페이스 12,13,16번 핀으로 빼냄
// D4, D13: 접근 불가
///////////////////////////*/

//// 헤더 파일 ////
#include <math.h>
#include <inttypes.h>
#include <Wire.h>
#include <lm75.h>
#include <ADXL345.h>
#include <ChainableLED.h>
#include "U8glib.h"
#include "ds3231.h"
#include "Adafruit_Sensor.h"
#include "Adafruit_AM2320.h"
#include <Adafruit_BMP085_U.h>
#include <Adafruit_HMC5883_U.h>
#include <Adafruit_GPS.h>

////////////////// RTC 변수 //////////////////
uint8_t time[8];
struct ts t;
int s, m, h, dy, mo, yr;

/////////////// 가속도계 변수 ///////////////
float X = 0.0, Y = 0.0, Z = 0.0, Gravity = 0.0;

/////////////// 자기계 변수 ////////////////
float Xm = 0.0, Ym = 0.0, Zm = 0.0;

//////////////// RGB LED 변수 //////////////
const int rgb_pwr = 12;
const int clk_pin = 6;
const int data_pin = 5;
float hue = 0.0;
boolean up = true;

//////////////// 포텐쇼미터 ////////////////
int pot = 0;

////////////// 배터리/공급 전압 //////////////
float Vbatt = 0.0;

//////////////// 온도계 ////////////////////
float temp = 0.0;

//////////////// 습도 //////////////////////
float humid = 0.0;

//////////////// 기압 //////////////////////
double prsr = 0.0;

//////////////// 고도 //////////////////////
float alt = 0.0;

///////////////// 위도, 경도 ////////////////
float lat = 0.0, lon = 0.0;

/////////////// 나침반 방위 ////////////////
float heading = 0.0;

/////////////// 기타 변수 ////////////////
int mstime = 0;
int sensor_selector = 1;

//////////// ENUM & I2C 장치 객체 ////////////
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_DEV_0); // I2C OLED
TempI2C_LM75 termo = TempI2C_LM75(0x48, TempI2C_LM75::nine_bits); // 온도
ADXL345 accelerometer; // 가속도
ChainableLED leds(clk_pin, data_pin, rgb_pwr, 1); // RGB LED
Adafruit_AM2320 am2320 = Adafruit_AM2320(); // 습도
Adafruit_BMP085_Unified bmp180 = Adafruit_BMP085_Unified(10085); // 기압
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345); // 나침반

// GPS 활성화 시 다음 두 줄 주석 해제 (시리얼 디버깅 불가)
// HardwareSerial mySerial = Serial;
// Adafruit_GPS GPS(&mySerial);

void setup(void) {
  // 아날로그 레퍼런스 1.1V 내부
  // analogReference(INTERNAL);

  // OLED용 신호 (실드에 솔더 브리지 필요 for D2,D3,D9,D10)
  pinMode(2, OUTPUT); // D2 -> RST (소프트 리셋)
  pinMode(3, OUTPUT); // XBee 12번
  pinMode(9, OUTPUT); // XBee 13번
  pinMode(10, OUTPUT); // XBee 16번
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(9, LOW);
  digitalWrite(10, HIGH);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  digitalWrite(7, HIGH);
  digitalWrite(8, LOW);

  // 10k POT on A0
  // Vin Batt/Supply on A1 (3.91k/21.76k 분압)

  // 시리얼 디버깅 (선택)
  // Serial.begin(9600);

  // 가속도계 초기화 대기
  if (!accelerometer.begin()) delay(50);

  // RTC 인터럽트 활성화
  DS3231_init(DS3231_INTCN);

  // 부저 초기화
  pinMode(11, OUTPUT);
  digitalWrite(11, LOW);
}

void loop(void) {
  // OLED 화면 갱신
  u8g.firstPage();
  do { draw(); } while (u8g.nextPage());

  // 센서 선택 로테이션 (0,1,2)
  sensor_selector++;
  if (sensor_selector >= 3) sensor_selector = 0;

  ///// 온도 읽기 /////
  temp = termo.getTemp();
  delay(5);

  ///// 기압 읽기 (selector 0일 때) /////
  if (sensor_selector == 0) {
    if (!bmp180.begin()) delay(11);
    sensors_event_t event;
    bmp180.getEvent(&event);
    if (event.pressure) {
      prsr = event.pressure / 10; // hPa -> kPa
      delay(50);
    }
  }

  ///// GPS 초기화 /////
  // GPS.begin(9600);

  ///// 고도 읽기 (selector 0일 때) /////
  if (sensor_selector == 0) {
    sensors_event_t event;
    bmp180.getEvent(&event);
    alt = bmp180.pressureToAltitude(1013.25, event.pressure);
    delay(50);
  }

  ///// 습도 센서 초기화 & 읽기 (selector 1일 때) /////
  if (sensor_selector == 1) {
    am2320.begin();
    delay(50);
    humid = am2320.readHumidity();
    delay(50);
  }

  ///// 가속도 읽기 /////
  Vector norm = accelerometer.readNormalize();
  X = norm.XAxis; Y = norm.YAxis; Z = norm.ZAxis;
  Gravity = sqrt(X*X + Y*Y + Z*Z) - 0.3; // 오프셋 보정

  ///// 시간 읽기 /////
  DS3231_get(&t);
  s = t.sec; m = t.min; h = t.hour;
  dy = t.mday; mo = t.mon; yr = t.year;

  ///// 포텐쇼미터 읽기 /////
  pot = analogRead(A0) * 5 / (10 * 3);
  delay(2);

  ///// 배터리 전압 읽기 /////
  // 1.1V 레퍼런스는 이 칩에서 1.08V로 측정됨
  Vbatt = (1.08 * analogRead(A1) / 1023) / 3.91 * (3.91 + 21.76);
  delay(2);

  ///// 나침반 방위 (selector 2일 때) /////
  if (sensor_selector == 2) {
    if (!mag.begin()) while(1);
    delay(70);
    sensor_t sensor2;
    mag.getSensor(&sensor2);
    delay(70);
    // 편각은 위치(GPS 위도/경도)로 계산 필요
    float declination = 0.0;
    sensors_event_t event2;
    mag.getEvent(&event2);
    // Xm = event2.magnetic.x; Ym = event2.magnetic.y;
    delay(70);
    heading = atan2(event2.magnetic.y, event2.magnetic.x);
    heading += declination;
    if (heading < 0) heading += 2*PI;
    if (heading > 2*PI) heading -= 2*PI;
    heading = heading * 180 / PI; // 도 단위 변환
  }

  ///// 부저 제어 /////
  if (pot > 80) digitalWrite(11, HIGH); else digitalWrite(11, LOW);

  ///// RGB LED 제어 /////
  if (pot > 40) {
    leds.pwr_set(PWR_ENABLE);
    for (byte i = 0; i < 1; i++) leds.setColorHSB(i, hue, 1.0, 0.5);
    if (up) hue += 0.025; else hue -= 0.025;
    if (hue >= 1.0 && up) up = false;
    else if (hue <= 0.0 && !up) up = true;
  }

  ///// 소프트 리셋 /////
  // digitalWrite(2, LOW);
}

/////////////////// OLED 그리기 함수 ///////////////////
void draw(void) {
  u8g.setFont(u8g_font_7x13);
  u8g.drawHLine(37, 0, 62);
  u8g.setPrintPos(41, 12);
  if (h < 10) u8g.print(0);
  u8g.print(h); u8g.drawStr(54, 12, ":");
  u8g.setPrintPos(62, 12);
  if (m < 10) u8g.print(0);
  u8g.print(m); u8g.drawStr(75, 12, ":");
  u8g.setPrintPos(83, 12);
  if (s < 10) u8g.print(0);
  u8g.print(s);
  u8g.drawHLine(37, 14, 62);
  u8g.setFont(u8g_font_5x8);
  u8g.drawVLine(37, 0, 14);
  u8g.setPrintPos(0, 7);
  if (dy < 10) u8g.print(0);
  u8g.print(dy); u8g.drawStr(11, 7, "/");
  u8g.setPrintPos(17, 7);
  if (mo < 10) u8g.print(0);
  u8g.print(mo); u8g.drawStr(0, 16, "/");
  u8g.setPrintPos(6, 16);
  u8g.print(yr);
  u8g.drawVLine(99, 0, 14);
  u8g.drawStr(104, 7, "BATT");
  u8g.setPrintPos(102, 15);
  u8g.print(Vbatt); u8g.drawStr(123, 15, "V");

  u8g.print(round(temp)); u8g.drawStr(55, 25, "'C");
  u8g.setFont(u8g_font_5x8);
  u8g.setPrintPos(15, 40); u8g.print(round(X));
  u8g.setPrintPos(60, 40); u8g.print(round(Y));
  u8g.setPrintPos(100, 40); u8g.print(round(Z));
  u8g.drawVLine(37, 18, 36);
  u8g.drawStr(39, 24, "AIR TEMP: ");
  u8g.setPrintPos(85, 24); u8g.print(temp); u8g.drawStr(118, 24, "C");
  u8g.drawStr(39, 34, "HUMIDITY: ");
  u8g.setPrintPos(85, 34); u8g.print(humid); u8g.drawStr(118, 34, "%");
  u8g.drawStr(39, 44, "PRESSURE: ");
  u8g.setPrintPos(85, 44); u8g.print(prsr); u8g.drawStr(118, 44, "hP");
  u8g.drawStr(39, 54, "ALTITUDE: ");
  u8g.setPrintPos(85, 54); u8g.print(round(alt)); u8g.drawStr(118, 54, "m");
  u8g.drawHLine(0, 56, 128);
  u8g.drawStr(19, 64, "LAT:"); u8g.setPrintPos(39, 64); u8g.print(23.57); // 데모 좌표
  u8g.drawStr(69, 64, "LONG:"); u8g.setPrintPos(94, 64); u8g.print(90.36); // 데모 좌표

  // 나침반 표시
  int r = round(sqrt(X*X + Y*Y));
  u8g.drawStr(0, 22, "heading");
  u8g.drawCircle(18, 34, r, U8G_DRAW_ALL); // 내부 수평 원
  u8g.drawCircle(18, 34, 10, U8G_DRAW_ALL); // 외부 고정 원
  // 수평일 때 내부 원이 점이 됨 -> 방위 정확
  u8g.setPrintPos(0, 53); u8g.print(heading); // 0/360=N, 90=E, 180=S, 270=W
  if ((heading > 315) || (heading <= 45)) u8g.drawLine(18, 34, 8, 34);   // N
  if ((heading > 45) && (heading <= 135)) u8g.drawLine(18, 34, 18, 24); // E
  if ((heading > 135) && (heading <= 225)) u8g.drawLine(18, 34, 28, 34); // S
  if ((heading > 225) && (heading < 315)) u8g.drawLine(18, 34, 18, 44);  // W
}

라이브러리

압축 해제 후 개별 라이브러리 ZIP을 아두이노 라이브러리 매니저에 추가하세요. (다운로드 필요)

회로도

아두이노 고급 하이킹 트래커: GPS, 센서, 배터리 팩 통합

제조공정

  1. 산소(O2) 센서 이해하기: 역할, 위치, 고장 징후
  2. 라즈베리 파이에서 PIR 모션 센서로 LED 제어하기
  3. 스마트 관수용 저가 라즈베리 파이 토양 수분 센서 만들기
  4. 라즈베리 파이 피트니스 트래커로 햄스터 일일 운동량 추적하기
  5. 라즈베리 파이에서 DS18B20 온도 센서 테스트하기
  6. MCP9808, InfluxDB, Grafana로 라즈베리 파이 홈 온도 모니터 구축하기
  7. Walabot 기반 화장실 위생 모니터
  8. 감정 센서 & EEG: 실시간 감정 감지를 위한 저비용 하드웨어
  9. 아다프루트 10DOF IMU 데이터를 라즈베리파이 2에서 Azure Event Hubs로 전송 (윈도우 10 IoT Core)
  10. ISS 센서용 정확한 WiFi RSSI 추적