当前位置: 首页 > news >正文

基于arduino uno r3主控的环境监测系统设计-1

准备设计arduino uno r3为主控的环境监测系统,通过传感器采集TVOC(总挥发性有机物)、HCHO(甲醛)和eCO2(等效二氧化碳)数据,并显示在LCD屏幕上,同时支持数据记录到SD卡,以及通过旋转编码器进行交互。

最终呈现效果:

结合RTC时间戳将数据记录至SD卡,并通过LCD显示屏和旋转编码器实现用户交互。系统具备三屏数据显示智能SD卡管理时间设置数据记录控制等核心功能。


代码结构分析:

  1. 包含的库:Wire(I2C通信)、LiquidCrystal_I2C(I2C LCD控制)、SoftwareSerial(软串口,用于与传感器通信)、SdFat(SD卡操作)、RTClib(实时时钟)。
  2. 引脚定义:TVOC传感器使用软串口(RX, TX)、SD卡片选、旋转编码器(CLK, DT, SW)、记录按钮、LED。
  3. 全局对象:软串口对象、LCD对象、SD卡对象、RTC对象。
  4. 全局变量:用于数据解析、传感器数据存储、显示控制、编码器状态、记录状态、硬件状态标志等。
  5. 函数:
    • setup():初始化系统,包括串口、LCD、RTC、传感器、SD卡、编码器、记录按钮等。
    • initLCD():初始化LCD,尝试多个I2C地址。
    • initSDCard():初始化SD卡,并检查其功能。
    • checkSDFunctional():检查SD卡功能(写一个测试文件并读取验证)。
    • ensureDataFile():确保数据文件存在,并写入表头。
    • loop():主循环,包括硬件状态检查、接收数据、处理编码器和按钮、更新显示、定期检查SD卡状态。
    • checkHardwareStatus():检查硬件状态(RTC、SD卡)。
    • checkSDStatus():检查SD卡状态(物理存在和功能)。
    • updateTimeDisplay():更新当前时间显示。
    • handleRecordButton():处理记录按钮的按下事件(切换记录状态)。
    • handleEncoder():处理旋转编码器的旋转和按钮事件(切换屏幕、进入时间设置模式)。
    • enterSetMode():进入时间设置模式。
    • exitTimeSetMode():退出时间设置模式,更新RTC时间。
    • receiveData():从TVOC传感器接收数据。
    • processData():处理接收到的传感器数据,验证校验和,并存储到结构体。
    • logSensorData():将传感器数据记录到SD卡。
    • loadLastRecord():从SD卡加载最后一条记录。
    • parseLastRecord():解析最后一条记录。
    • displaySDStatus():在LCD上显示SD卡状态(使用自定义字符)。
    • updateDisplay():根据当前屏幕索引更新显示内容。
    • displayTVOCHCHO():显示TVOC和HCHO数据。
    • displayECO2():显示eCO2数据。
    • displayLastRecord():显示最后记录的数据和记录状态。
    • displaySetItem():在设置模式下显示当前设置项。
    • handleSetMode():处理设置模式下的编码器旋转事件。
    • adjustTimeValue():调整时间值(根据设置项)。
    • daysInMonth():计算某年某月的天数。

功能概述:


该设备通过软串口与TVOC传感器通信,获取TVOC、HCHO和eCO2数据。这些数据会显示在LCD屏幕上,用户可以通过旋转编码器切换显示屏幕(三个屏幕:TVOC+HCHO、eCO2、最后记录)。同时,设备支持将数据记录到SD卡(记录状态由记录按钮控制)。设备还包含一个实时时钟(RTC)用于时间戳。旋转编码器长按可以进入时间设置模式,调整年、月、日、时、分、秒。

