Arduino 进阶徒步追踪器:GPS、多传感器与电池组
元器件与材料
- Arduino 101 ×1
- AA 电池 ×4
- 4×AA 电池盒 ×1
- Accessory Shield 扩展板 ×1
- Adafruit BMP180 气压传感器 ×1
- Adafruit Ultimate GPS Breakout ×1
- Adafruit 3轴磁力计 (HMC5883) ×1
- Adafruit AM2320 湿度传感器 ×1
- CR2012 钮扣电池 ×2 (RTC 备用)
- 10kΩ 电阻 ×1
- 3.9kΩ 电阻 ×1
所需工具
- 热熔胶枪
- 电烙铁
软件与在线服务
- Arduino IDE
项目简介
什么是徒步追踪器?
徒步追踪器用于感知徒步者周围环境变化:温度、气压、湿度、海拔、位置、方向等,为户外探索提供数据洞察。高海拔空气稀薄干燥,许多人呼吸困难、皮肤干燥;本设备帮助了解自身极限。探索沼泽、沙漠、森林等不同环境时,实时监测环境参数能更好理解自然。

硬件分配
Accessory Shield 内置 I2C 传感器:
- LM75B 温度传感器 → 环境温度
- ADXL345 三轴加速度计 → 倾斜与重力
- DS3231 高精度 RTC → 时间
- HMC5883 三轴磁力计 → 罗盘方位
- AM2320 湿度传感器 → 相对湿度
- BMP180 气压传感器 → 大气压与海拔
- MTK3339 GPS → 位置与行走距离
Arduino 101 扩展:
- 3.9k+22k 分压电阻 → 检测 4×AA 电池电压
- SSD1306 OLED → 数据显示
- 蜂鸣器 → 报警提示
- RGB LED、XBee 接口、摇杆、电位器本项目未用
硬件改装与连接
Arduino 101 用螺柱+热熔胶固定在 4×AA 电池盒上。


安装 4×AA 电池与 CR1220 (RTC)。

电池盒与主板间隙塞入气压、湿度、磁力计、GPS 模块。

I2C 传感器焊在万能板上置于该间隙,从底部引线连接 Arduino。


分压电路测电池电压,接 ADC A1。扩展板摇杆占用 A1,故拆除摇杆。


通过焊桥引出 D2(软复位)、D3、D9、D10 到 XBee 接口备用。

分层堆叠:顶层扩展板、中层传感器+GPS、底层电池盒。连接示意见原理图。

软件开发
Arduino IDE 1.8.5,通过库管理器安装所需库:


