项目概述和目标
本项目利用 Seeed Studio 的 Wio Terminal 开发板,结合 Grove 传感器模块,构建一个多传感器环境监测站,用于实时采集和显示环境数据。项目面向电子初学者、创客和教育工作者,目标是展示 Wio Terminal 的多功能性,帮助用户快速上手 Seeed 生态系统,同时提供一个可扩展的环境监测解决方案。
为什么做这个项目?
- 实际需求:环境监测在农业、教育和家庭场景中有广泛应用,例如监测空气质量、气体浓度、光照和噪音水平,帮助用户了解周围环境状况。
- 学习价值:通过本项目,初学者可以学习 Wio Terminal 的硬件连接、Arduino 编程基础,以及如何使用多种传感器。
- Seeed 产品展示:展示 Wio Terminal 和 Grove 模块的易用性和强大功能,体现 Seeed 在创客生态中的优势。
项目功能
- 实时采集以下数据: 空气质量:使用 Grove - Air Quality Sensor v1.3 检测环境空气质量。
- 气体浓度:使用 Grove - Gas Sensor (MQ2) 检测可燃气体(如 LPG、甲烷)。
- 光照强度:使用 Wio Terminal 内置光传感器。
- 声音强度:使用 Wio Terminal 内置麦克风。
- 在 Wio Terminal 的屏幕上交替显示两个界面: 界面 1:空气质量和 MQ2 气体的数值、状态和建议。
- 界面 2:光照和声音的数值、状态和建议。
- 每个界面显示 10 秒,自动切换,持续循环。
材料清单与环境设置
硬件需求
- Wio Terminal:Seeed Studio 开发板,带屏幕和内置传感器(加速度、光照、麦克风)。
- Grove - Air Quality Sensor v1.3:用于检测空气质量,连接到 A0/A1 端口。
- Grove - Gas Sensor (MQ2):用于检测气体浓度,连接到 A2 端口
- USB-C 数据线:用于供电和上传代码。
- 电脑:用于编写和上传代码。
软件需求
- Arduino IDE:
- Wio Terminal 板卡支持包:通过 Arduino IDE 的板卡管理器安装。
- 库文件:
#define MQ2_PIN A1 // MQ2 Gas Sensor
#define LIGHT_PIN WIO_LIGHT // Built-in Light Sensor
#define MIC_PIN WIO_MIC // Built-in Microphone
#include <TFT_eSPI.h> // Wio Terminal LCD library
#include <Air_Quality_Sensor.h> // Air Quality Sensor library
环境设置
- 安装 Arduino IDE: 下载并安装 Arduino IDE(Software | Arduino)。
- 启动 Arduino IDE。
- 添加 Wio Terminal 板卡支持: 打开 Arduino IDE,进入 File > Preferences。
- 在 Additional Boards Manager URLs 中添加以下链接: text
Copy
https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json
-
- 进入 Tools > Board > Boards Manager,搜索 Seeeduino SAMD,安装最新版本。
- 安装库: 进入 Tools > Manage Libraries。
- 搜索 LIS3DHTR,安装 Grove-3-Axis-Digital-Accelerometer-2g-to-16g-LIS3DHTR 库。
- TFT_eSPI 库随板卡支持包自动安装。
- 硬件连接: 使用 Grove 连接线将 Grove - Air Quality Sensor v1.3 连接到 Wio Terminal 的 A0/A1 端口。
- 使用 Grove 连接线将 Grove - Gas Sensor (MQ2) 连接到 Wio Terminal 的 A2 端口。
- 用 USB-C 数据线连接 Wio Terminal 和电脑。
连接示意图:
运行代码,测试结果截图:
应用示例和扩展思路
实际应用场景
- 家庭环境监测:放置在客厅或卧室,实时监测空气质量和气体浓度,提醒用户通风。
- 教育实验:用于学校课堂,教学生如何使用传感器和 Arduino 编程。
- 工业安全:部署在工厂,检测气体泄漏,触发报警。
扩展思路
- 联网功能: 使用 Wio Terminal 的 Wi-Fi 功能,将数据上传到云端(如 ThingSpeak)。
- 扩展代码示例: cpp
Copy
#include <WiFiNINA.h>
char ssid[] = "your-SSID";
char pass[] = "your-password";
WiFiClient client;
void connectWiFi() {
while (WiFi.status() != WL_CONNECTED) {
WiFi.begin(ssid, pass);
delay(5000);
}
}
- 数据存储: 使用 microSD 卡模块存储历史数据。
- 远程控制: 通过红外发射器(WIO_IR)控制家电,例如在空气质量超标时自动打开风扇。
- 声音提醒: 超标时播放不同音调的报警声: cpp
Copy
void playAlarm(int frequency, int duration) {
tone(WIO_BUZZER, frequency, duration);
delay(duration);
}
参考资源
- Wio Terminal 官方文档:Get Started with Wio Terminal | Seeed Studio Wiki
- Grove - Air Quality Sensor v1.3 文档:Grove - Air Quality Sensor v1.3 | Seeed Studio Wiki
- Grove - Gas Sensor (MQ2) 文档:Grove - Gas Sensor(MQ2) | Seeed Studio Wiki
- LIS3DHTR 库文档:https://github.com/Seeed-Studio/Seeed_LIS3DHTR
- Arduino 官方教程:https://www.arduino.cc/en/Tutorial/HomePage
参考代码:
以下是完整的 Arduino 代码,用于采集传感器数据并在屏幕上显示:
cpp
#include <TFT_eSPI.h> // Wio Terminal LCD library
#include <Air_Quality_Sensor.h> // Air Quality Sensor library
// Initialize TFT display
TFT_eSPI tft = TFT_eSPI();
// Initialize Air Quality Sensor on A0
AirQualitySensor airSensor(A0);
// Pin definitions
#define MQ2_PIN A1 // MQ2 Gas Sensor
#define LIGHT_PIN WIO_LIGHT // Built-in Light Sensor
#define MIC_PIN WIO_MIC // Built-in Microphone
// Display mode
enum DisplayMode { AIR_GAS, LIGHT_SOUND };
DisplayMode currentMode = AIR_GAS;
// Setup
void setup() {
Serial.begin(9600);
while (!Serial);
// Initialize sensor pins
pinMode(LIGHT_PIN, INPUT);
pinMode(MIC_PIN, INPUT);
pinMode(MQ2_PIN, INPUT);
// Initialize TFT display
tft.begin();
tft.setRotation(3); // Landscape mode
tft.fillScreen(TFT_BLACK);
tft.setTextSize(1); // Smallest font size for compact display
// Initialize Air Quality Sensor
Serial.println("Initializing Air Quality Sensor...");
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.drawString("Initializing...", 10, 10);
delay(20000); // Wait for sensor stabilization
if (airSensor.init()) {
Serial.println("Air Quality Sensor ready.");
tft.fillScreen(TFT_BLACK);
tft.drawString("Air Quality: OK", 10, 10);
} else {
Serial.println("Air Quality Sensor error!");
tft.fillScreen(TFT_BLACK);
tft.drawString("Air Quality: Error", 10, 10);
while (1);
}
// Initialize MQ2 Gas Sensor
Serial.println("MQ2 Gas Sensor ready.");
tft.drawString("MQ2 Gas: OK", 10, 20);
delay(1000);
// Initialize Light and Sound Sensors
Serial.println("Light and Sound Sensors ready.");
tft.drawString("Light & Sound: OK", 10, 30);
delay(1000);
// Clear screen for data display
tft.fillScreen(TFT_BLACK);
}
// Main loop
void loop() {
// Display current mode
if (currentMode == AIR_GAS) {
displayAirGas();
} else {
displayLightSound();
}
// Wait for 10 seconds
delay(10000);
// Switch to next mode
currentMode = (currentMode == AIR_GAS) ? LIGHT_SOUND : AIR_GAS;
}
// Display Air Quality and MQ2 Gas
void displayAirGas() {
// Clear screen
tft.fillScreen(TFT_BLACK);
// Draw decorative border and separator
tft.drawRect(5, 5, 310, 230, TFT_CYAN);
tft.drawFastHLine(10, 115, 300, TFT_CYAN);
// Air Quality
int airQuality = airSensor.slope();
int airValue = airSensor.getValue();
String airStatus, airAdvice;
uint16_t statusColor;
if (airQuality == AirQualitySensor::FORCE_SIGNAL) {
airStatus = "Critical Pollution";
airAdvice = "Ventilate immediately!";
statusColor = TFT_RED;
Serial.println("Critical pollution! Force signal active.");
} else if (airQuality == AirQualitySensor::HIGH_POLLUTION) {
airStatus = "High Pollution";
airAdvice = "Ventilate now!";
statusColor = TFT_RED;
Serial.println("High pollution!");
} else if (airQuality == AirQualitySensor::LOW_POLLUTION) {
airStatus = "Moderate Pollution";
airAdvice = "Consider ventilation.";
statusColor = TFT_YELLOW;
Serial.println("Moderate pollution!");
} else {
airStatus = "Fresh Air";
airAdvice = "Air quality is good.";
statusColor = TFT_GREEN;
Serial.println("Fresh air.");
}
// MQ2 Gas
int mq2Value = analogRead(MQ2_PIN);
float mq2Voltage = (mq2Value / 1023.0) * 5.0;
String mq2Status, mq2Advice;
uint16_t mq2Color;
if (mq2Value > 1000) {
mq2Status = "Dangerous";
mq2Advice = "Evacuate and check for leaks!";
mq2Color = TFT_RED;
} else if (mq2Value > 700) {
mq2Status = "High";
mq2Advice = "Possible gas leak!";
mq2Color = TFT_RED;
} else if (mq2Value > 300) {
mq2Status = "Moderate";
mq2Advice = "Check environment.";
mq2Color = TFT_YELLOW;
} else if (mq2Value > 100) {
mq2Status = "Low";
mq2Advice = "Monitor periodically.";
mq2Color = TFT_GREEN;
} else {
mq2Status = "Normal";
mq2Advice = "Gas level normal.";
mq2Color = TFT_GREEN;
}
// Display data
int yPos = 10;
// Air Quality
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.drawString("Air Quality", 10, yPos);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Data: " + String(airValue) + " units", 10, yPos + 15);
tft.setTextColor(statusColor, TFT_BLACK);
tft.drawString("Status: " + airStatus, 10, yPos + 30);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Advice: " + airAdvice, 10, yPos + 45);
// MQ2 Gas
yPos += 65;
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.drawString("MQ2 Gas", 10, yPos);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Data: " + String(mq2Value) + ", " + String(mq2Voltage, 2) + "V", 10, yPos + 15);
tft.setTextColor(mq2Color, TFT_BLACK);
tft.drawString("Status: " + mq2Status, 10, yPos + 30);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Advice: " + mq2Advice, 10, yPos + 45);
// Serial output
Serial.print("Air Quality Value: ");
Serial.println(airValue);
Serial.print("MQ2 Value: ");
Serial.println(mq2Value);
Serial.print("MQ2 Voltage: ");
Serial.println(mq2Voltage);
Serial.println();
}
// Display Light and Sound
void displayLightSound() {
// Clear screen
tft.fillScreen(TFT_BLACK);
// Draw decorative border and separator
tft.drawRect(5, 5, 310, 230, TFT_CYAN);
tft.drawFastHLine(10, 115, 300, TFT_CYAN);
// Light
int lightValue = analogRead(LIGHT_PIN);
String lightStatus, lightAdvice;
uint16_t lightColor;
if (lightValue < 100) {
lightStatus = "Very Dark";
lightAdvice = "Add strong lighting.";
lightColor = TFT_RED;
} else if (lightValue < 200) {
lightStatus = "Low Light";
lightAdvice = "Increase lighting.";
lightColor = TFT_YELLOW;
} else if (lightValue < 600) {
lightStatus = "Moderate Light";
lightAdvice = "Lighting is normal.";
lightColor = TFT_GREEN;
} else if (lightValue < 1000) {
lightStatus = "Bright Light";
lightAdvice = "Lighting is sufficient.";
lightColor = TFT_GREEN;
} else {
lightStatus = "Very Bright";
lightAdvice = "Reduce lighting.";
lightColor = TFT_YELLOW;
}
// Sound
long soundSum = 0;
const int samples = 50;
for (int i = 0; i < samples; i++) {
soundSum += analogRead(MIC_PIN);
delay(5);
}
int soundValue = soundSum / samples;
String soundStatus, soundAdvice;
uint16_t soundColor;
if (soundValue < 50) {
soundStatus = "Very Quiet";
soundAdvice = "Environment is calm.";
soundColor = TFT_GREEN;
} else if (soundValue < 100) {
soundStatus = "Quiet";
soundAdvice = "Sound level is good.";
soundColor = TFT_GREEN;
} else if (soundValue < 300) {
soundStatus = "Moderate Sound";
soundAdvice = "Sound level is normal.";
soundColor = TFT_GREEN;
} else if (soundValue < 500) {
soundStatus = "Loud";
soundAdvice = "Consider reducing noise.";
soundColor = TFT_YELLOW;
} else {
soundStatus = "Noisy";
soundAdvice = "Reduce noise immediately.";
soundColor = TFT_RED;
}
// Display data
int yPos = 10;
// Light
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.drawString("Light", 10, yPos);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Data: " + String(lightValue) + " units", 10, yPos + 15);
tft.setTextColor(lightColor, TFT_BLACK);
tft.drawString("Status: " + lightStatus, 10, yPos + 30);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Advice: " + lightAdvice, 10, yPos + 45);
// Sound
yPos += 65;
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.drawString("Sound", 10, yPos);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Data: " + String(soundValue) + " units", 10, yPos + 15);
tft.setTextColor(soundColor, TFT_BLACK);
tft.drawString("Status: " + soundStatus, 10, yPos + 30);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Advice: " + soundAdvice, 10, yPos + 45);
// Serial output
Serial.print("Light Value: ");
Serial.println(lightValue);
Serial.print("Sound Value: ");
Serial.println(soundValue);
Serial.println();
}