22、基于Arduino的SD卡数据记录与网络连接

基于Arduino的SD卡数据记录与网络连接

1. 使用RTC第三方库

为了扩展Arduino的功能,我们使用第三方库RTClib来方便与实时时钟(RTC)芯片进行通信。该库最初由JeeLabs开发,后由adafruit Industries更新。可以从网页(www.exploringarduino.com/content/ch13)下载该库,并将其添加到Arduino用户库文件夹中,注意文件夹名称不能包含连字符,下划线是可以的。

首次运行示例代码时,使用 RTC.adjust 函数从计算机获取当前日期和时间来设置时钟。之后,RTC将自主运行,可通过执行 RTC.now() 命令获取当前时间和日期。

2. 结合SD卡和实时时钟进行数据记录
2.1 安装RTC和SD卡模块

确保SD卡屏蔽罩和RTC都连接到Arduino。如果使用Cooking Hacks SD屏蔽罩和adafruit RTC屏蔽罩,连接后外观应类似相关图示。需要注意的是,RTC的最后一个引脚悬空,它是RTC产生的方波,我们不会使用。在代码中,需要将A2引脚接地,A3引脚接5V以确保RTC正常供电。

2.2 更新软件

要将RTC功能集成到软件中,需要进行以下操作:
- 包含RTC库
- 为RTC模块供电
- 初始化RTC对象
- 如果RTC时间未设置,则使用计算机时间设置
- 将实际时间戳写入日志文件

同时,在代码修订中,每次代码启动时都会打印列标题,方便在追加到现有CSV文件时找到日志重启的时间。

以下是更新后的程序示例(sd_read_write_rtc.ino):

//SD read and write with RTC
#include <SD.h>   //For talking to SD Card
#include <Wire.h>  //For RTC
#include "RTClib.h" //For RTC
//Define pins
//SD card is on standard SPI pins
//RTC is on Standard I2C Pins
const int CS_PIN      =10;
const int SD_POW_PIN  =8;
const int RTC_POW_PIN =A3;
const int RTC_GND_PIN =A2;
//Default rate of 5 seconds
int refresh_rate = 5000;
//Define an RTC object
RTC_DS1307 RTC;
//Initialize strings
String year, month, day, hour, minute, second, time, date;
void setup()
{
  Serial.begin(9600);
  Serial.println(F("Initializing Card"));

  //CS pin and pwr/gnd pins are outputs
  pinMode(CS_PIN,   OUTPUT);
  pinMode(SD_POW_PIN, OUTPUT);
  pinMode(RTC_POW_PIN, OUTPUT);
  pinMode(RTC_GND_PIN, OUTPUT);

  //Setup power and ground pins for both modules 
  digitalWrite(SD_POW_PIN, HIGH);
  digitalWrite(RTC_POW_PIN, HIGH);
  digitalWrite(RTC_GND_PIN, LOW);

  //Initiate the I2C bus and the RTC library
  Wire.begin();
  RTC.begin();

  //If RTC is not running, set it to the computer's compile time
  if (! RTC.isrunning())
  {
    Serial.println(F("RTC is NOT running!"));
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }

  //Initialize SD card
  if (!SD.begin(CS_PIN))
  {
    Serial.println(F("Card Failure"));
    return;
  }
  Serial.println(F("Card Ready"));

  //Read the configuration information (speed.txt)
  File commandFile = SD.open("speed.txt");
  if (commandFile)
  {
    Serial.println(F("Reading Command File"));

    while(commandFile.available())
    {
      refresh_rate = commandFile.parseInt();
    }
    Serial.print(F("Refresh Rate = "));
    Serial.print(refresh_rate);
    Serial.println(F("ms"));
    commandFile.close();
  }
  else
  {
    Serial.println(F("Could not read command file."));
    return;
  }

  //Write column headers
  File dataFile = SD.open("log.csv", FILE_WRITE);
  if (dataFile)
  {
    dataFile.println(F("\nNew Log Started!"));
    dataFile.println(F("Date,Time,Phrase"));
    dataFile.close(); //Data isn't actually written until we 
                      //close the connection!

    //Print same thing to the screen for debugging
    Serial.println(F("\nNew Log Started!"));
    Serial.println(F("Date,Time,Phrase"));
  }
  else
  {
    Serial.println(F("Couldn't open log file"));
  }

}
void loop()
{
  //Get the current date and time info and store in strings
  DateTime datetime = RTC.now();
  year  = String(datetime.year(),  DEC);
  month = String(datetime.month(), DEC);
  day  = String(datetime.day(),  DEC);
  hour  = String(datetime.hour(),  DEC);
  minute = String(datetime.minute(), DEC);
  second = String(datetime.second(), DEC);

  //Concatenate the strings into date and time
  date = year + "/" + month + "/" + day;
  time = hour + ":" + minute + ":" + second;

  String dataString = "Hello There!";

  //Open a file and write to it.
  File dataFile = SD.open("log.csv", FILE_WRITE);
  if (dataFile)
  {
    dataFile.print(date);
    dataFile.print(F(","));
    dataFile.print(time);
    dataFile.print(F(","));
    dataFile.println(dataString);
    dataFile.close(); //Data isn't actually written until we 
                      //close the connection!

    //Print same thing to the screen for debugging
    Serial.print(date);
    Serial.print(F(","));
    Serial.print(time);
    Serial.print(F(","));
    Serial.println(dataString);
  }
  else
  {
    Serial.println(F("Couldn't open log file"));
  }
  delay(refresh_rate);
}
3. 构建入口记录器
3.1 记录器硬件

