回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【Arduino 动手做】使用 ESP32 的紧凑型气压温度高度计 简单

头像 驴友花雕 2025.05.28 5 0

Arduino是一个开放源码的电子原型平台,它可以让你用简单的硬件和软件来创建各种互动的项目。Arduino的核心是一个微控制器板,它可以通过一系列的引脚来连接各种传感器、执行器、显示器等外部设备。Arduino的编程是基于C/C++语言的,你可以使用Arduino IDE(集成开发环境)来编写、编译和上传代码到Arduino板上。Arduino还有一个丰富的库和社区,你可以利用它们来扩展Arduino的功能和学习Arduino的知识。

Arduino的特点是:
1、开放源码:Arduino的硬件和软件都是开放源码的,你可以自由地修改、复制和分享它们。
2、易用:Arduino的硬件和软件都是为初学者和非专业人士设计的,你可以轻松地上手和使用它们。
3、便宜:Arduino的硬件和软件都是非常经济的,你可以用很低的成本来实现你的想法。
4、多样:Arduino有多种型号和版本,你可以根据你的需要和喜好来选择合适的Arduino板。
5、创新:Arduino可以让你用电子的方式来表达你的创意和想象,你可以用Arduino来制作各种有趣和有用的项目,如机器人、智能家居、艺术装置等。
 

zzz.jpg
zzz-0.jpg

在这个项目中,我们将使用 MS5611 精密压力传感器和 Seeed Studio XIAO ESP32-C3 微控制器构建一个紧凑型高度计。

我们将在小型 OLED 屏幕上显示温度、压力和高度,并使用带按钮的旋转编码器来校准和切换米和英尺之间的单位!

 

00.jpg

补给品

Seeed Studio XIAO ESP32-C3
MS5611压力传感器模块
0.96英寸OLED显示屏(SSD1306,128x64)
带按钮的旋转编码器
300mah锂电池
微型滑动电源开关
10kΩ 上拉电阻(如果发生弹跳,则编码器可选)

 

00-.jpg

步骤1:外壳设计和3D打印

外壳设计和3D打印
我使用 Fusion 360 来规划和设计我的项目,这需要仔细优化空间。我需要将所有部件装入尽可能小的体积,同时确保实用性,包括足够的布线空间和易于组装。首先,我导入了所有部件的 3D 模型,并通过将部件放置在不同的位置来尝试不同的配置。找到最佳配置后,我围绕它们构建了外壳。所有设计文件如下

我用橙色 PLA 打印了主体,同时用黑色打印了旋钮和前面板

 

附件
下载 {{ file.name }}使用 ESP32.f3d 的 Altisense_Compact 高度计下载3D视图
下载 {{ file.name }}使用 ESP32.step 的 Altisense_Compact 高度计下载3D视图
下载 {{ file.name }}编码器旋钮.3mf下载
下载 {{ file.name }}前盖.3mf下载
下载 {{ file.name }}主体.3mf下载

 

01.jpg
01-.jpg

 步骤1:外壳设计和3D打印

外壳设计和3D打印
我使用 Fusion 360 来规划和设计我的项目,这需要仔细优化空间。我需要将所有部件装入尽可能小的体积,同时确保实用性,包括足够的布线空间和易于组装。首先,我导入了所有部件的 3D 模型,并通过将部件放置在不同的位置来尝试不同的配置。找到最佳配置后,我围绕它们构建了外壳。所有设计文件如下

我用橙色 PLA 打印了主体,同时用黑色打印了旋钮和前面板

 

01.jpg

 

 

附件
下载 {{ file.name }}使用 ESP32.f3d 的 Altisense_Compact 高度计下载3D视图
下载 {{ file.name }}使用 ESP32.step 的 Altisense_Compact 高度计下载3D视图
下载 {{ file.name }}编码器旋钮.3mf下载
下载 {{ file.name }}前盖.3mf下载
下载 {{ file.name }}主体.3mf下载

 

01-.jpg

第 2 步:代码

代码将执行以下操作:

主屏幕:
显示温度、压力和计算的高度。

设置菜单(长按编码器按钮后):
校准:根据实际读数调整显示的高度。
单位:在米和英尺之间切换高度显示。
返回:返回主屏幕。
https://github.com/jarzebski/Arduino-MS5611:我们正在使用这个库来支持 MS5611。请确保在将此代码刷入 XAIO 之前,先将其安装到 IDE 中。

