ESP32环境下基于SD卡与FTP实现温湿度数据采集与存储

news2024/12/25 2:49:47

本篇文章将介绍如何利用ESP32开发板结合SD卡与FTP服务器功能,实现温湿度数据的实时采集、存储与远程访问。项目代码基于Arduino IDE平台编写,主要依赖于以下库:

  • SPI
  • DHT
  • WiFi
  • time.h
  • ESP-FTP-Server-Lib
  • SD
  • SPIFFS
  • FS

1. 硬件连接与配置

1.1 所需要的硬件

  1. ESP32开发板在这里插入图片描述

  2. DHT22传感器模块

  3. SD卡模块

1.2 SD卡接口

ESP32通过SPI总线与SD卡进行通信,各引脚连接如下:

  • CS:GPIO 23
  • MOSI:GPIO 17
  • MISO:GPIO 2
  • SCK:GPIO 16

1.3 DHT22温湿度传感器

DHT22传感器连接至ESP32的GPIO 13引脚。

1.4 Wi-Fi模块

ESP32内置Wi-Fi模块,用于接入无线网络。此处设定的Wi-Fi信息如下:

  • SSIDxxx
  • Passwordxxx

1.5 NTP服务器与时区设置

为了获取准确的本地时间,通过NTP协议从阿里云NTP服务器同步时间,并设定时区为中国标准时间(UTC+8):

  • NTP Serverntp1.aliyun.com
  • GMT Offset8 * 3600
  • Daylight Offset0

1.6 FTP服务器配置

创建一个FTP服务器,供远程设备访问存储在ESP32上的温湿度数据。FTP登录信息如下:

  • Usernameftp
  • Passwordftp

2. 代码详解

2.1 引入库与定义常量

#include <Arduino.h>
/*
  sd Card Interface code for ESP32
  SPI Pins of ESP32 sd card as follows:
  CS    = 23;
  MOSI  = 17;
  MISO  = 2;
  SCK   = 16;
*/

#include <SPI.h>
#include <DHT.h>
#include <WiFi.h>
#include <time.h>
#include <ESP-FTP-Server-Lib.h>
#include <SD.h>
#include <SPIFFS.h>
#include <FS.h>

#define DHTPIN 13
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);
static float h = 0;
static float t = 0;

File myFile;

FTPServer ftp;
// 设置SPI Pins
const int CS = 23;
const int MOSI_PIN = 17;
const int MISO_PIN = 2;
const int SCK_PIN = 16;

SPIClass CustomSPI;

// 设置wifi信息,更换为你的wifi设置
const char *ssid = "xxx";
const char *password = "xxx";

// NTP相关参数,设置NTP服务器为阿里NTP服务器
const char *ntpServer = "ntp1.aliyun.com";
const long gmtOffset_sec = 8 * 3600;
const int daylightOffset_sec = 0;

char timeStr_bj[9];
char dateStr_bj[11];
char timeStr_gmt[9];

// FTP服务器配置
#define FTP_USER "ftp"
#define FTP_PASSWORD "ftp"

// 上一次执行的时间,初始化为0
unsigned long previousExecutionTime = 0;
// 执行间隔时间,单位为毫秒,这里设置为60000毫秒,即1分钟
const unsigned long INTERVAL_MS = 60000;

String printLocalTime()
{
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo))
  {
    Serial.println("Failed to obtain time");
    return "";
  }

  char formattedTimeBuffer[30];                                                                // 为格式化的时间字符串分配足够的缓冲区
  strftime(formattedTimeBuffer, sizeof(formattedTimeBuffer), "%Y-%B-%d  %H:%M:%S", &timeinfo); // 使用 strftime 将 timeinfo 转换为格式化的字符串

  String formattedTime = formattedTimeBuffer; // 将格式化后的字符串复制到 String 对象中
  return formattedTime;
}

/**
 * 将温度和湿度数据写入指定路径的CSV文件中。
 * 
 * @param path 指定的CSV文件路径。
 * @param timeStr 记录的时间字符串。
 * @param humidity 湿度值。
 * @param temperature 温度值。
 * 
 * 函数首先尝试打开文件以追加模式写入。如果文件成功打开,将依次写入时间字符串、湿度和温度值,
 * 每个数据项之间用逗号分隔,并在温度值后换行。写入完成后,关闭文件并打印完成信息。
 * 如果打开文件时出现错误,将打印错误信息和文件路径。
 */
