基于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); // 初始化以太网连接
- 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开发出各种有趣的项目,实现数据记录和网络连接的功能。