代码
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <MS5611.h> // https://github.com/jarzebski/Arduino-MS5611
#include <EEPROM.h>
#include <Arduino.h> // Required for ESP32 and IRAM_ATTR
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C // See datasheet for Address
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
MS5611 ms5611;
// Rotary Encoder Inputs - Using the working code's definitions
#define ENCODER_PIN_A D0 // D0
#define ENCODER_PIN_B D1 // D1
#define ENCODER_BUTTON_PIN D2 // D2
// EEPROM Addresses
#define EEPROM_REFERENCE_PRESSURE 0
#define EEPROM_UNITS 8
// Menu States
enum MenuState {
MAIN_PAGE,
SETTINGS_MENU,
CALIBRATION_PAGE,
UNITS_PAGE
};
MenuState currentMenuState = MAIN_PAGE;
// Calibration Variables
double currentReferencePressure;
float currentSetAltitude = 0; // User-defined altitude for calibration
// Units Variable (0 for meters, 1 for feet)
uint8_t currentUnits = 0;
// Encoder Variables - Using the working code's logic
volatile long encoderCount = 0;
int lastStateCLK;
String currentDir = "";
unsigned long lastButtonPress = 0;
unsigned long buttonDebounceTime = 1000; // Debounce delay for button in ms
byte buttonPressCount = 0;
// Function Prototypes
void displayMainPage();
void displaySettingsMenu(int selectedOption);
void displayCalibrationPage();
void displayUnitsPage(int selectedOption);
void readEncoder();
void readButton();
void saveCalibration();
void loadCalibration();
void saveUnits();
void loadUnits();
float calculateAltitude(double pressure);
float convertToFeet(float meters);
void setupEncoder();
void setupButton();
void setup() {
Serial.begin(115200);
// Initialize MS5611 sensor
Serial.println("Initialize MS5611 Sensor");
while (!ms5611.begin()) {
Serial.println("Could not find a valid MS5611 sensor, check wiring!");
delay(500);
}
// Initialize OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
display.clearDisplay();
display.display();
delay(500);
// Initialize Encoder
setupEncoder();
// Initialize Button
setupButton();
// Load calibration and units from EEPROM
loadCalibration();
loadUnits();
// Set initial reference pressure
currentReferencePressure = ms5611.readPressure();
Serial.print("Initial Reference Pressure: ");
Serial.println(currentReferencePressure);
Serial.print("Initial Units: ");
Serial.println(currentUnits == 0 ? "Meters" : "Feet");
displayMainPage();
}
void loop() {
readButton();
readEncoder(); // Call the encoder reading function in the loop
switch (currentMenuState) {
case MAIN_PAGE:
displayMainPage();
break;
case SETTINGS_MENU: {
static int selectedSetting = 0;
if (encoderCount != 0) {
selectedSetting -= encoderCount;
if (selectedSetting < 0) selectedSetting = 2;
if (selectedSetting > 2) selectedSetting = 0;
encoderCount = 0;
displaySettingsMenu(selectedSetting);
}
if (buttonPressCount == 1) {
buttonPressCount = 0; // Reset after action
if (selectedSetting == 0) {
currentMenuState = CALIBRATION_PAGE;
currentSetAltitude = calculateAltitude(ms5611.readPressure()); // Initialize with current calculated altitude
displayCalibrationPage();
} else if (selectedSetting == 1) {
currentMenuState = UNITS_PAGE;
displayUnitsPage(currentUnits);
} else if (selectedSetting == 2) {
currentMenuState = MAIN_PAGE;
displayMainPage();
}
}
break;
}
case CALIBRATION_PAGE:
if (encoderCount != 0) {
currentSetAltitude += encoderCount; // Adjust the 'Set Altitude' value
encoderCount = 0;
displayCalibrationPage();
}
if (buttonPressCount == 1) {
buttonPressCount = 0;
// Recalculate reference pressure based on the set altitude
long currentPressure = ms5611.readPressure();
currentReferencePressure = currentPressure / pow(1.0 - (currentSetAltitude / 44330.0), 5.255);
saveCalibration();
currentMenuState = SETTINGS_MENU;
displaySettingsMenu(0);
}
break;
case UNITS_PAGE: {
static int selectedUnit = currentUnits;
if (encoderCount != 0) {
selectedUnit -= encoderCount;
if (selectedUnit < 0) selectedUnit = 1;
if (selectedUnit > 1) selectedUnit = 0;
encoderCount = 0;
displayUnitsPage(selectedUnit);
}
if (buttonPressCount == 1) {
buttonPressCount = 0;
currentUnits = selectedUnit;
saveUnits();
currentMenuState = SETTINGS_MENU;
displaySettingsMenu(1);
}
break;
}
}
delay(1); // Small delay for overall loop
}
void displayMainPage() {
double realTemperature = ms5611.readTemperature();
long realPressure = ms5611.readPressure();
float altitudeMeters = calculateAltitude(realPressure);
float altitudeDisplay = (currentUnits == 1) ? convertToFeet(altitudeMeters) : altitudeMeters;
String unitString = (currentUnits == 1) ? "ft" : "m";
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(3, 50);
display.print("Tmp");
display.setTextSize(2);
display.setCursor(24, 46);
display.print(realTemperature, 1);
display.print("c");
display.setTextSize(1);
display.setCursor(3, 28);
display.print("Pre");
display.setTextSize(2);
display.setTextSize(2);
display.setCursor(24, 24);
display.print((int)(realPressure / 100));
display.print("hPa");
display.setTextSize(1);
display.setCursor(3, 7);
display.print("Alt");
display.setTextSize(2);
display.setCursor(24, 3);
display.print(altitudeDisplay, 0);
display.print(unitString);
display.display();
}
void displaySettingsMenu(int selectedOption) {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
String options[] = {"Calibration", "Units", "Back"};
for (int i = 0; i < 3; i++) {
display.setCursor(0, i * 16);
if (i == selectedOption) {
display.print("> ");
} else {
display.print(" ");
}
display.println(options[i]);
}
display.display();
}
void displayCalibrationPage() {
long realPressure = ms5611.readPressure();
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(4, 4);
display.print("Altitude Calibration");
display.setCursor(6, 50);
display.print("Pre");
display.setTextSize(2);
display.setCursor(30, 46);
display.print((int)(realPressure / 100));
display.print("hPa");
display.setTextSize(1);
display.setCursor(6, 27);
display.print("Alt");;
display.setTextSize(2);
display.setCursor(32, 23);
display.print(currentSetAltitude, 0);
display.print("m");
display.display();
}
void displayUnitsPage(int selectedUnit) {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.print("Select Units:");
display.setCursor(0, 16);
if (selectedUnit == 0) {
display.print("> Meters");
} else {
display.print(" Meters");
}
display.setCursor(0, 32);
if (selectedUnit == 1) {
display.print("> Feet");
} else {
display.print(" Feet");
}
display.display();
}
void readEncoder() {
// Rotary Encoder Inputs
int currentStateCLK = digitalRead(ENCODER_PIN_A);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1) {
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(ENCODER_PIN_B) != currentStateCLK) {
encoderCount--;
currentDir = "CCW";
} else {
// Encoder is rotating CW so increment
encoderCount++;
currentDir = "CW";
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(encoderCount);
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
}
void readButton() {
unsigned long currentTime = millis();
int buttonState = digitalRead(ENCODER_BUTTON_PIN);
if (buttonState == LOW) {
if (currentTime - lastButtonPress > buttonDebounceTime) {
buttonPressCount++;
lastButtonPress = currentTime;
}
}
if (currentMenuState == MAIN_PAGE && buttonPressCount >= 2) {
currentMenuState = SETTINGS_MENU;
displaySettingsMenu(0);
buttonPressCount = 0; // Reset the count after entering the menu
} else if (currentMenuState != MAIN_PAGE && buttonPressCount >= 1) {
// For other menus, a single press acts as "select" or "save"
// The action is handled within the respective menu's state logic
buttonPressCount = 1; // Ensure it's treated as a single action
} else if (buttonState == HIGH) {
// Reset the count if the button is released for a while
if (currentTime - lastButtonPress > 200) { // Adjust this delay as needed
buttonPressCount = 0;
}
}
}
void saveCalibration() {
EEPROM.put(EEPROM_REFERENCE_PRESSURE, currentReferencePressure);
Serial.println("Calibration saved to EEPROM");
}
void loadCalibration() {
if (EEPROM.read(EEPROM_REFERENCE_PRESSURE) != 0xFF) { // Check if EEPROM has been written before
EEPROM.get(EEPROM_REFERENCE_PRESSURE, currentReferencePressure);
Serial.print("Calibration loaded from EEPROM: ");
Serial.println(currentReferencePressure);
} else {
Serial.println("No calibration data in EEPROM, using default.");
}
}
void saveUnits() {
EEPROM.write(EEPROM_UNITS, currentUnits);
Serial.print("Units saved to EEPROM: ");
Serial.println(currentUnits == 0 ? "Meters" : "Feet");
}
void loadUnits() {
currentUnits = EEPROM.read(EEPROM_UNITS);
if (currentUnits != 0 && currentUnits != 1) {
currentUnits = 0; // Default to meters if invalid value
Serial.println("Invalid units in EEPROM, using default (Meters).");
} else {
Serial.print("Units loaded from EEPROM: ");
Serial.println(currentUnits == 0 ? "Meters" : "Feet");
}
}
float calculateAltitude(double pressure) {
// Simplified altitude calculation based on pressure and reference pressure
// Assumes standard atmospheric conditions
return 44330.0 * (1.0 - pow(pressure / currentReferencePressure, 0.1903));
}
float convertToFeet(float meters) {
return meters * 3.28084;
}
void setupEncoder() {
// Set encoder pins as inputs with pull-up resistors
pinMode(ENCODER_PIN_A, INPUT_PULLUP);
pinMode(ENCODER_PIN_B, INPUT_PULLUP);
// Initialize the last state of CLK for the encoder reading logic
lastStateCLK = digitalRead(ENCODER_PIN_A);
}
void setupButton() {
// Set the button pin as an input with a pull-up resistor
pinMode(ENCODER_BUTTON_PIN, INPUT_PULLUP);
}