详细分析:

  1. 初始化(setup):

    • 初始化串口(用于调试)。
    • 初始化板载LED(用于指示状态)。
    • 初始化LCD(尝试多个I2C地址)。
    • 初始化RTC(如果失败则显示错误,如果RTC未运行则使用编译时间设置)。
    • 初始化TVOC传感器的软串口。
    • 初始化SD卡(并确保数据文件存在)。
    • 初始化编码器引脚(上拉输入)。
    • 初始化记录按钮引脚(上拉输入)。
    • 创建自定义字符(SD卡图标)。
    • 显示初始化完成信息。
  2. 主循环(loop):

    • 检查硬件状态(每5秒检查一次RTC和SD卡)。
    • 接收传感器数据(通过软串口,按照特定帧格式解析)。
    • 处理编码器事件(旋转和按钮,包括短按切换屏幕,长按进入时间设置模式)。
    • 处理记录按钮(按下切换记录状态,并切换到记录状态屏幕)。
    • 每500ms更新显示(包括时间和传感器数据)。
    • 每3秒检查SD卡状态(物理存在和功能状态)。
  3. 数据记录:

    • 当记录使能(recordingEnabled为true)且传感器数据有效且RTC可用时,将数据写入SD卡。
    • 数据文件为CSV格式,包含UNIX时间戳、日期时间、TVOC、HCHO、CO2。
    • 每次记录后更新最后一条记录(lastRecord结构体)。
  4. 显示:

    • 三个屏幕:
      Screen0: TVOC和HCHO的数值(第一行TVOC,第二行HCHO)。
      Screen1: eCO2的数值和日期时间(第一行eCO2,第二行日期和时间)。
      Screen2: 最后记录的数据(包括TVOC、eCO2、记录状态、SD卡状态)。
    • 在LCD右上角显示SD卡状态(自定义图标:正常为SD图标,物理存在但功能异常为'!',不存在为'X')。
  5. 时间设置模式:

    • 长按编码器按钮进入时间设置模式。
    • 通过旋转编码器调整当前设置项(年、月、日、时、分、秒)。
    • 每按一次按钮切换一个设置项,设置完所有项后退出设置模式并更新RTC时间。
  6. SD卡状态管理:

    • 定期检查SD卡物理存在(通过尝试初始化)和功能状态(通过读写测试)。
    • 状态变化时更新显示。
  7. 错误处理:

    • 初始化失败时在串口输出错误信息,并在LCD上显示(如RTC错误)。
    • SD卡写入失败时标记为功能异常,并点亮LED指示错误。

修复和增强:

代码中有一些修复和增强,例如SD卡状态检测、时间设置等。

需要单独考虑:

  • 使用条件判断来确保硬件可用性(如lcdInitialized, rtcAvailable等)。
  • 使用volatile关键字修饰编码器相关变量(因为它们在中断服务函数中修改,但本代码中并未使用中断,而是在主循环中查询,所以实际上可以不用volatile,但保留也无妨)。
  • 使用状态机思想处理编码器旋转和按钮事件。
  • 记录按钮和编码器按钮都做了防抖处理。
  • 在记录数据时,如果打开文件失败,会将sdCardFunctional置为false,然后在下一次检查时尝试恢复。

代码实现 


🛠️ ​硬件架构
  1. 核心控制器​:Arduino开发板
  2. 传感器模块​:TVOC传感器(软串口通信)
  3. 存储模块​:SD卡(SPI接口)
  4. 显示模块​:I2C LCD1602液晶屏
  5. 用户输入​:旋转编码器(CLK/DT/SW引脚) + 记录按钮
  6. 时间模块​:DS1307 RTC时钟
  7. 状态指示​:LED指示灯

🧩 ​代码结构分析
🔌 ​1. 初始化设置(setup())​
void setup() {// 串口调试初始化Serial.begin(9600); // 硬件初始化链initLCD();       // LCD显示初始化[11](@ref)initRTC();       // 实时时钟初始化[6](@ref)initSDCard();    // SD卡系统初始化[10](@ref)initSensors();   // 传感器通信初始化// 用户输入设备初始化pinMode(ENC_CLK, INPUT_PULLUP);  // 编码器CLK引脚[9](@ref)pinMode(RECORD_BTN, INPUT_PULLUP); // 记录按钮// 自定义字符创建(SD图标)lcd.createChar(0, sdIcon);  // 创建SD卡图标[11](@ref)
}

关键点​:

  • 采用模块化初始化策略,各硬件独立初始化
  • LCD支持多地址自动探测​(0x27/0x3F)
  • RTC首次启动时自动注入编译时间
🔁 ​2. 主循环逻辑(loop())​
void loop() {checkHardwareStatus();    // 硬件健康监测(5秒间隔)receiveData();            // 传感器数据采集handleEncoder();          // 编码器事件处理[9](@ref)handleRecordButton();     // 记录按钮逻辑if(needDisplayUpdate()) { // 500ms显示刷新updateTimeDisplay();    // 更新时间显示[6](@ref)updateDisplay();        // 刷新LCD内容}checkSDStatus();          // SD卡状态监测(3秒间隔)[10](@ref)
}

核心机制​:

  • 分层式任务调度​:硬件监控、数据采集、用户交互分离
  • 节流机制​:显示刷新(500ms)、SD检测(3s)避免资源争用
  • 状态机驱动​:通过currentScreen管理三屏显示