void WriteCSVRecord(const char *path, const char *timeStr, float humidity, float temperature)
{
  myFile = SD.open(path, FILE_APPEND);

  if (myFile)
  {
    Serial.printf("Writing to %s ", path);
    myFile.print(timeStr);
    myFile.print(",");
    myFile.print(humidity, 2);
    myFile.print(",");
    myFile.println(temperature, 2);
    myFile.close();
    Serial.println("completed.");
  }
  else
  {
    Serial.println("error opening file ");
    Serial.println(path);
  }
}

void ReadFile(const char *path)
{
  myFile = SD.open(path);

  if (myFile)
  {
    Serial.printf("Reading file from %s\n", path);
    while (myFile.available())
    {
      // Serial.write(myFile.read());
      Serial.println("Read succeded");
    }
    myFile.close();
  }
  else
  {
    char error_msg[64];
    sprintf(error_msg, "error opening %s", path);
    Serial.println(error_msg);
  }
}

void environment()
{
  h = dht.readHumidity();
  t = dht.readTemperature();
  Serial.printf("Humidity: %f \n", h);
  Serial.printf("Temperature: %f \n", t);
}
void setup()
{
  Serial.begin(9600);
  delay(500);
  dht.begin();
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP()); // 记下运行时生成的IP地址,后面会用到
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  SPIFFS.begin(true);
  CustomSPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS);

  while (!Serial)
  {
    ;
  }

  Serial.println("Initializing sd card...");

  if (!SD.begin(CS, CustomSPI, 1000000))
  {
    Serial.println("initialization failed!");
    return;
  }
  else
  {
    Serial.println("SD card initialization succeeded.");
  }

  // // 配置FTP

ftp.addUser(FTP_USER, FTP_PASSWORD); // 添加FTP用户

ftp.addFilesystem("SD", &SD); // 注册SD卡文件系统
ftp.addFilesystem("SPIFFS", &SPIFFS); // 注册SPIFFS文件系统

ftp.begin(); // 启动FTP服务

Serial.println("FTP server started"); // 打印启动信息}


void loop()
{
  unsigned long currentMillis = millis(); // 获取当前毫秒时间戳

  // 检查是否已过去一分钟
  if (currentMillis - previousExecutionTime >= INTERVAL_MS)
  {
    previousExecutionTime = currentMillis; // 更新上一次执行的时间戳

    environment(); // 执行环境相关操作
    String localTimeStr = printLocalTime(); // 获取本地时间字符串

    // 如果温度和湿度都不为零,则写入CSV文件
    if (h != 0 && t != 0)
    {
      WriteCSVRecord("/record.csv", localTimeStr.c_str(), h, t);
    }
  }
  // 处理FTP请求
  ftp.handle();
}

2.2 如果你是使用的PlatfromIO IDE可以把以下内容复制到你的platformio.ini里

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 9600
lib_deps = 
	adafruit/DHT sensor library@^1.4.6
	adafruit/Adafruit Unified Sensor@^1.1.14
	peterus/ESP-FTP-Server-Lib@^0.14.0

3. FTP命令行操作

3.1 步骤说明

  1. 打开命令提示符(Windows)或终端(macOS/Linux)。

  2. 输入以下命令连接到 FTP 服务器(请将 IP 地址替换为实际 FTP 服务器的 IP 地址):

  3. 输入用户名(默认为 “ftp”)和密码(默认为 “ftp”)。

  4. 输入 “ls” 或 “dir” 查看文件列表。

  5. 输入 “cd” 命令切换目录,例如 “cd /SD” 进入 “/SD” 目录。

  6. 输入 “lcd” 命令切换本地目录,例如 “lcd E:\temp” 进入 “E:\temp” 目录。

  7. 使用 “get” 或 “recv” 命令从 FTP 服务器下载文件,例如 “get record.csv” 下载 “record.csv” 文件。

  8. 使用 “put” 或 “send” 命令将文件上传到 FTP 服务器,例如 “put local_file.txt” 上传 “local_file.txt” 文件。

  9. 输入 “bye” 或 “quit” 命令退出 FTP 客户端。
    10.更多命令直接输入"help"来了解

3.2 我用如下步骤下载了SD卡中的record.csv到电脑上:

C:\Users\User>ftp 192.168.55.203
连接到 192.168.55.203。
220--- Welcome to FTP Server for ESP32 ---
220---        By Peter Buchegger       ---
220 --           Version 0.1           ---
200 Ok
用户(192.168.55.203:(none)): ftp
331 OK. Password required
密码:
230 OK.
ftp> cd SD
250 Ok. Current directory is /SD
ftp> lcd "E:\temp"
目前的本地目录 E:\temp。
ftp> dir
200 PORT command successful
150 Accepted data connection
drwxr-xr-x 1 owner group             0 Jan 01  1970 System Volume Information
-rw-r--r-- 1 owner group            16 Jan 01  1970 test.txt
-rw-r--r-- 1 owner group        113466 Jan 01  1970 record.csv
226 3 matches total
ftp: 收到 208 字节,用时 0.0120.80千字节/秒。
ftp> recv record.csv
200 PORT command successful
150 Accepted data connection
226 File successfully transferred
ftp: 收到 113466 字节,用时 1.1796.81千字节/秒。
ftp>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1619922.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习与实现】机器学习概述

目录 一、机器学习的基本概念和方法&#xff08;一&#xff09;基本概念&#xff08;二&#xff09;机器学习的一般过程举例&#xff08;三&#xff09;样本和参数估计 二、机器学习的步骤总结&#xff08;一&#xff09;机器学习的主要步骤&#xff08;二&#xff09;样本及样…

zookeeper安装原生开发 C API接口时报错

报出的错误&#xff1a;error: %d directive writing between 1 and 5 bytes into a region of size be 问题原因 %d 格式说明符用于格式化有符号十进制整数。它需要一个与要格式化的整数大小相匹配的缓冲区。如果缓冲区太小&#xff0c;则会导致缓冲区溢出&#xff0c;从而可…

异地组网、内部网络需求同时满足,贝锐企业路由器G300开箱体验

由于公司最近新增了办事处&#xff0c;作为IT管理员的我不仅需要搞定办事处的网络&#xff0c;还需要解决远程访问公司内部办公系统以及文件存储服务器的问题。 在此之前&#xff0c;只有少量人员出差时&#xff0c;我们采用了虚拟专网方案来进行远程访问。然而&#xff0c;新…

ROS2中node之最简单的HelloWorld(C++)案例

ROS2中node之最简单的HelloWorld&#xff08;C&#xff09;案例 1、创建工作空间2、编译工作空间3、创建功能包4、编写helloworld 代码5、编辑CMakeLists.txt6、编译工作空间所有功能包7、运行结果 1、创建工作空间 $ mkdir -p ~/devnode_ws/src $ cd ~/devnode_ws/2、编译工作…

探索SmartEDA:电路仿真的教学新境界

在电子工程的教学领域&#xff0c;随着技术的飞速发展&#xff0c;传统的教学方法已经难以满足现代学生的学习需求。近年来&#xff0c;电路仿真软件在教学中的应用逐渐受到关注&#xff0c;而SmartEDA作为一款功能强大的电路仿真软件&#xff0c;为电子工程教学带来了革命性的…

韩国机器人公司Rainbow Robotics推出RB-Y1轮式双臂机器人

文 | BFT机器人 近日&#xff0c;韩国机器人领域的佼佼者Rainbow Robotics揭开了RB-Y1移动机器人的神秘面纱&#xff0c;这款机器人以其创新的设计和卓越的功能引起了业界的广泛关注。与此同时&#xff0c;Rainbow Robotics还携手舍弗勒集团&#xff08;提供汽车、工业技术服务…

Java | Leetcode Java题解之第41题缺失的第一个正数

题目&#xff1a; 题解&#xff1a; class Solution {public int firstMissingPositive(int[] nums) {int n nums.length;for (int i 0; i < n; i) {while (nums[i] > 0 && nums[i] < n && nums[nums[i] - 1] ! nums[i]) {int temp nums[nums[i] …

【前端技术】HTML基础入门篇

1.1 HTML简介 ​ HTML&#xff08;HyperText Markup Language&#xff1a;超文本标记语言&#xff09;是一种标识性的语言。它包括一系列标签&#xff0e;通过这些标签可以将网络上的文档格式统一&#xff0c;使分散的Internet资源连接为一个逻辑整体。HTML文本是由HTML命令组…

