基于ARDUINO ESP32 霍尔传感器数据采集及SD卡存储系统设计

基于ARDUINO ESP32 霍尔传感器数据采集及SD卡存储系统设计

前言

​ 因大学毕业10+年一直从事的是汽车零部件的开发工作,主要是汽车内外饰的结构设计,研发管理工作。对单片机的知识(当时学的是8051系列,程序是用汇编写的)已经都还给老师了。打算以小白的身份边学边做,就索性买了一套周边套件。

任务目标:为了获取设备在工作过程中的流量曲线。
开发工具:Arduino ESP32 IoT 开发套件
编译器:Arduino IDE 2.3.2
仿真器:Proteus Professional 8.17 SP2 (为了直观的了解某些器件的工作状态,它提供了很多虚拟仪器,如示波器,波形发生器,电压表,电流表等)。
示波器:某宝上买的 DIY散装版,焊接,装配。粗略的看一下波形电压信息。

1. 硬件系统设计

1.1 ARDUINO 开发板

开发板选用目前比较流行的Arduino单片机,考滤到后继的无线数据传输,选了某宝上卖的最好的ESP32系列,此处注意避坑,当时没注意问版本,结果买了ESP32-D0WDQ6-V3 ,查看官方文件才发现已经被列入“不推荐用于新设计 (NRND)”,建议买最新版本。
在这里插入图片描述

1.2 霍尔传感器

本方案采用使用成熟的 单极性叶轮式霍尔传感器,当水流通过时叶轮同步转动(叶轮内嵌磁铁),每旋转一周磁场强度发生两次变化。
提示:本案例采用下降沿触发,每旋转一周产生一个下降沿脉冲信号,用于计数。
在这里插入图片描述

线序描述
VCC +5V
GND
S 信号线

在这里插入图片描述

流量计的流量特性
最小感知:流量为300ml/min (流量达到300ml/min 水流才能推动叶轮转动,输出脉冲信号)
以下为脉冲数与流量的对应关系(已用专业设备校准),从数据上看脉冲与流量的对应关系非常接近线性.
在这里插入图片描述
脉冲(X)与流量(Y)简化为:
Y=2X (150<=X<=2750)
在这里插入图片描述

1.3 SD卡存储模块ADAFRUIT DATA LOGGER SHIELD

​ 刚好手上有一张10年前的SD卡,就在某宝上买了一个SD卡模块,到手后发现引脚的排序和ESP32的开发板没上对应上,问商家要资料商家有也没,后来看到其它商家的资料介绍,最后发现这块板源于 ADAFRUIT DATA LOGGER SHIELD。