📡 ​3. 传感器数据处理
void processData() {byte checksum = 0;for(int i=0; i<8; i++) checksum += rawData[i];if(checksum != rawData[8]) { // 校验和验证Serial.println("TVOC checksum error!");return;}// 数据解析(大端序)currentData.tvoc = (rawData[2] << 8) | rawData[3]; currentData.hcho = (rawData[4] << 8) | rawData[5];currentData.eco2 = (rawData[6] << 8) | rawData[7];logSensorData(); // 有效数据记录
}

协议特性​:

  • 帧结构​:0x2C头 + 8字节数据 + 1字节校验和
  • 错误处理​:校验失败自动丢弃数据包
  • 数据映射​:TVOC/HCHO单位µg/m³,eCO₂单位ppm
💾 ​4. SD卡高级管理
void checkSDStatus() {// 物理存在检测bool physicalPresent = SD.begin(SD_CS_PIN); if(physicalPresent != sdCardPresent) { // 状态变化检测if(sdCardPresent) {sdCardFunctional = checkSDFunctional(); // 功能测试if(sdCardFunctional) ensureDataFile();  // 文件系统验证[10](@ref)}}// 自动恢复机制if(sdCardPresent && !sdCardFunctional) {sdCardFunctional = checkSDFunctional(); // 定期重试}
}

创新设计​:

  • 双状态检测​:物理存在(sdCardPresent) + 功能状态(sdCardFunctional)
  • 智能恢复​:定期尝试重新挂载失效SD卡
  • 文件保障​:自动创建CSV文件并写入表头
  • 图标化显示​:自定义SD状态字符(正常/异常/缺失)
⏰ ​5. 时间管理系统
void handleSetMode() {if(encTurned) {int delta = (digitalRead(ENC_DT) != lastClkState) ? -1 : 1;adjustTimeValue(delta); // 时间值调整switch(setIndex) { // 多级设置菜单[9](@ref)case 0: newTime = DateTime(newTime.year()+delta, ...); break;case 1: // 月份(带天数边界检查)uint8_t newMonth = constrain(month+delta, 1, 12);uint8_t newDay = min(day, daysInMonth(newMonth, year));...}}
}

交互特性​:

  • 长按触发​:编码器按钮长按>1秒进入设置模式
  • 循环设置​:年→月→日→时→分→秒→保存
  • 智能边界​:自动计算每月天数(含闰年)
📊 ​6. 数据显示系统
void updateDisplay() {switch(currentScreen) {case 0: // TVOC+HCHO同屏显示lcd.print("TVOC:"); lcd.print(currentData.tvoc); lcd.print("HCHO:"); lcd.print(currentData.hcho);break;case 1: // eCO2与日期时间lcd.print("eCO2:"); lcd.print(currentData.eco2);snprintf(dateBuffer, "%02d%02d%02d", now.year%100, now.month, now.day);break;case 2: // 最后记录与状态lcd.print("TV:"); lcd.print(lastRecord.tvoc);lcd.print("CO2:"); lcd.print(lastRecord.eco2);lcd.print(recordingEnabled ? "ON" : "OFF");displaySDStatus(); // 右下角状态图标[11](@ref)}
}

显示优化​:

  • 多屏切换​:编码器短按循环切换三个界面
  • 动态更新​:时间显示每秒刷新,数据每500ms更新
  • 状态集成​:SD图标(0)/警告(!)/缺失(X)直观指示

⚙️ ​系统创新设计
  1. SD卡智能恢复系统

    • 实现物理检测→功能验证→自动恢复的全链路管理
    • 采用双状态机模型(prevSdCardPresent/sdCardPresent)
    • 文件操作增加写后验证(创建测试文件校验完整性)
  2. 时间设置容错机制

    void adjustTimeValue(int delta) {case 1: // 月份调整uint8_t newDay = min(day, daysInMonth(newMonth, year));
    • 自动计算当月最大天数(含闰年判断)
    • 防止设置无效日期(如2月30日)
  3. 数据记录优化

    void logSensorData() {if(!recordingEnabled || !sdCardFunctional) return;File dataFile = SD.open("sensor.csv", FILE_WRITE);dataFile.print(unixTime); // UNIX时间戳[6](@ref)dataFile.print(currentData.tvoc); ...
    • 双时间戳存储:人类可读时间+UNIX时间戳
    • CSV格式标准化:兼容Excel/LibreOffice分析
  4. LCD显示优化

    • 自定义字符​:8×5像素SD图标设计
    • 空间复用​:15×0位置显示状态图标
    • 多屏布局​:科学分配16×2显示空间

📝 ​改进建议
  1. 增加传感器异常处理

    // 在processData()中增加:
    if(currentData.tvoc > 30000) { // 异常值判定Serial.println("Sensor out of range!");runSelfTest(); // 触发自检
    }
  2. 实现数据缓存机制

    • SD卡不可用时启用RAM缓存
    • 恢复后自动写入缓存数据
  3. 添加低功耗模式

    void enterSleepMode() {if(noInteraction(5 * 60 * 1000)) { // 5分钟无操作lcd.noBacklight();setCpuFrequency(10); // ESP32特有节能}
    }
  4. 优化时间设置

    // 在displaySetItem()中:
    lcd.print("▲▼"); // 增加操作提示

🔍 ​关键引用说明
  • LCD初始化​:支持I2C地址自动探测
  • RTC时间设置​:编译时间注入机制
  • SD卡操作​:CSV文件创建与表头写入
  • 编码器控制​:旋转检测与按钮处理
  • 数据显示​:多屏切换与自定义字符

该设计实现了环境数据的采集→处理→存储→显示全链路管理,通过创新性的状态管理和错误恢复机制,显著提升了系统的可靠性和用户体验。

代码修正1

时间设置功能的设计存在以下问题,导致LCD显示内容在时间设置期间会被覆盖:

  1. 主显示循环冲突​:updateDisplay()函数每500毫秒运行一次,而该函数并不检查timeSetMode状态。因此即使在时间设置模式下,它仍会根据当前屏幕设置(0/1/2)覆盖显示内容。

  2. 显示刷新逻辑​:displaySetItem()仅在旋转编码器时才被调用(通过handleSetMode()),没有独立的周期性刷新机制。当没有编码器操作时,主显示循环会覆盖时间设置界面。

解决方案

updateDisplay()函数开头添加时间设置模式的专属显示逻辑:

void updateDisplay() {if (!lcdInitialized) return;// ============ 添加的代码 - 时间设置模式优先 ============if (timeSetMode) {displaySetItem();return;  // 进入设置模式后跳过常规显示}// =================================================// ... 其余原有代码保持不变 ...
}

具体修改说明

  1. 优先级控制​:

    if (timeSetMode) {displaySetItem();return;
    }
    • 首先检查是否处于时间设置模式
    • timeSetMode=true时立即显示设置界面
    • return语句确保退出函数,防止常规内容覆盖设置界面

完整测试代码

https://download.csdn.net/download/Medlar_CN/91477067

http://www.dtcms.com/a/297008.html

相关文章:

  • MySQL八股篇
  • Task 02: 深入理解MARL
  • WPF基础内容记录
  • RPG65. 制作死亡画面(一):制作ui
  • OSPF路由协议(单区域)
  • 解决 SQL 错误 [1055]:深入理解 only_full_group_by 模式下的查询规范
  • ddos 放在多个云主机,同时运行
  • 计算机网络知识点总结 (1)
  • Web前端:JavaScript indexOf()方法
  • 腾势N9再进化:智能加buff,豪华更对味
  • 无线通信资源分配相关算法
  • ESP32-S3学习笔记<6>:ADC的应用
  • AI助力 三步实现电子发票发票号码的提取
  • 小架构step系列24:功能模块
  • 【ResizeObserver】【页面布局】监听一个 div 元素的动态高度变化并同步设置另一个元素的高度
  • Windows环境下 Go项目迁移至Ubuntu(WSL) 以部署filebeat为例
  • 【数组的定义与使用】
  • 保障工业核心命脉:深度解读工业交换机QoS的“智能流量治理”之道
  • CMake ARGV变量使用指南
  • Python桌面版数独(五版)-优化选择模式触发新棋盘生成
  • OSPF 实验
  • RuoYi-Vue 项目 Docker 全流程部署实战教程
  • 中国、美国、欧盟、日本、英国临床试验API数据接口
  • Ⅹ—6.计算机二级综合题7---10套
  • Mac系统机能连接操控别的平台设备吗?能被远程操作吗?
  • Nginx快速入门及案例
  • 【安卓笔记】解决livedata粘性事件
  • 《Java语言程序设计》第2章复习题(2)
  • RePlugin 坑位使用原理与指南
  • 多源信息融合智能投资【“图神经网络+强化学习“的融合架构】【低配显卡正常运行】