只需在现有设置中添加一个模拟距离传感器。如果设置与示例相同,甚至不需要面包板,只需将适当的电线连接到电源、地和A0(用于传感器的模拟信号输出)。为了使系统正常工作,建议将红外距离传感器和Arduino安装在墙上,使红外光束水平穿过门。

3.2 记录器软件

对于入口记录器,从SD卡读取配置变量不是特别有用,可以移除相关代码。需要添加代码来检查距离传感器的状态,判断其读数在两次轮询之间是否有显著变化。如果有变化,则认为有物体在传感器前移动,即有人进入或离开房间。

还需要选择一个“变化阈值”,对于示例设置,两次轮询之间模拟读数变化超过75是判断移动的良好指标。建议在检测到移动时立即写入SD卡,没有移动时定期写入,以平衡存储空间和准确性。

以下是入口记录器的完整软件示例(entrance_logger.ino):

//Logs Room Entrance Activity
#include <SD.h>   //For talking to SD Card
#include <Wire.h>  //For RTC
#include "RTClib.h" //For RTC
//Define pins
//SD Card is on Standard SPI Pins
//RTC is on Standard I2C Pins
const int CS_PIN      =10; //SS for SD Shield
const int SD_POW_PIN  =8;  //Power for SD Shield
const int RTC_POW_PIN =A3; //Used as digital output
const int RTC_GND_PIN =A2; //Used as digital output
const int IR_PIN      =0; //Analog input 0
//Define an RTC object
RTC_DS1307 RTC;
//Initialize strings
String year, month, day, hour, minute, second, time, date;
//Initialize distance variables
int raw = 0;
int raw_prev = 0;
boolean active = false;
int update_time = 0;
void setup()
{
  Serial.begin(9600);
  Serial.println(F("Initializing Card"));

  //CS pin, and pwr/gnd pins are outputs
  pinMode(CS_PIN,   OUTPUT);
  pinMode(SD_POW_PIN, OUTPUT);
  pinMode(RTC_POW_PIN, OUTPUT);
  pinMode(RTC_GND_PIN, OUTPUT);

  //Setup power and ground pins for both modules 
  digitalWrite(SD_POW_PIN, HIGH);
  digitalWrite(RTC_POW_PIN, HIGH);
  digitalWrite(RTC_GND_PIN, LOW);

  //Initiate the I2C bus and the RTC library
  Wire.begin();
  RTC.begin();

  //If RTC is not running, set it to the computer's compile time
  if (! RTC.isrunning())
  {
    Serial.println(F("RTC is NOT running!"));
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }

  //Initialize SD card
  if (!SD.begin(CS_PIN))
  {
    Serial.println(F("Card Failure"));
    return;
  }
  Serial.println(F("Card Ready"));

  //Write column headers
  File dataFile = SD.open("log.csv", FILE_WRITE);
  if (dataFile)
  {
    dataFile.println(F("\nNew Log Started!"));
    dataFile.println(F("Date,Time,Raw,Active"));
    dataFile.close(); //Data isn't actually written until we 
                      //close the connection!

    //Print same thing to the screen for debugging
    Serial.println(F("\nNew Log Started!"));
    Serial.println(F("Date,Time,Raw,Active"));
  }
  else
  {
    Serial.println(F("Couldn't open log file"));
  }

}
void loop()
{
  //Get the current date and time info and store in strings
  DateTime datetime = RTC.now();
  year  = String(datetime.year(),  DEC);
  month = String(datetime.month(), DEC);
  day  = String(datetime.day(),  DEC);
  hour  = String(datetime.hour(),  DEC);
  minute = String(datetime.minute(), DEC);
  second = String(datetime.second(), DEC);

  //Concatenate the strings into date and time
  date = year + "/" + month + "/" + day;
  time = hour + ":" + minute + ":" + second;

  //Gather motion data
  raw = analogRead(IR_PIN);
  //If the value changes by more than 75 between readings, 
  //indicate movement.
  if (abs(raw-raw_prev) > 75)
    active = true;
  else
    active = false;
  raw_prev = raw;

  //Open a file and write to it.
  if (active || update_time == 20)
  {
    File dataFile = SD.open("log.csv", FILE_WRITE);
    if (dataFile)
    {
      dataFile.print(date);
      dataFile.print(F(","));
      dataFile.print(time);
      dataFile.print(F(","));
      dataFile.print(raw);
      dataFile.print(F(","));
      dataFile.println(active);
      dataFile.close(); //Data isn't actually written until we 
                        //close the connection!

      //Print same thing to the screen for debugging
      Serial.print(date);
      Serial.print(F(","));
      Serial.print(time);
      Serial.print(F(","));
      Serial.print(raw);
      Serial.print(F(","));
      Serial.println(active);
    }
    else
    {
      Serial.println(F("Couldn't open log file"));
    }
    update_time = 0;
  }
  delay(50);
  update_time++;
}
4. 数据分析