上官网(https://learn.adafruit.com/adafruit-data-logger-shield)找资料里面有原理图及PCBA (鹰酱家的板子可真贵 $13.95 )
在这里插入图片描述
在这里插入图片描述

SHIELD 上的与SD卡有关的引脚对应如下:

SHIELD-PIN说明
10CS
11MOSI
12MISO
13SCK

<SD 卡的引脚补充说明>

在这里插入图片描述

2. 电路设计

2.1 霍尔传感器的调理电路

注意:本案例在开始时未注意到霍尔传感器的信号的最高输出电压仅为1.6V的脉冲信号,在后续的程序调试过程中无法读取脉冲信号(高电平过低,无法识别为有效输入)。通过学习套装内的其它传感器的电路,引用了LM393调理电路。
LM393的基本原理演示仿真如下(部分电路做了简化):
其工作原理:
当LM393的 3号引脚的电压 > 2号引脚的电压时,1号引脚输出高电平;
当LM393的 3号引脚的电压 < 2号引脚的电压时,1号引脚输出低电平;
在这里插入图片描述

2.2 ADAFRUIT DATA LOGGER SHIELD 模块与ESP32的连接

​ 从SD.h文件及SD_TEST的示例文件内得知 SD与ESP32的通讯采用SPI。根据官方文件SHIELD模块上有电压调理电路,建议真接使用+5V供电。

SD-PIN引脚名称说明ESP32-PINSHIELD-PIN
1(data3)CSSD卡片选。低电平使能(默认使能)5(CS)/
2(cmd)MOSI数据输人口23(MOSI)11
3(vss)VSS1GNDGNDGND
4(vdd)VDD电源 ++5 V+5V
5(clk)SCLKSPI时钟18(SCK)13
6(vss)VSS2GNDGND/
7(data0)MISO数据输出口19(MISO)12
8(data1)NCNOT Connected/
9(data2)NCNOT Connected/

在这里插入图片描述

2.程序设计

2.1 所引用的库文件

#include <Wire.h>
#include <LiquidCrystal_I2C.h>  // 包含LCD1602 I2C库
#include <SD.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"

2.2 引脚定义

const int hallPin = 17;  // 霍尔流量计信号引脚
const int ledPin = 14;   // LED灯引脚

2.3 变量定义

 ---变量定义 START--
// 用于计算的变量
volatile unsigned long pulseCount = 0;  // 使用volatile关键字,确保在中断中修改的值能被主循环正确读取
unsigned long previousMillis = 0;
const long interval = 1000;  // 1秒间隔
unsigned long lastDebounceTime = 0;
int RPM = 0;
int estimatedFlow = 0;
unsigned long lastInterruptTime = 0;
unsigned long currentRpm = 0;
long Pulse_Duration;
const int CHIP_SELECT = 5;  // ESP32的SD卡片选引脚

//文件类
File dataFile;

// 霍尔传感器去抖动变量
boolean lastState = HIGH;
boolean currentState;
unsigned long debounceDelay = 1;
char flowStr[10]; // 存储转换后的字符串
 ---变量定义 END--

2.4 LCD1602

// LCD1602设置
LiquidCrystal_I2C lcd(0x27, 16, 2);  // 地址0x27为LCD1602的默认I2C地址

2.5 主程序

 --- 主程序 --- START 
// 初始化
void setup() {
  // 初始化串行通信,用于调试
  Serial.begin(115200);
  // 初始化LCD1602
  lcd.init();
  lcd.backlight();

  // 设置引脚模式
  pinMode(hallPin, INPUT_PULLUP);  // 霍尔传感器,启用内部上拉
  pinMode(ledPin, OUTPUT);         // LED灯

  // 为霍尔传感器设置中断
  attachInterrupt(digitalPinToInterrupt(hallPin), Pulse_Counter_ISR, FALLING);
  previousMillis = millis();

  if(!SD.begin()){
    Serial.println("初始化SD卡失败");
    return;
  }
  Serial.println("初始化SD卡成功");


  // 检查FLOW.CSV是否存在,不存在则创建
  if (!SD.exists("/FLOW002.CSV")) {
    dataFile = SD.open("/FLOW002.CSV", FILE_WRITE);
    dataFile.println("Timestamp,Flow"); // 写入CSV头 
    dataFile.close();
    Serial.println("表头写入成功");
    }

}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    displayPulseCount();  // 每秒显示实时脉冲数
    RPM = calculateRPMFromPulseCount(pulseCount);
    estimatedFlow = 2 * RPM;
    pulseCount = 0;  // 重置脉冲计数,准备下一轮
    previousMillis = currentMillis;
    Pulse_Duration = pulseIn(hallPin, LOW);

    sprintf(flowStr, "%d", estimatedFlow); // 转换为字符串
    // 打开FLOW.CSV文件以追加模式写入
    dataFile = SD.open("/FLOW002.CSV", FILE_APPEND);
    if (dataFile) {
      // 写入当前时间戳和流量值,这里简单使用millis()作为时间戳
      dataFile.print(millis());
      dataFile.print(",");
      dataFile.println(flowStr);
      dataFile.close(); // 关闭文件
      Serial.println("数据写入OK");
    }
    else
    {
      Serial.println("Error opening file");
    }

  }
  delay(1000);  // 等待一秒钟
}

// 中断服务程序,用于计数霍尔流量计的脉冲
void Pulse_Counter_ISR() {
  if ((millis() - debounceDelay) >= lastDebounceTime) {
    if (digitalRead(hallPin) == LOW) {
      pulseCount++;
      lastDebounceTime = millis();
    }
  }
}

// 根据pulseCount和时间间隔计算RPM的函数
unsigned long calculateRPMFromPulseCount(unsigned long pulseCount) {
  return (pulseCount * 60000) / interval;
}


// 显示实时脉冲数到LCD
void displayPulseCount() {
  lcd.clear();
  lcd.print("Pulses: ");
  lcd.print(pulseCount);
  lcd.setCursor(0, 1);
  lcd.print("Flow: ");
  lcd.print(estimatedFlow);
  lcd.print(" mL/min");

  digitalWrite(ledPin, LOW);
  Serial.println(pulseCount);
  Serial.println(Pulse_Duration);
  Serial.println(RPM);
  Serial.println(estimatedFlow);
}

经验总结:

  1. 在使用霍尔传感器件时,需先熟读产品的DATASHEET, 了解其输入输出特性。在本设计开始时只是用简易的示波器看了脉冲信号,忽略了其电压输出特性,其输出的电压仅为1.6V,不能变识别为高电平。

  2. 注意变量的类型,在调试的过程中 将变量“estimatedFlow ”写入SD卡的过程中报错。dataFile.print()内的变量类型为string。而“estimatedFlow ”的变量的类型为int 。




------



## 经验总结:

1. 在使用霍尔传感器件时,需先熟读产品的DATASHEET, 了解其输入输出特性。在本设计开始时只是用简易的示波器看了脉冲信号,忽略了其电压输出特性,其输出的电压仅为1.6V,不能变识别为高电平。

2. 注意变量的类型,在调试的过程中 将变量“estimatedFlow ”写入SD卡的过程中报错。dataFile.print()内的变量类型为string。而“estimatedFlow ”的变量的类型为int 。

   



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值