步骤3:接线


我们可以通过准备最终组装的部件来开始组装过程

我在模块的每个引脚上焊接了一根 4 厘米细线

或者,您可以将 10k 下拉电阻焊接到编码器的 A 和 B 引脚。

最后,将所有东西连接到 XIAO

 

03.jpg03-.jpg03-2.jpg03-3.jpg03-4.jpg03-5.jpg
 

步骤4:最终组装

我们将线束和组件放入外壳中。

首先,我们可以从编码器开始。将编码器放入3D打印的插槽中,并使用两个螺母将编码器固定在3D打印件上。

在 XIAO 上面涂点胶水,然后把它放在主体上

将胶水涂抹在压力传感器模块的 3D 打印插槽上,然后将模块插入插槽。

将电源开关连接至电池

将电池粘好并放置在主机上

将电源开关放入侧壁上的 3D 打印插槽中

要完成接线,请将电池线和电源开关线焊接到 XIAO 的电池输入端

将显示模块安装到前盖上并熔化塑料以固定所有部件。

将字体帽放在主体上

为了完成我们的构建,将 3D 打印旋钮连接到编码器。

 

04.jpg
04-.jpg
04-1.jpg
04-2.jpg
04-3.jpg
04-4.jpg
04-5.jpg
04-6.jpg
04-7.jpg
04-8.jpg
04-9.jpg