opencv绘制线段------c++

绘制线段 bool opencvTool::drawLines(std::string image_p, std::vector<cv::Point> points) {cv::Mat ima cv::imread(image_p.c_str()); // 读取图像&#xff0c;替换为你的图片路径 cv::Scalar red cv::Scalar(0, 0, 255); // Red color int thickness 2;// 遍…

用html画一个四叶草

<!DOCTYPE html> <html lang"en" > <head> <meta charset"UTF-8"> <title>四叶草</title> <link href"" rel"stylesheet"> <link rel"stylesheet" href"css/style.css&q…

上位机图像处理和嵌入式模块部署(树莓派4b与视觉slam十四讲)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 实际使用中&#xff0c;树莓派4b是非常好的一个基础平台。本身板子价格也不是很贵&#xff0c;建议大家多多使用。之前关于vslam&#xff0c;也就是…

树 —— 树和森林的遍历

一、树的遍历 &#xff08;1&#xff09;先根遍历 若树非空&#xff0c;则遍历方法为 &#xff08;1&#xff09;访问根结点。 &#xff08;2&#xff09;从左到右&#xff0c;依次先根遍历根结点的每一棵子树。 先根遍历序列为&#xff1a;ABECFHGD。 &#xff08;2&#…

IP定位技术助力网络安全保护

在当今数字化时代&#xff0c;网络安全成为各个组织和个人关注的焦点之一。随着网络攻击日益复杂和频繁&#xff0c;有效的网络安全保护措施变得至关重要。IP定位技术作为网络安全的重要组成部分&#xff0c;为识别、定位和防御网络攻击提供了关键支持。本文将探讨IP定位技术如…

Spring Cloud学习笔记(Ribbon):Ribbon的应用样例

这是本人学习的总结&#xff0c;主要学习资料如下 - 马士兵教育 1、Ribbon简介1.1、架构图1.2、简单实现负载均衡 2、配置负载均衡策略2.1、IRule2.2、使用IRule简单示例2.2.1、Overview2.2.1、注入IRule2.2.2、关联IRule和服务 1、Ribbon简介 我们都知道Ribbon是用于负载均衡…

【数据结构】算法效率揭秘:时间与空间复杂度的较量

前言 在计算机科学中&#xff0c;时间复杂度和空间复杂度是衡量算法性能的两个重要指标。它们分别表示算法在执行过程中所需的时间和空间资源。了解这两个概念有助于我们评估和比较不同算法的优劣&#xff0c;从而选择更合适的算法解决问题~ 欢迎关注个人主页&#xff1a;逸狼 …

电子邮件免费版有哪些?免费注册电子邮箱

电子邮件有付费版和免费版两种类型&#xff0c;付费版通常具有更大的电子邮箱容量和更强大的电子邮箱功能。但是对于我们个人用户或者是中小型企业来说注册电子邮箱免费版的就够日常使用了。电子邮件的免费版提供商有Zoho Mail、微软、腾讯等&#xff0c;今天我们就来具体了解下…

【Linux】使用Jenkins + svn + springboot自动构建jar包并自动打包在服务器上运行

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

报告!这里发现了一个赛博炼丹的神级平台!

众所周知&#xff0c;“赛博炼丹”是一个AI开发研究领域古老又神秘的活动&#xff0c;它往往对炼丹平台有很高的要求。如果你也是一路从“炼丹小白”成长到“资深AI算法工程师”&#xff0c;那你一定懂我在说什么&#xff1f;说好了&#xff0c;天台见&#xff01; GpuMall智算…

redis单线程模型

工作原理 在Redis中&#xff0c;当两个客户端同时发送相同的请求时&#xff0c;Redis采用单线程模型来处理所有的客户端请求&#xff0c;会依次处理这些请求&#xff0c;每个请求都会按照先后顺序被执行&#xff0c;不会同时处理多个请求。使得Redis能够避免多线程并发访问数据…

探索比特币符文热:市场趋势与持续性分析

在加密货币世界中&#xff0c;比特币一直是备受关注的焦点之一。然而&#xff0c;近年来&#xff0c;随着DeFi&#xff08;去中心化金融&#xff09;的兴起&#xff0c;一种新的潮流开始崭露头角——比特币符文。本文将探讨比特币符文的兴起&#xff0c;分析市场趋势&#xff0…