将代码加载到Arduino上并运行一段时间后,将SD卡插入计算机,使用喜欢的电子表格程序打开CSV文件。假设只记录了一天的数据,可以绘制时间列与活动列的图表。当没有活动时,活动折线图保持为零;有人进入或离开房间时,图表会跳到1,可清晰看到事件发生的时间。

为方便绘制图表,有一个预格式化的在线电子表格可供使用。需要有Google账户,访问网页(www.exploringarduino.com/content/ch13)并跟随链接到图表生成电子表格,按照提示在Google Drive账户中创建新的电子表格,然后将数据复制到模板数据的位置,图表将自动更新。

5. 连接Arduino到互联网
5.1 网络术语

在将Arduino联网之前,需要了解一些网络术语:
- IP地址 :互联网协议(IP)地址是识别每个连接到互联网的设备的唯一地址。对于家庭网络,需要关注本地IP地址和全局IP地址。

5.2 Arduino与互联网的关系

可以将Arduino与互联网的关系简单理解为:首先在本地网络范围内工作,只有当Arduino和Web浏览器都在同一本地网络时,才能通过Web浏览器与Arduino通信。之后,可以探索如何穿越路由器,在世界任何地方(至少是有互联网连接的地方)访问Arduino的功能。

总结

通过以上内容,我们学习了以下知识:
- CSV文件使用换行符和逗号作为分隔符,以纯文本格式轻松存储数据。
- 可以在Windows、Mac或Linux系统中格式化SD卡。
- 有多种SD卡屏蔽罩可供选择,各有独特功能。
- 可以使用SD库对SD卡上的文件进行读写操作。
- 可以构建RTC并编写软件利用它插入时间戳。
- 可以通过将字符串存储在闪存中来克服RAM限制。
- 可以通过观察距离传感器产生的模拟值变化来检测移动。
- 可以使用计算机上的电子表格对数据记录器的数据进行绘图分析。

所需部件

  • Arduino(推荐Uno)
  • USB电缆
  • Arduino以太网屏蔽罩
  • 光敏电阻
  • 10kΩ电阻
  • TMP36温度传感器
  • RGB LED
  • 220Ω电阻(3个)
  • 150Ω电阻
  • 扬声器或蜂鸣器
  • 以太网电缆
  • 有线路由器访问权限
  • 跳线
  • 面包板

下面是一个简单的mermaid流程图,展示入口记录器的工作流程:

graph TD;
    A[开始] --> B[初始化SD卡和RTC];
    B --> C[读取距离传感器数据];
    C --> D{读数变化是否超阈值};
    D -- 是 --> E[记录移动事件到SD卡];
    D -- 否 --> F{是否达到定期写入时间};
    F -- 是 --> G[写入无移动事件到SD卡];
    F -- 否 --> C;
    E --> C;
    G --> C;

通过以上步骤和代码,我们可以实现基于Arduino的SD卡数据记录和网络连接功能,进一步开发各种有趣的项目。

基于Arduino的SD卡数据记录与网络连接

6. 网络连接的深入探讨
6.1 本地网络通信

在本地网络中,要实现通过Web浏览器与Arduino通信,需确保两者处于同一网络。可以通过以下步骤进行操作:
1. 硬件连接 :将Arduino以太网屏蔽罩安装在Arduino上,并使用以太网电缆将其连接到有线路由器。
2. 软件配置 :在Arduino代码中设置正确的IP地址和端口号,以便Web浏览器能够访问。例如,可以使用以下代码片段来设置IP地址:

IPAddress ip(192, 168, 1, 177); // 本地IP地址
Ethernet.begin(mac, ip); // 初始化以太网连接
  1. Web服务器搭建 :使用Arduino的以太网库搭建一个简单的Web服务器,处理来自Web浏览器的请求。以下是一个简单的Web服务器示例:
#include <Ethernet.h>
#include <SPI.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 177);
EthernetServer server(80);

void setup() {
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.begin(9600);
}

void loop() {
  EthernetClient client = server.available();
  if (client) {
    Serial.println("New client connected");
    while (client.connected()) {
      if (client.available()) {
        String line = client.readStringUntil('\n');
        Serial.println(line);
        if (line.length() == 1 && line[0] == '\r') {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-type:text/html");
          client.println();
          client.println("<html><body>Hello from Arduino!</body></html>");
          break;
        }
      }
    }
    client.stop();
    Serial.println("Client disconnected");
  }
}
6.2 穿越路由器访问

要在世界任何地方访问Arduino的功能,需要穿越路由器。这通常涉及到端口转发和动态域名解析(DDNS)等技术。具体操作步骤如下:
1. 端口转发设置 :登录路由器的管理界面,找到端口转发设置选项。将Arduino使用的端口(如80端口)转发到Arduino的本地IP地址。
2. DDNS配置 :如果你的网络服务提供商分配的是动态IP地址,需要使用DDNS服务。注册一个DDNS服务提供商的账号,并在路由器中配置DDNS信息。
3. 测试访问 :在完成上述设置后,使用外部网络的Web浏览器访问DDNS提供的域名,检查是否能够访问Arduino的Web服务器。

7. 数据记录与网络连接的优化
7.1 数据记录优化

为了提高数据记录的效率和可靠性,可以采取以下优化措施:
- 批量写入 :将多次采集的数据批量写入SD卡,减少写入次数,降低SD卡的磨损。例如,可以使用一个数组来存储数据,当数组满时再一次性写入SD卡。
- 错误处理 :在写入数据时,添加错误处理机制,确保数据的完整性。例如,在写入数据前检查SD卡是否正常,写入失败时进行重试。

7.2 网络连接优化

为了提高网络连接的稳定性和速度,可以采取以下优化措施:
- 选择合适的网络 :尽量选择稳定的有线网络连接,避免使用不稳定的无线网络。
- 优化代码 :减少网络通信的数据量,避免频繁的网络请求。例如,可以使用JSON格式来压缩数据,减少数据传输量。

8. 实际应用案例
8.1 环境监测系统

结合温度传感器、湿度传感器和光照传感器等,构建一个环境监测系统。Arduino实时采集环境数据,并将数据记录到SD卡中,同时通过网络将数据发送到远程服务器。用户可以通过Web浏览器实时查看环境数据。

以下是一个简单的环境监测系统代码示例:

#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <SPI.h>
#include <Ethernet.h>

// 传感器引脚定义
const int TEMP_PIN = A0;
const int HUMIDITY_PIN = A1;
const int LIGHT_PIN = A2;

// SD卡和RTC引脚定义
const int CS_PIN = 10;
const int RTC_POW_PIN = A3;
const int RTC_GND_PIN = A2;

// 以太网相关定义
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 177);
EthernetServer server(80);

RTC_DS1307 RTC;