Accessory Shield 专用库通过 ZIP 安装。参考各传感器示例代码整合 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 泄漏供电) 后代码定型。
已知局限
- 海拔基于气压计算,仅适用常规天气
- 罗盘代码未做倾斜补偿,需水平持握;同心圆指示倾斜度,内圈缩为点时最准
- 磁偏角随地点和太阳活动变化,未修正会有几度偏差,查询 magnetic-declination.com
- 罗盘受附近磁性物体干扰 (如磁性矿物)
- 最低工作电压 ~4.5V,低于此需换电池
- RTC 时间需改代码重烧录或拔插钮扣电池
- 开发期无 GPS 模块,代码含演示坐标,复现需加入 GPS 库与代码
- 加速度计/磁力计 XY 平面取决于 PCB 布局,代码需相应调整
- 加速度计易受振动影响,建议静止测量
改进方向
固件: 参数定期存 EEPROM、海拔/位置里程碑蜂鸣提醒、喝水/休息提醒、低电压/低温/低湿报警、罗盘软件倾斜补偿、GPS+物联网自动获取磁偏角。
硬件: 用户按键设置时间/磁偏角、3D 打印外壳、改用可充电 LiPo、单板紧凑 PCB 设计。
结语
徒步旅行很酷,随时掌握环境变化更有趣。海拔、湿度、气压、温度随行程变化,本设备让你知道多高会高反、多干皮肤会不适。虽电路布局和代码仍有改进空间,但作为野外探索小工具已很实用!
外部资源
Accessory Shield 文档:waveshare.com
推荐更好版本的硬件
Rapid Prototyping Kit
代码
Hiking_Tracker.ino (主程序)
/* 注意:
* D2 可软复位,D3/D9/D10 经 XBee 接口引出
* D4/D13 不可用
*/
#include
#include
#include
#include
#include
#include
#include "U8glib.h"
#include "ds3231.h"
#include "Adafruit_Sensor.h"
#include "Adafruit_AM2320.h"
#include
#include
#include
// RTC 变量
uint8_t time[8]; struct ts t; int s,m,h,dy,mo,yr;
// 加速度计
float X=0,Y=0,Z=0,Gravity=0;
// 磁力计
float Xm=0,Ym=0,Zm=0;
// RGB LED
const int rgb_pwr=12, clk_pin=6, data_pin=5;
float hue=0; boolean up=true;
// 电位器
int pot=0;
// 电池电压
float Vbatt=0;
// 温湿度气压海拔
float temp=0, humid=0, prsr=0, alt=0;
// GPS 坐标
float lat=0, lon=0;
// 罗盘
float heading=0;
// 其他
int mstime=0, sensor_selector=1;
// I2C 设备对象
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);
TempI2C_LM75 termo(0x48, TempI2C_LM75::nine_bits);
ADXL345 accelerometer;
ChainableLED leds(clk_pin, data_pin, rgb_pwr, 1);
Adafruit_AM2320 am2320;
Adafruit_BMP085_Unified bmp180(10085);
Adafruit_HMC5883_Unified mag(12345);
// GPS 串口冲突调试时取消注释
// HardwareSerial mySerial = Serial;
// Adafruit_GPS GPS(&mySerial);
void setup() {
// analogReference(INTERNAL); // 1.1V 内部基准
// OLED 信号引出需扩展板焊桥
pinMode(2,1); pinMode(3,1); pinMode(9,1); pinMode(10,1);
digitalWrite(2,HIGH); digitalWrite(3,HIGH); digitalWrite(9,LOW); digitalWrite(10,HIGH);
pinMode(7,1); pinMode(8,1); digitalWrite(7,HIGH); digitalWrite(8,LOW);
// A0: 10k电位器, A1: 电池分压 (3.91k/21.76k)
// Serial.begin(9600); // 调试用
if (!accelerometer.begin()) delay(50);
DS3231_init(DS3231_INTCN);
pinMode(11,1); digitalWrite(11,LOW); // 蜂鸣器
}
void loop() {
// OLED 画面循环
u8g.firstPage();
do { draw(); } while(u8g.nextPage());
sensor_selector++; if (sensor_selector>=3) sensor_selector=0;
// 温度
temp = termo.getTemp(); delay(5);
// 气压/海拔 (sensor_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; delay(50); } // hPa->kPa
sensors_event_t event2;
bmp180.getEvent(&event2);
alt = bmp180.pressureToAltitude(1013.25, event2.pressure); delay(50);
}
// 湿度 (sensor_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;
// RTC 时间
DS3231_get(&t);
s=t.sec; m=t.min; h=t.hour; dy=t.mday; mo=t.mon; yr=t.year;
// 电位器 (0-5V 映射)
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);
// 罗盘 (sensor_selector==2)
if (sensor_selector==2) {
if(!mag.begin()) while(1);
delay(70);
sensor_t sensor2; mag.getSensor(&sensor2); delay(70);
sensors_event_t event2; mag.getEvent(&event2); delay(70);
float declination = 0; // 需按 GPS 坐标查表填入
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 / M_PI;
}
// 蜂鸣器阈值
if (pot > 80) digitalWrite(11,1); else digitalWrite(11,0);
// 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() {
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.setPrintPos(95,22); u8g.print(Vbatt); u8g.drawStr(123,22,"V");
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
}
依赖库
需解压后逐个通过 Arduino 库管理器“添加 ZIP 库”安装,详见原项目附件。
原理图

制造工艺
- 树莓派 PIR 运动传感器控制 LED 实战指南
- 树莓派低成本土壤湿度传感器:智能灌溉 DIY 指南
- 树莓派仓鼠健身追踪器:激光断束传感器记录每日运动量
- 树莓派 DS18B20 温度传感器测试与 HC-SR04 超声波测距实战
- 树莓派 + MCP9808 + InfluxDB + Grafana:家庭温度监测系统
- Walabot 驱动的马桶卫生监测器
- 低成本情绪传感器与 EEG:实时情绪检测硬件方案
- 树莓派 2 (Windows 10 IoT Core) 采集 Adafruit 10DOF IMU 数据上传 Azure Event Hubs
- UnifiedWater v1 — 基于 Arduino MKR GSM 1400 的完整水质监测套件
- MyRiver:基于 Arduino MKR FOX 1200 的河流水质远程监测系统
- 氧传感器 (O2) 详解:作用、位置与故障征兆
- 国际空间站传感器精准WiFi RSSI定位追踪