第五步:操作


用户可以使用滑动开关打开设备电源。务必确保在电池充电过程中设备处于开启状态。

启动后,主屏幕将显示温度、压力和高度。如果用户按住编码器按钮 2 秒钟,将进入设置菜单,其中包含三个选项:校准、单位和返回主页。

如果用户选择“校准”选项,系统将跳转至校准页面。用户可以使用旋转编码器调整海拔高度。他们可以查看当前气压并相应地设置海拔高度。调整完成后,用户只需按一下编码器按钮,即可将数值保存到 ESP32 的 EEPROM 中,然后返回设置页面。

如果用户选择单位选项,他们将能够在英尺和米之间更改海拔的测量单位。
 

05.jpg

项目链接:https://www.instructables.com/Altisense-Compact-Altimeter-Using-ESP32/
项目作者:印度 戈库克斯(gokux)
(当我探索令人兴奋的电子世界时,我不禁对我所发现的令人难以置信的事物感到惊奇和惊叹!)
项目视频(1分钟35秒):https://www.youtube.com/watch?v=fqXnK-mnJYk
项目库:https://github.com/jarzebski/Arduino-MS5611
3D文件:https://content.instructables.com/FHT/GTU0/MA2IPPFH/FHTGTU0MA2IPPFH.f3d

 

00134-.gif

附件

评论

user-avatar
icon 他的勋章
    展开更多