void setup() {
  Serial.begin(9600);

  // 初始化SD卡和RTC
  pinMode(CS_PIN, OUTPUT);
  pinMode(RTC_POW_PIN, OUTPUT);
  pinMode(RTC_GND_PIN, OUTPUT);
  digitalWrite(RTC_POW_PIN, HIGH);
  digitalWrite(RTC_GND_PIN, LOW);

  Wire.begin();
  RTC.begin();
  if (!RTC.isrunning()) {
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }

  if (!SD.begin(CS_PIN)) {
    Serial.println("SD card initialization failed!");
    return;
  }

  // 初始化以太网
  Ethernet.begin(mac, ip);
  server.begin();

  // 写入列标题
  File dataFile = SD.open("log.csv", FILE_WRITE);
  if (dataFile) {
    dataFile.println("Date,Time,Temperature,Humidity,Light");
    dataFile.close();
  }
}

void loop() {
  DateTime datetime = RTC.now();
  String date = String(datetime.year()) + "/" + String(datetime.month()) + "/" + String(datetime.day());
  String time = String(datetime.hour()) + ":" + String(datetime.minute()) + ":" + String(datetime.second());

  // 读取传感器数据
  int tempValue = analogRead(TEMP_PIN);
  int humidityValue = analogRead(HUMIDITY_PIN);
  int lightValue = analogRead(LIGHT_PIN);

  // 写入数据到SD卡
  File dataFile = SD.open("log.csv", FILE_WRITE);
  if (dataFile) {
    dataFile.print(date);
    dataFile.print(",");
    dataFile.print(time);
    dataFile.print(",");
    dataFile.print(tempValue);
    dataFile.print(",");
    dataFile.print(humidityValue);
    dataFile.print(",");
    dataFile.println(lightValue);
    dataFile.close();
  }

  // 处理网络请求
  EthernetClient client = server.available();
  if (client) {
    Serial.println("New client connected");
    while (client.connected()) {
      if (client.available()) {
        String line = client.readStringUntil('\n');
        Serial.println(line);
        if (line.length() == 1 && line[0] == '\r') {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-type:text/html");
          client.println();
          client.println("<html><body>");
          client.print("Date: ");
          client.print(date);
          client.print("<br>Time: ");
          client.print(time);
          client.print("<br>Temperature: ");
          client.print(tempValue);
          client.print("<br>Humidity: ");
          client.print(humidityValue);
          client.print("<br>Light: ");
          client.print(lightValue);
          client.println("</body></html>");
          break;
        }
      }
    }
    client.stop();
    Serial.println("Client disconnected");
  }

  delay(5000); // 每5秒采集一次数据
}
8.2 智能家居系统

利用Arduino的网络连接功能,构建一个智能家居系统。通过传感器监测房间的温度、湿度、光照等环境参数,根据环境参数自动控制家电设备的开关。同时,用户可以通过手机APP远程控制家电设备。

总结

通过以上内容,我们深入探讨了基于Arduino的数据记录和网络连接技术。从SD卡数据记录到实时时钟的应用,再到网络连接的实现和优化,我们学习了一系列的知识和技能。同时,通过实际应用案例,我们了解了如何将这些技术应用到实际项目中。在实际开发过程中,我们需要根据具体需求选择合适的硬件和软件方案,并进行相应的优化,以确保系统的稳定性和可靠性。

操作步骤总结

以下是一个表格,总结了本文涉及的主要操作步骤:
| 操作内容 | 操作步骤 |
| — | — |
| 数据记录 | 1. 安装SD卡和RTC模块
2. 编写数据记录代码
3. 运行代码,将数据记录到SD卡 |
| 网络连接 | 1. 安装以太网屏蔽罩,连接到路由器
2. 编写网络通信代码
3. 设置端口转发和DDNS
4. 测试网络访问 |
| 数据记录与网络连接优化 | 1. 批量写入数据,添加错误处理
2. 选择合适的网络,优化代码 |

下面是一个mermaid流程图,展示环境监测系统的工作流程:

graph TD;
    A[开始] --> B[初始化SD卡、RTC和以太网];
    B --> C[读取传感器数据];
    C --> D[记录数据到SD卡];
    D --> E{是否有网络请求};
    E -- 是 --> F[响应网络请求,返回数据];
    E -- 否 --> C;
    F --> C;

通过以上的学习和实践,我们可以利用Arduino开发出各种有趣的项目,实现数据记录和网络连接的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值