First Commit

This commit is contained in:
MindCreeper03
2025-02-27 19:31:50 +01:00
parent bcbb6aff9a
commit e490df1715
2470 changed files with 1479965 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
// A simple data logger for the Arduino analog pins with optional DS1307
// uses RTClib from https://github.com/adafruit/RTClib
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
#include "FreeStack.h"
#define SD_CHIP_SELECT SS // SD chip select pin
#define USE_DS1307 0 // set nonzero to use DS1307 RTC
#define LOG_INTERVAL 1000 // mills between entries
#define SENSOR_COUNT 3 // number of analog pins to log
#define ECHO_TO_SERIAL 1 // echo data to serial port if nonzero
#define WAIT_TO_START 1 // Wait for serial input in setup()
#define ADC_DELAY 10 // ADC delay for high impedence sensors
// file system object
SdFat sd;
// text file for logging
ofstream logfile;
// Serial print stream
ArduinoOutStream cout(Serial);
// buffer to format data - makes it eaiser to echo to Serial
char buf[80];
//------------------------------------------------------------------------------
#if SENSOR_COUNT > 6
#error SENSOR_COUNT too large
#endif // SENSOR_COUNT
//------------------------------------------------------------------------------
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
#if USE_DS1307
// use RTClib from Adafruit
// https://github.com/adafruit/RTClib
// The Arduino IDE has a bug that causes Wire and RTClib to be loaded even
// if USE_DS1307 is false.
#error remove this line and uncomment the next two lines.
//#include <Wire.h>
//#include <RTClib.h>
RTC_DS1307 RTC; // define the Real Time Clock object
//------------------------------------------------------------------------------
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
DateTime now = RTC.now();
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(now.year(), now.month(), now.day());
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(now.hour(), now.minute(), now.second());
}
//------------------------------------------------------------------------------
// format date/time
ostream& operator << (ostream& os, DateTime& dt) {
os << dt.year() << '/' << int(dt.month()) << '/' << int(dt.day()) << ',';
os << int(dt.hour()) << ':' << setfill('0') << setw(2) << int(dt.minute());
os << ':' << setw(2) << int(dt.second()) << setfill(' ');
return os;
}
#endif // USE_DS1307
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial.
while (!Serial) {
yield();
}
// F() stores strings in flash to save RAM
cout << endl << F("FreeStack: ") << FreeStack() << endl;
#if WAIT_TO_START
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Discard input.
do {
delay(10);
} while(Serial.available() && Serial.read() >= 0);
#endif // WAIT_TO_START
#if USE_DS1307
// connect to RTC
Wire.begin();
if (!RTC.begin()) {
error("RTC failed");
}
// set date time callback function
SdFile::dateTimeCallback(dateTime);
DateTime now = RTC.now();
cout << now << endl;
#endif // USE_DS1307
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// create a new file in root, the current working directory
char name[] = "logger00.csv";
for (uint8_t i = 0; i < 100; i++) {
name[6] = i/10 + '0';
name[7] = i%10 + '0';
if (sd.exists(name)) {
continue;
}
logfile.open(name);
break;
}
if (!logfile.is_open()) {
error("file.open");
}
cout << F("Logging to: ") << name << endl;
cout << F("Type any character to stop\n\n");
// format header in buffer
obufstream bout(buf, sizeof(buf));
bout << F("millis");
#if USE_DS1307
bout << F(",date,time");
#endif // USE_DS1307
for (uint8_t i = 0; i < SENSOR_COUNT; i++) {
bout << F(",sens") << int(i);
}
logfile << buf << endl;
#if ECHO_TO_SERIAL
cout << buf << endl;
#endif // ECHO_TO_SERIAL
}
//------------------------------------------------------------------------------
void loop() {
uint32_t m;
// wait for time to be a multiple of interval
do {
m = millis();
} while (m % LOG_INTERVAL);
// use buffer stream to format line
obufstream bout(buf, sizeof(buf));
// start with time in millis
bout << m;
#if USE_DS1307
DateTime now = RTC.now();
bout << ',' << now;
#endif // USE_DS1307
// read analog pins and format data
for (uint8_t ia = 0; ia < SENSOR_COUNT; ia++) {
#if ADC_DELAY
analogRead(ia);
delay(ADC_DELAY);
#endif // ADC_DELAY
bout << ',' << analogRead(ia);
}
bout << endl;
// log data and flush to SD
logfile << buf << flush;
// check for error
if (!logfile) {
error("write data failed");
}
#if ECHO_TO_SERIAL
cout << buf;
#endif // ECHO_TO_SERIAL
// don't log two points in the same millis
if (m == millis()) {
delay(1);
}
if (!Serial.available()) {
return;
}
logfile.close();
cout << F("Done!");
while (true) {}
}

View File

@@ -0,0 +1,46 @@
/*
* Program to test Short File Name character case flags.
*/
#include <SPI.h>
#include "SdFat.h"
const uint8_t chipSelect = SS;
SdFat sd;
SdFile file;
const char* name[] = {
"low.low", "low.Mix", "low.UP",
"Mix.low", "Mix.Mix", "Mix.UP",
"UP.low", "UP.Mix", "UP.UP"
};
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
Serial.println("type any character to start");
while (!Serial.available()) {
yield();
}
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
Serial.println("begin failed");
return;
}
for (uint8_t i = 0; i < 9; i++) {
sd.remove(name[i]);
if (!file.open(name[i], O_RDWR | O_CREAT | O_EXCL)) {
sd.errorHalt(name[i]);
}
file.println(name[i]);
file.close();
}
sd.ls(LS_DATE|LS_SIZE);
Serial.println("Done");
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,20 @@
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// create a serial output stream
ArduinoOutStream cout(Serial);
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
delay(2000);
cout << "Hello, World!\n";
}
void loop() {}

View File

@@ -0,0 +1,29 @@
// This example illustrates use of SdFat's
// minimal unbuffered AVR Serial support.
//
// This is useful for debug and saves RAM
// Will not work on Due, Leonardo, or Teensy
#include <SPI.h>
#include "SdFat.h"
#include "FreeStack.h"
#ifdef UDR0 // Must be AVR with serial port zero.
#include "MinimumSerial.h"
MinimumSerial MiniSerial;
void setup() {
MiniSerial.begin(9600);
MiniSerial.println(FreeStack());
}
void loop() {
int c;
MiniSerial.println(F("Type any Character"));
while ((c = MiniSerial.read()) < 0) {}
MiniSerial.print(F("Read: "));
MiniSerial.println((char)c);
while (MiniSerial.read() >= 0) {}
}
#else // UDR0
#error no AVR serial port 0
#endif // UDR0

View File

@@ -0,0 +1,125 @@
/*
* This program is a simple Print benchmark.
*/
#include <SPI.h>
#include <SD.h>
// SD chip select pin
const uint8_t chipSelect = SS;
// number of lines to print
const uint16_t N_PRINT = 20000;
// test file
File file;
//------------------------------------------------------------------------------
void error(const char* s) {
Serial.println(s);
while (1) {
yield();
}
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
}
//------------------------------------------------------------------------------
void loop() {
uint32_t maxLatency;
uint32_t minLatency;
uint32_t totalLatency;
// Read any existing Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
// F() stores strings in flash to save RAM
Serial.println(F("Type any character to start"));
while (!Serial.available()) {
yield();
}
// initialize the SD card
if (!SD.begin(chipSelect)) {
error("begin");
}
Serial.println(F("Starting print test. Please wait.\n"));
// do write test
for (int test = 0; test < 2; test++) {
file = SD.open("bench.txt", FILE_WRITE);
if (!file) {
error("open failed");
}
switch(test) {
case 0:
Serial.println(F("Test of println(uint16_t)"));
break;
case 1:
Serial.println(F("Test of println(double)"));
break;
}
maxLatency = 0;
minLatency = 999999;
totalLatency = 0;
uint32_t t = millis();
for (uint16_t i = 0; i < N_PRINT; i++) {
uint32_t m = micros();
switch(test) {
case 0:
file.println(i);
break;
case 1:
file.println((double)0.01*i);
break;
}
if (file.getWriteError()) {
error("write failed");
}
m = micros() - m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m;
}
file.flush();
t = millis() - t;
double s = file.size();
Serial.print(F("Time "));
Serial.print(0.001*t);
Serial.println(F(" sec"));
Serial.print(F("File size "));
Serial.print(0.001*s);
Serial.print(F(" KB\n"));
Serial.print(F("Write "));
Serial.print(s/t);
Serial.print(F(" KB/sec\n"));
Serial.print(F("Maximum latency: "));
Serial.print(maxLatency);
Serial.print(F(" usec, Minimum Latency: "));
Serial.print(minLatency);
Serial.print(F(" usec, Avg Latency: "));
Serial.print(totalLatency/N_PRINT);
Serial.println(F(" usec\n"));
SD.remove("bench.txt");
}
file.close();
Serial.println(F("Done!\n"));
}

View File

@@ -0,0 +1,30 @@
/*
* Program to compare size of Arduino SD library with SdFat.
* See SdFatSize.ino for SdFat program.
*/
#include <SPI.h>
#include <SD.h>
File file;
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
if (!SD.begin()) {
Serial.println("begin failed");
return;
}
file = SD.open("TEST_SD.TXT", FILE_WRITE);
file.println("Hello");
file.close();
Serial.println("Done");
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,33 @@
/*
* Program to compare size of SdFat with Arduino SD library.
* See SD_Size.ino for Arduino SD program.
*
*/
#include <SPI.h>
#include "SdFat.h"
SdFat sd;
SdFile file;
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
if (!sd.begin()) {
Serial.println("begin failed");
return;
}
file.open("SizeTest.txt", O_RDWR | O_CREAT | O_AT_END);
file.println("Hello");
file.close();
Serial.println("Done");
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,44 @@
// Simple demo of the Stream parsInt() member function.
#include <SPI.h>
// The next two lines replace #include <SD.h>.
#include "SdFat.h"
SdFat SD;
// SD card chip select pin - Modify the value of csPin for your SD module.
const uint8_t csPin = SS;
File file;
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial.
while(!Serial) {
yield();
}
Serial.println(F("Type any character to start"));
while (!Serial.available()) {
yield();
}
// Initialize the SD.
if (!SD.begin(csPin)) {
Serial.println(F("begin error"));
return;
}
// Create and open the file. Use flag to truncate an existing file.
file = SD.open("stream.txt", O_RDWR|O_CREAT|O_TRUNC);
if (!file) {
Serial.println(F("open error"));
return;
}
// Write a test number to the file.
file.println("12345");
// Rewind the file and read the number with parseInt().
file.seek(0);
int i = file.parseInt();
Serial.print(F("parseInt: "));
Serial.println(i);
file.close();
}
void loop() {}

View File

@@ -0,0 +1,77 @@
/*
* Append Example
*
* This program shows how to use open for append.
* The program will append 100 line each time it opens the file.
* The program will open and close the file 100 times.
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD chip select pin
const uint8_t chipSelect = SS;
// file system object
SdFat sd;
// create Serial stream
ArduinoOutStream cout(Serial);
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
void setup() {
// filename for this example
char name[] = "append.txt";
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
// F() stores strings in flash to save RAM
cout << endl << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
cout << F("Appending to: ") << name;
for (uint8_t i = 0; i < 100; i++) {
// open stream for append
ofstream sdout(name, ios::out | ios::app);
if (!sdout) {
error("open failed");
}
// append 100 lines to the file
for (uint8_t j = 0; j < 100; j++) {
// use int() so byte will print as decimal number
sdout << "line " << int(j) << " of pass " << int(i);
sdout << " millis = " << millis() << endl;
}
// close the stream
sdout.close();
if (!sdout) {
error("append data failed");
}
// output progress indicator
if (i % 25 == 0) {
cout << endl;
}
cout << '.';
}
cout << endl << "Done" << endl;
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,82 @@
/*
* Calculate the sum and average of a list of floating point numbers
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD chip select pin
const uint8_t chipSelect = SS;
// object for the SD file system
SdFat sd;
// define a serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
void writeTestFile() {
// open the output file
ofstream sdout("AvgTest.txt");
// write a series of float numbers
for (int16_t i = -1001; i < 2000; i += 13) {
sdout << 0.1 * i << endl;
}
if (!sdout) {
sd.errorHalt("sdout failed");
}
sdout.close();
}
//------------------------------------------------------------------------------
void calcAverage() {
uint16_t n = 0; // count of input numbers
double num; // current input number
double sum = 0; // sum of input numbers
// open the input file
ifstream sdin("AvgTest.txt");
// check for an open failure
if (!sdin) {
sd.errorHalt("sdin failed");
}
// read and sum the numbers
while (sdin >> num) {
n++;
sum += num;
}
// print the results
cout << "sum of " << n << " numbers = " << sum << endl;
cout << "average = " << sum/n << endl;
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
// F() stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// write the test file
writeTestFile();
// read the test file and calculate the average
calcAverage();
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,149 @@
/*
* This program is a simple binary write/read benchmark
* for the standard Arduino SD.h library.
*/
#include <SPI.h>
#include <SD.h>
// SD chip select pin
const uint8_t chipSelect = SS;
#define FILE_SIZE_MB 5
#define FILE_SIZE (1000000UL*FILE_SIZE_MB)
#define BUF_SIZE 100
uint8_t buf[BUF_SIZE];
// test file
File file;
//------------------------------------------------------------------------------
void error(const char* s) {
Serial.println(s);
while (1) {
yield();
}
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
}
//------------------------------------------------------------------------------
void loop() {
uint32_t maxLatency;
uint32_t minLatency;
uint32_t totalLatency;
// Discard any input.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
// F() stores strings in flash to save RAM
Serial.println(F("Type any character to start"));
while (!Serial.available()) {
yield();
}
if (!SD.begin(chipSelect)) {
error("begin");
}
// open or create file - truncate existing file.
file = SD.open("Bench.dat", O_RDWR | O_TRUNC | O_CREAT);
if (!file) {
error("open failed");
}
// fill buf with known data
for (size_t_t i = 0; i < (BUF_SIZE-2); i++) {
buf[i] = 'A' + (i % 26);
}
buf[BUF_SIZE-2] = '\r';
buf[BUF_SIZE-1] = '\n';
Serial.print(F("File size "));
Serial.print(FILE_SIZE_MB);
Serial.println(F("MB"));
Serial.print(F("Buffer size "));
Serial.print(BUF_SIZE);
Serial.println(F(" bytes"));
Serial.println(F("Starting write test. Please wait up to a minute"));
// do write test
uint32_t n = FILE_SIZE/sizeof(buf);
maxLatency = 0;
minLatency = 999999;
totalLatency = 0;
uint32_t t = millis();
for (uint32_t i = 0; i < n; i++) {
uint32_t m = micros();
if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
error("write failed");
}
m = micros() - m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m;
}
file.flush();
t = millis() - t;
double s = file.size();
Serial.print(F("Write "));
Serial.print(s/t);
Serial.print(F(" KB/sec\n"));
Serial.print(F("Maximum latency: "));
Serial.print(maxLatency);
Serial.print(F(" usec, Minimum Latency: "));
Serial.print(minLatency);
Serial.print(F(" usec, Avg Latency: "));
Serial.print(totalLatency/n);
Serial.print(F(" usec\n\n"));
Serial.println(F("Starting read test. Please wait up to a minute"));
// do read test
file.seek(0);
maxLatency = 0;
minLatency = 99999;
totalLatency = 0;
t = millis();
for (uint32_t i = 0; i < n; i++) {
buf[BUF_SIZE-1] = 0;
uint32_t m = micros();
if (file.read(buf, sizeof(buf)) != sizeof(buf)) {
error("read failed");
}
m = micros() - m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m;
if (buf[BUF_SIZE-1] != '\n') {
error("data check");
}
}
t = millis() - t;
Serial.print(F("Read "));
Serial.print(s/t);
Serial.print(F(" KB/sec\n"));
Serial.print(F("Maximum latency: "));
Serial.print(maxLatency);
Serial.print(F(" usec, Minimum Latency: "));
Serial.print(minLatency);
Serial.print(F(" usec, Avg Latency: "));
Serial.print(totalLatency/n);
Serial.print(F(" usec\n\n"));
Serial.print(F("Done\n\n"));
file.close();
}

View File

@@ -0,0 +1,39 @@
/*
* Use of ibufsteam to parse a line and obufstream to format a line
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// create a serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
void setup() {
char buf[20]; // buffer for formatted line
int i, j, k; // values from parsed line
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
delay(2000);
// initialize input string
ibufstream bin("123 456 789");
// parse the string "123 456 789"
bin >> i >> j >> k;
// initialize output buffer
obufstream bout(buf, sizeof(buf));
// format the output string
bout << k << ',' << j << ',' << i << endl;
// write the string to serial
cout << buf;
}
void loop() {}

View File

@@ -0,0 +1,39 @@
/*
* Demo of ArduinoInStream and ArduinoOutStream
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// create serial output stream
ArduinoOutStream cout(Serial);
// input line buffer
char cinBuf[40];
// create serial input stream
ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
}
//------------------------------------------------------------------------------
void loop() {
int32_t n = 0;
cout << "\nenter an integer\n";
cin.readline();
if (cin >> n) {
cout << "The number is: " << n;
} else {
// will fail if no digits or not in range [-2147483648, 2147483647]
cout << "Invalid input: " << cinBuf;
}
cout << endl;
}

View File

@@ -0,0 +1,62 @@
/*
* Append a line to a file - demo of pathnames and streams
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD chip select pin
const uint8_t chipSelect = SS;
// file system object
SdFat sd;
// define a serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
/*
* Append a line to logfile.txt
*/
void logEvent(const char *msg) {
// create dir if needed
sd.mkdir("logs/2014/Jan");
// create or open a file for append
ofstream sdlog("logs/2014/Jan/logfile.txt", ios::out | ios::app);
// append a line to the file
sdlog << msg << endl;
// check for errors
if (!sdlog) {
sd.errorHalt("append failed");
}
sdlog.close();
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
// F() stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
delay(400); // catch Due reset problem
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// append a line to the logfile
logEvent("Another line for the logfile");
cout << F("Done - check /logs/2014/Jan/logfile.txt on the SD") << endl;
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,111 @@
// Demo of rewriting a line read by fgets
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD card chip select pin
const uint8_t chipSelect = SS;
// file system
SdFat sd;
// print stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// store error strings in flash memory
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
void demoFgets() {
char line[25];
int c;
uint32_t pos;
// open test file
SdFile rdfile("fgets.txt", O_RDWR);
// check for open error
if (!rdfile.isOpen()) {
error("demoFgets");
}
// list file
cout << F("-----Before Rewrite\r\n");
while ((c = rdfile.read()) >= 0) {
Serial.write(c);
}
rdfile.rewind();
// read lines from the file to get position
while (1) {
pos = rdfile.curPosition();
if (rdfile.fgets(line, sizeof(line)) < 0) {
error("Line not found");
}
// find line that contains "Line C"
if (strstr(line, "Line C")) {
break;
}
}
// rewrite line with 'C'
if (!rdfile.seekSet(pos)) {
error("seekSet");
}
rdfile.println("Line R");
rdfile.rewind();
// list file
cout << F("\r\n-----After Rewrite\r\n");
while ((c = rdfile.read()) >= 0) {
Serial.write(c);
}
// close so rewrite is not lost
rdfile.close();
}
//------------------------------------------------------------------------------
void makeTestFile() {
// create or open test file
SdFile wrfile("fgets.txt", O_WRONLY | O_CREAT | O_TRUNC);
// check for open error
if (!wrfile.isOpen()) {
error("MakeTestFile");
}
// write test file
wrfile.print(F(
"Line A\r\n"
"Line B\r\n"
"Line C\r\n"
"Line D\r\n"
"Line E\r\n"
));
wrfile.close();
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
makeTestFile();
demoFgets();
cout << F("\nDone\n");
}
void loop() {}

View File

@@ -0,0 +1,51 @@
/*
* Read the logfile created by the eventlog.ino example.
* Demo of pathnames and working directories
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD chip select pin
const uint8_t chipSelect = SS;
// file system object
SdFat sd;
// define a serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
void setup() {
int c;
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// set current working directory
if (!sd.chdir("logs/2014/Jan/")) {
sd.errorHalt("chdir failed. Did you run eventlog.ino?");
}
// open file in current working directory
ifstream file("logfile.txt");
if (!file.is_open()) {
sd.errorHalt("open failed");
}
// copy the file to Serial
while ((c = file.get()) >= 0) {
cout << (char)c;
}
cout << "Done" << endl;
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,34 @@
Old and debug examples.
AnalogLogger - A simple data logger for one or more analog pins.
append - This sketch creates a large file by successive
open/write/close operations.
average - A demonstration of parsing floating point numbers.
BaseExtCaseTest - Long file name test.
benchSD - A read/write benchmark for the standard Arduino SD.h library.
bufstream - ibufsteam to parse a line and obufstream to format a line.
cin_cout - Demo of ArduinoInStream and ArduinoOutStream.
eventlog - Append a line to a file - demo of pathnames and streams.
fgetsRewrite - Demo of rewriting a line read by fgets.
HelloWorld - Create a serial output stream.
MiniSerial - SdFat minimal serial support for debug.
PrintBenchmarkSD - Bench mark SD.h print.
readlog - Read file. Demo of pathnames and current working directory.
SD_Size - Determine flash used by SD.h example.
SdFatSize - Determine flash used by SdFat.
StreamParseInt - Simple demo of the Stream parsInt() member function.

View File

@@ -0,0 +1,39 @@
#ifndef AnalogBinLogger_h
#define AnalogBinLogger_h
//------------------------------------------------------------------------------
// First block of file.
struct metadata_t {
unsigned long adcFrequency; // ADC clock frequency
unsigned long cpuFrequency; // CPU clock frequency
unsigned long sampleInterval; // Sample interval in CPU cycles.
unsigned long recordEightBits; // Size of ADC values, nonzero for 8-bits.
unsigned long pinCount; // Number of analog pins in a sample.
unsigned long pinNumber[123]; // List of pin numbers in a sample.
};
//------------------------------------------------------------------------------
// Data block for 8-bit ADC mode.
const size_t DATA_DIM8 = 508;
struct block8_t {
unsigned short count; // count of data values
unsigned short overrun; // count of overruns since last block
unsigned char data[DATA_DIM8];
};
//------------------------------------------------------------------------------
// Data block for 10-bit ADC mode.
const size_t DATA_DIM16 = 254;
struct block16_t {
unsigned short count; // count of data values
unsigned short overrun; // count of overruns since last block
unsigned short data[DATA_DIM16];
};
//------------------------------------------------------------------------------
// Data block for PC use
struct adcdata_t {
unsigned short count; // count of data values
unsigned short overrun; // count of overruns since last block
union {
unsigned char u8[DATA_DIM8];
unsigned short u16[DATA_DIM16];
} data;
};
#endif // AnalogBinLogger_h

View File

@@ -0,0 +1,827 @@
/**
* This program logs data from the Arduino ADC to a binary file.
*
* Samples are logged at regular intervals. Each Sample consists of the ADC
* values for the analog pins defined in the PIN_LIST array. The pins numbers
* may be in any order.
*
* Edit the configuration constants below to set the sample pins, sample rate,
* and other configuration values.
*
* If your SD card has a long write latency, it may be necessary to use
* slower sample rates. Using a Mega Arduino helps overcome latency
* problems since 13 512 byte buffers will be used.
*
* Each 512 byte data block in the file has a four byte header followed by up
* to 508 bytes of data. (508 values in 8-bit mode or 254 values in 10-bit mode)
* Each block contains an integral number of samples with unused space at the
* end of the block.
*
* Data is written to the file using a SD multiple block write command.
*/
#ifdef __AVR__
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
#include "FreeStack.h"
#include "AnalogBinLogger.h"
//------------------------------------------------------------------------------
// Analog pin number list for a sample. Pins may be in any order and pin
// numbers may be repeated.
const uint8_t PIN_LIST[] = {0, 1, 2, 3, 4};
//------------------------------------------------------------------------------
// Sample rate in samples per second.
const float SAMPLE_RATE = 5000; // Must be 0.25 or greater.
// The interval between samples in seconds, SAMPLE_INTERVAL, may be set to a
// constant instead of being calculated from SAMPLE_RATE. SAMPLE_RATE is not
// used in the code below. For example, setting SAMPLE_INTERVAL = 2.0e-4
// will result in a 200 microsecond sample interval.
const float SAMPLE_INTERVAL = 1.0/SAMPLE_RATE;
// Setting ROUND_SAMPLE_INTERVAL non-zero will cause the sample interval to
// be rounded to a a multiple of the ADC clock period and will reduce sample
// time jitter.
#define ROUND_SAMPLE_INTERVAL 1
//------------------------------------------------------------------------------
// ADC clock rate.
// The ADC clock rate is normally calculated from the pin count and sample
// interval. The calculation attempts to use the lowest possible ADC clock
// rate.
//
// You can select an ADC clock rate by defining the symbol ADC_PRESCALER to
// one of these values. You must choose an appropriate ADC clock rate for
// your sample interval.
// #define ADC_PRESCALER 7 // F_CPU/128 125 kHz on an Uno
// #define ADC_PRESCALER 6 // F_CPU/64 250 kHz on an Uno
// #define ADC_PRESCALER 5 // F_CPU/32 500 kHz on an Uno
// #define ADC_PRESCALER 4 // F_CPU/16 1000 kHz on an Uno
// #define ADC_PRESCALER 3 // F_CPU/8 2000 kHz on an Uno (8-bit mode only)
//------------------------------------------------------------------------------
// Reference voltage. See the processor data-sheet for reference details.
// uint8_t const ADC_REF = 0; // External Reference AREF pin.
uint8_t const ADC_REF = (1 << REFS0); // Vcc Reference.
// uint8_t const ADC_REF = (1 << REFS1); // Internal 1.1 (only 644 1284P Mega)
// uint8_t const ADC_REF = (1 << REFS1) | (1 << REFS0); // Internal 1.1 or 2.56
//------------------------------------------------------------------------------
// File definitions.
//
// Maximum file size in blocks.
// The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks.
// This file is flash erased using special SD commands. The file will be
// truncated if logging is stopped early.
const uint32_t FILE_BLOCK_COUNT = 256000;
// log file base name. Must be six characters or less.
#define FILE_BASE_NAME "analog"
// Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC.
#define RECORD_EIGHT_BITS 0
//------------------------------------------------------------------------------
// Pin definitions.
//
// Digital pin to indicate an error, set to -1 if not used.
// The led blinks for fatal errors. The led goes on solid for SD write
// overrun errors and logging continues.
const int8_t ERROR_LED_PIN = 3;
// SD chip select pin.
const uint8_t SD_CS_PIN = SS;
//------------------------------------------------------------------------------
// Buffer definitions.
//
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
// buffers. QUEUE_DIM must be a power of two larger than
//(BUFFER_BLOCK_COUNT + 1).
//
#if RAMEND < 0X8FF
#error Too little SRAM
//
#elif RAMEND < 0X10FF
// Use total of two 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 1;
// Dimension for queues of 512 byte SD blocks.
const uint8_t QUEUE_DIM = 4; // Must be a power of two!
//
#elif RAMEND < 0X20FF
// Use total of five 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 4;
// Dimension for queues of 512 byte SD blocks.
const uint8_t QUEUE_DIM = 8; // Must be a power of two!
//
#elif RAMEND < 0X40FF
// Use total of 13 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 12;
// Dimension for queues of 512 byte SD blocks.
const uint8_t QUEUE_DIM = 16; // Must be a power of two!
//
#else // RAMEND
// Use total of 29 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 28;
// Dimension for queues of 512 byte SD blocks.
const uint8_t QUEUE_DIM = 32; // Must be a power of two!
#endif // RAMEND
//==============================================================================
// End of configuration constants.
//==============================================================================
// Temporary log file. Will be deleted if a reset or power failure occurs.
#define TMP_FILE_NAME "tmp_log.bin"
// Size of file base name. Must not be larger than six.
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
// Number of analog pins to log.
const uint8_t PIN_COUNT = sizeof(PIN_LIST)/sizeof(PIN_LIST[0]);
// Minimum ADC clock cycles per sample interval
const uint16_t MIN_ADC_CYCLES = 15;
// Extra cpu cycles to setup ADC with more than one pin per sample.
const uint16_t ISR_SETUP_ADC = PIN_COUNT > 1 ? 100 : 0;
// Maximum cycles for timer0 system interrupt, millis, micros.
const uint16_t ISR_TIMER0 = 160;
//==============================================================================
SdFat sd;
SdBaseFile binFile;
char binName[13] = FILE_BASE_NAME "00.bin";
#if RECORD_EIGHT_BITS
const size_t SAMPLES_PER_BLOCK = DATA_DIM8/PIN_COUNT;
typedef block8_t block_t;
#else // RECORD_EIGHT_BITS
const size_t SAMPLES_PER_BLOCK = DATA_DIM16/PIN_COUNT;
typedef block16_t block_t;
#endif // RECORD_EIGHT_BITS
block_t* emptyQueue[QUEUE_DIM];
uint8_t emptyHead;
uint8_t emptyTail;
block_t* fullQueue[QUEUE_DIM];
volatile uint8_t fullHead; // volatile insures non-interrupt code sees changes.
uint8_t fullTail;
// queueNext assumes QUEUE_DIM is a power of two
inline uint8_t queueNext(uint8_t ht) {
return (ht + 1) & (QUEUE_DIM -1);
}
//==============================================================================
// Interrupt Service Routines
// Pointer to current buffer.
block_t* isrBuf;
// Need new buffer if true.
bool isrBufNeeded = true;
// overrun count
uint16_t isrOver = 0;
// ADC configuration for each pin.
uint8_t adcmux[PIN_COUNT];
uint8_t adcsra[PIN_COUNT];
uint8_t adcsrb[PIN_COUNT];
uint8_t adcindex = 1;
// Insure no timer events are missed.
volatile bool timerError = false;
volatile bool timerFlag = false;
//------------------------------------------------------------------------------
// ADC done interrupt.
ISR(ADC_vect) {
// Read ADC data.
#if RECORD_EIGHT_BITS
uint8_t d = ADCH;
#else // RECORD_EIGHT_BITS
// This will access ADCL first.
uint16_t d = ADC;
#endif // RECORD_EIGHT_BITS
if (isrBufNeeded && emptyHead == emptyTail) {
// no buffers - count overrun
if (isrOver < 0XFFFF) {
isrOver++;
}
// Avoid missed timer error.
timerFlag = false;
return;
}
// Start ADC
if (PIN_COUNT > 1) {
ADMUX = adcmux[adcindex];
ADCSRB = adcsrb[adcindex];
ADCSRA = adcsra[adcindex];
if (adcindex == 0) {
timerFlag = false;
}
adcindex = adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0;
} else {
timerFlag = false;
}
// Check for buffer needed.
if (isrBufNeeded) {
// Remove buffer from empty queue.
isrBuf = emptyQueue[emptyTail];
emptyTail = queueNext(emptyTail);
isrBuf->count = 0;
isrBuf->overrun = isrOver;
isrBufNeeded = false;
}
// Store ADC data.
isrBuf->data[isrBuf->count++] = d;
// Check for buffer full.
if (isrBuf->count >= PIN_COUNT*SAMPLES_PER_BLOCK) {
// Put buffer isrIn full queue.
uint8_t tmp = fullHead; // Avoid extra fetch of volatile fullHead.
fullQueue[tmp] = (block_t*)isrBuf;
fullHead = queueNext(tmp);
// Set buffer needed and clear overruns.
isrBufNeeded = true;
isrOver = 0;
}
}
//------------------------------------------------------------------------------
// timer1 interrupt to clear OCF1B
ISR(TIMER1_COMPB_vect) {
// Make sure ADC ISR responded to timer event.
if (timerFlag) {
timerError = true;
}
timerFlag = true;
}
//==============================================================================
// Error messages stored in flash.
#define error(msg) {sd.errorPrint(F(msg));fatalBlink();}
//------------------------------------------------------------------------------
//
void fatalBlink() {
while (true) {
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
delay(200);
digitalWrite(ERROR_LED_PIN, LOW);
delay(200);
}
}
}
//==============================================================================
#if ADPS0 != 0 || ADPS1 != 1 || ADPS2 != 2
#error unexpected ADC prescaler bits
#endif
//------------------------------------------------------------------------------
// initialize ADC and timer1
void adcInit(metadata_t* meta) {
uint8_t adps; // prescaler bits for ADCSRA
uint32_t ticks = F_CPU*SAMPLE_INTERVAL + 0.5; // Sample interval cpu cycles.
if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) {
error("Invalid ADC reference");
}
#ifdef ADC_PRESCALER
if (ADC_PRESCALER > 7 || ADC_PRESCALER < 2) {
error("Invalid ADC prescaler");
}
adps = ADC_PRESCALER;
#else // ADC_PRESCALER
// Allow extra cpu cycles to change ADC settings if more than one pin.
int32_t adcCycles = (ticks - ISR_TIMER0)/PIN_COUNT - ISR_SETUP_ADC;
for (adps = 7; adps > 0; adps--) {
if (adcCycles >= (MIN_ADC_CYCLES << adps)) {
break;
}
}
#endif // ADC_PRESCALER
meta->adcFrequency = F_CPU >> adps;
if (meta->adcFrequency > (RECORD_EIGHT_BITS ? 2000000 : 1000000)) {
error("Sample Rate Too High");
}
#if ROUND_SAMPLE_INTERVAL
// Round so interval is multiple of ADC clock.
ticks += 1 << (adps - 1);
ticks >>= adps;
ticks <<= adps;
#endif // ROUND_SAMPLE_INTERVAL
if (PIN_COUNT > sizeof(meta->pinNumber)/sizeof(meta->pinNumber[0])) {
error("Too many pins");
}
meta->pinCount = PIN_COUNT;
meta->recordEightBits = RECORD_EIGHT_BITS;
for (int i = 0; i < PIN_COUNT; i++) {
uint8_t pin = PIN_LIST[i];
if (pin >= NUM_ANALOG_INPUTS) {
error("Invalid Analog pin number");
}
meta->pinNumber[i] = pin;
// Set ADC reference and low three bits of analog pin number.
adcmux[i] = (pin & 7) | ADC_REF;
if (RECORD_EIGHT_BITS) {
adcmux[i] |= 1 << ADLAR;
}
// If this is the first pin, trigger on timer/counter 1 compare match B.
adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0;
#ifdef MUX5
if (pin > 7) {
adcsrb[i] |= (1 << MUX5);
}
#endif // MUX5
adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps;
adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC;
}
// Setup timer1
TCCR1A = 0;
uint8_t tshift;
if (ticks < 0X10000) {
// no prescale, CTC mode
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
tshift = 0;
} else if (ticks < 0X10000*8) {
// prescale 8, CTC mode
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
tshift = 3;
} else if (ticks < 0X10000*64) {
// prescale 64, CTC mode
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11) | (1 << CS10);
tshift = 6;
} else if (ticks < 0X10000*256) {
// prescale 256, CTC mode
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12);
tshift = 8;
} else if (ticks < 0X10000*1024) {
// prescale 1024, CTC mode
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12) | (1 << CS10);
tshift = 10;
} else {
error("Sample Rate Too Slow");
}
// divide by prescaler
ticks >>= tshift;
// set TOP for timer reset
ICR1 = ticks - 1;
// compare for ADC start
OCR1B = 0;
// multiply by prescaler
ticks <<= tshift;
// Sample interval in CPU clock ticks.
meta->sampleInterval = ticks;
meta->cpuFrequency = F_CPU;
float sampleRate = (float)meta->cpuFrequency/meta->sampleInterval;
Serial.print(F("Sample pins:"));
for (uint8_t i = 0; i < meta->pinCount; i++) {
Serial.print(' ');
Serial.print(meta->pinNumber[i], DEC);
}
Serial.println();
Serial.print(F("ADC bits: "));
Serial.println(meta->recordEightBits ? 8 : 10);
Serial.print(F("ADC clock kHz: "));
Serial.println(meta->adcFrequency/1000);
Serial.print(F("Sample Rate: "));
Serial.println(sampleRate);
Serial.print(F("Sample interval usec: "));
Serial.println(1000000.0/sampleRate, 4);
}
//------------------------------------------------------------------------------
// enable ADC and timer1 interrupts
void adcStart() {
// initialize ISR
isrBufNeeded = true;
isrOver = 0;
adcindex = 1;
// Clear any pending interrupt.
ADCSRA |= 1 << ADIF;
// Setup for first pin.
ADMUX = adcmux[0];
ADCSRB = adcsrb[0];
ADCSRA = adcsra[0];
// Enable timer1 interrupts.
timerError = false;
timerFlag = false;
TCNT1 = 0;
TIFR1 = 1 << OCF1B;
TIMSK1 = 1 << OCIE1B;
}
//------------------------------------------------------------------------------
void adcStop() {
TIMSK1 = 0;
ADCSRA = 0;
}
//------------------------------------------------------------------------------
// Convert binary file to csv file.
void binaryToCsv() {
uint8_t lastPct = 0;
block_t buf;
metadata_t* pm;
uint32_t t0 = millis();
char csvName[13];
StdioStream csvStream;
if (!binFile.isOpen()) {
Serial.println(F("No current binary file"));
return;
}
binFile.rewind();
if (binFile.read(&buf , 512) != 512) {
error("Read metadata failed");
}
// Create a new csv file.
strcpy(csvName, binName);
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
if (!csvStream.fopen(csvName, "w")) {
error("open csvStream failed");
}
Serial.println();
Serial.print(F("Writing: "));
Serial.print(csvName);
Serial.println(F(" - type any character to stop"));
pm = (metadata_t*)&buf;
csvStream.print(F("Interval,"));
float intervalMicros = 1.0e6*pm->sampleInterval/(float)pm->cpuFrequency;
csvStream.print(intervalMicros, 4);
csvStream.println(F(",usec"));
for (uint8_t i = 0; i < pm->pinCount; i++) {
if (i) {
csvStream.putc(',');
}
csvStream.print(F("pin"));
csvStream.print(pm->pinNumber[i]);
}
csvStream.println();
uint32_t tPct = millis();
while (!Serial.available() && binFile.read(&buf, 512) == 512) {
if (buf.count == 0) {
break;
}
if (buf.overrun) {
csvStream.print(F("OVERRUN,"));
csvStream.println(buf.overrun);
}
for (uint16_t j = 0; j < buf.count; j += PIN_COUNT) {
for (uint16_t i = 0; i < PIN_COUNT; i++) {
if (i) {
csvStream.putc(',');
}
csvStream.print(buf.data[i + j]);
}
csvStream.println();
}
if ((millis() - tPct) > 1000) {
uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100);
if (pct != lastPct) {
tPct = millis();
lastPct = pct;
Serial.print(pct, DEC);
Serial.println('%');
}
}
if (Serial.available()) {
break;
}
}
csvStream.fclose();
Serial.print(F("Done: "));
Serial.print(0.001*(millis() - t0));
Serial.println(F(" Seconds"));
}
//------------------------------------------------------------------------------
// read data file and check for overruns
void checkOverrun() {
bool headerPrinted = false;
block_t buf;
uint32_t bgnBlock, endBlock;
uint32_t bn = 0;
if (!binFile.isOpen()) {
Serial.println(F("No current binary file"));
return;
}
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
error("contiguousRange failed");
}
binFile.rewind();
Serial.println();
Serial.println(F("Checking overrun errors - type any character to stop"));
if (binFile.read(&buf , 512) != 512) {
error("Read metadata failed");
}
bn++;
while (binFile.read(&buf, 512) == 512) {
if (buf.count == 0) {
break;
}
if (buf.overrun) {
if (!headerPrinted) {
Serial.println();
Serial.println(F("Overruns:"));
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount"));
headerPrinted = true;
}
Serial.print(bn);
Serial.print(',');
Serial.print(bgnBlock + bn);
Serial.print(',');
Serial.println(buf.overrun);
}
bn++;
}
if (!headerPrinted) {
Serial.println(F("No errors found"));
} else {
Serial.println(F("Done"));
}
}
//------------------------------------------------------------------------------
// dump data file to Serial
void dumpData() {
block_t buf;
if (!binFile.isOpen()) {
Serial.println(F("No current binary file"));
return;
}
binFile.rewind();
if (binFile.read(&buf , 512) != 512) {
error("Read metadata failed");
}
Serial.println();
Serial.println(F("Type any character to stop"));
delay(1000);
while (!Serial.available() && binFile.read(&buf , 512) == 512) {
if (buf.count == 0) {
break;
}
if (buf.overrun) {
Serial.print(F("OVERRUN,"));
Serial.println(buf.overrun);
}
for (uint16_t i = 0; i < buf.count; i++) {
Serial.print(buf.data[i], DEC);
if ((i+1)%PIN_COUNT) {
Serial.print(',');
} else {
Serial.println();
}
}
}
Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
// log data
// max number of blocks to erase per erase call
uint32_t const ERASE_SIZE = 262144L;
void logData() {
uint32_t bgnBlock, endBlock;
// Allocate extra buffer space.
block_t block[BUFFER_BLOCK_COUNT];
Serial.println();
// Initialize ADC and timer1.
adcInit((metadata_t*) &block[0]);
// Find unused file name.
if (BASE_NAME_SIZE > 6) {
error("FILE_BASE_NAME too long");
}
while (sd.exists(binName)) {
if (binName[BASE_NAME_SIZE + 1] != '9') {
binName[BASE_NAME_SIZE + 1]++;
} else {
binName[BASE_NAME_SIZE + 1] = '0';
if (binName[BASE_NAME_SIZE] == '9') {
error("Can't create file name");
}
binName[BASE_NAME_SIZE]++;
}
}
// Delete old tmp file.
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("Deleting tmp file"));
if (!sd.remove(TMP_FILE_NAME)) {
error("Can't remove tmp file");
}
}
// Create new file.
Serial.println(F("Creating new file"));
binFile.close();
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
error("createContiguous failed");
}
// Get the address of the file on the SD.
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
error("contiguousRange failed");
}
// Use SdFat's internal buffer.
uint8_t* cache = (uint8_t*)sd.vol()->cacheClear();
if (cache == 0) {
error("cacheClear failed");
}
// Flash erase all data in the file.
Serial.println(F("Erasing all data"));
uint32_t bgnErase = bgnBlock;
uint32_t endErase;
while (bgnErase < endBlock) {
endErase = bgnErase + ERASE_SIZE;
if (endErase > endBlock) {
endErase = endBlock;
}
if (!sd.card()->erase(bgnErase, endErase)) {
error("erase failed");
}
bgnErase = endErase + 1;
}
// Start a multiple block write.
if (!sd.card()->writeStart(bgnBlock)) {
error("writeBegin failed");
}
// Write metadata.
if (!sd.card()->writeData((uint8_t*)&block[0])) {
error("Write metadata failed");
}
// Initialize queues.
emptyHead = emptyTail = 0;
fullHead = fullTail = 0;
// Use SdFat buffer for one block.
emptyQueue[emptyHead] = (block_t*)cache;
emptyHead = queueNext(emptyHead);
// Put rest of buffers in the empty queue.
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
emptyQueue[emptyHead] = &block[i];
emptyHead = queueNext(emptyHead);
}
// Give SD time to prepare for big write.
delay(1000);
Serial.println(F("Logging - type any character to stop"));
// Wait for Serial Idle.
Serial.flush();
delay(10);
uint32_t bn = 1;
uint32_t t0 = millis();
uint32_t t1 = t0;
uint32_t overruns = 0;
uint32_t count = 0;
uint32_t maxLatency = 0;
// Start logging interrupts.
adcStart();
while (1) {
if (fullHead != fullTail) {
// Get address of block to write.
block_t* pBlock = fullQueue[fullTail];
// Write block to SD.
uint32_t usec = micros();
if (!sd.card()->writeData((uint8_t*)pBlock)) {
error("write data failed");
}
usec = micros() - usec;
t1 = millis();
if (usec > maxLatency) {
maxLatency = usec;
}
count += pBlock->count;
// Add overruns and possibly light LED.
if (pBlock->overrun) {
overruns += pBlock->overrun;
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
}
}
// Move block to empty queue.
emptyQueue[emptyHead] = pBlock;
emptyHead = queueNext(emptyHead);
fullTail = queueNext(fullTail);
bn++;
if (bn == FILE_BLOCK_COUNT) {
// File full so stop ISR calls.
adcStop();
break;
}
}
if (timerError) {
error("Missed timer event - rate too high");
}
if (Serial.available()) {
// Stop ISR calls.
adcStop();
if (isrBuf != 0 && isrBuf->count >= PIN_COUNT) {
// Truncate to last complete sample.
isrBuf->count = PIN_COUNT*(isrBuf->count/PIN_COUNT);
// Put buffer in full queue.
fullQueue[fullHead] = isrBuf;
fullHead = queueNext(fullHead);
isrBuf = 0;
}
if (fullHead == fullTail) {
break;
}
}
}
if (!sd.card()->writeStop()) {
error("writeStop failed");
}
// Truncate file if recording stopped early.
if (bn != FILE_BLOCK_COUNT) {
Serial.println(F("Truncating file"));
if (!binFile.truncate(512L * bn)) {
error("Can't truncate file");
}
}
if (!binFile.rename(binName)) {
error("Can't rename file");
}
Serial.print(F("File renamed: "));
Serial.println(binName);
Serial.print(F("Max block write usec: "));
Serial.println(maxLatency);
Serial.print(F("Record time sec: "));
Serial.println(0.001*(t1 - t0), 3);
Serial.print(F("Sample count: "));
Serial.println(count/PIN_COUNT);
Serial.print(F("Samples/sec: "));
Serial.println((1000.0/PIN_COUNT)*count/(t1-t0));
Serial.print(F("Overruns: "));
Serial.println(overruns);
Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
void setup(void) {
if (ERROR_LED_PIN >= 0) {
pinMode(ERROR_LED_PIN, OUTPUT);
}
Serial.begin(9600);
// Read the first sample pin to init the ADC.
analogRead(PIN_LIST[0]);
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
sd.initErrorPrint();
fatalBlink();
}
}
//------------------------------------------------------------------------------
void loop(void) {
// Read any Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
Serial.println();
Serial.println(F("type:"));
Serial.println(F("c - convert file to csv"));
Serial.println(F("d - dump data to Serial"));
Serial.println(F("e - overrun error details"));
Serial.println(F("r - record ADC data"));
while(!Serial.available()) {
yield();
}
char c = tolower(Serial.read());
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, LOW);
}
// Read any Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
if (c == 'c') {
binaryToCsv();
} else if (c == 'd') {
dumpData();
} else if (c == 'e') {
checkOverrun();
} else if (c == 'r') {
logData();
} else {
Serial.println(F("Invalid entry"));
}
}
#else // __AVR__
#error This program is only for AVR.
#endif // __AVR__

View File

@@ -0,0 +1,102 @@
// Example use of lfnOpenNext and open by index.
// You can use test files located in
// SdFat/examples/LongFileName/testFiles.
#include<SPI.h>
#include "SdFat.h"
#include "FreeStack.h"
// SD card chip select pin.
const uint8_t SD_CS_PIN = SS;
SdFat sd;
SdFile file;
SdFile dirFile;
// Number of files found.
uint16_t n = 0;
// Max of ten files since files are selected with a single digit.
const uint16_t nMax = 10;
// Position of file's directory entry.
uint16_t dirIndex[nMax];
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
while (!Serial) {}
delay(1000);
// Print the location of some test files.
Serial.println(F("\r\n"
"You can use test files located in\r\n"
"SdFat/examples/LongFileName/testFiles"));
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
Serial.println();
// List files in root directory.
if (!dirFile.open("/", O_RDONLY)) {
sd.errorHalt("open root failed");
}
while (n < nMax && file.openNext(&dirFile, O_RDONLY)) {
// Skip directories and hidden files.
if (!file.isSubDir() && !file.isHidden()) {
// Save dirIndex of file in directory.
dirIndex[n] = file.dirIndex();
// Print the file number and name.
Serial.print(n++);
Serial.write(' ');
file.printName(&Serial);
Serial.println();
}
file.close();
}
}
//------------------------------------------------------------------------------
void loop() {
int c;
// Read any existing Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
Serial.print(F("\r\nEnter File Number: "));
while (!Serial.available()) {
yield();
}
c = Serial.read();
uint8_t i = c - '0';
if (!isdigit(c) || i >= n) {
Serial.println(F("Invald number"));
return;
}
Serial.println(i);
if (!file.open(&dirFile, dirIndex[i], O_RDONLY)) {
sd.errorHalt(F("open"));
}
Serial.println();
char last = 0;
// Copy up to 500 characters to Serial.
for (int k = 0; k < 500 && (c = file.read()) > 0; k++) {
Serial.write(last = (char)c);
}
// Add new line if missing from last line.
if (last != '\n') {
Serial.println();
}
file.close();
Serial.flush();
delay(100);
}

View File

@@ -0,0 +1,4 @@
This is "A long name can be 255 characters.txt"
This file has a typical Long File Name.
The maximum length of a Long File Name is 255 characters.

View File

@@ -0,0 +1 @@
LFN,NAME.TXT is not 8.3 since it has a comma.

View File

@@ -0,0 +1,5 @@
MIXCASE.txt does not have a Long File Name.
Starting with NT, file names of this form,
have the basename and extension character case
encoded in two bits of the 8.3 directory entry.

View File

@@ -0,0 +1,2 @@
Not_8_3.txt has a Long File Name
since the basename is mixed case.

View File

@@ -0,0 +1 @@
OK%83.TXT is a valid 8.3 name.

View File

@@ -0,0 +1 @@
STD_8_3.TXT - a vanilla 8.3 name.

View File

@@ -0,0 +1,2 @@
With Blank.txt
Just another example of a Long File Name.

View File

@@ -0,0 +1,2 @@
"With Two.dots.txt"
Lots of reasons this is a Long File Name.

View File

@@ -0,0 +1,5 @@
lower.txt does not have a Long File Name.
Starting with NT, file names of this form,
have the basename and extension character case
encoded in two bits of the 8.3 directory entry.

View File

@@ -0,0 +1,5 @@
mixed.TXT does not have a Long File Name.
Starting with NT, file names of this form,
have the basename and extension character case
encoded in two bits of the 8.3 directory entry.

View File

@@ -0,0 +1,655 @@
/**
* This program logs data to a binary file. Functions are included
* to convert the binary file to a csv text file.
*
* Samples are logged at regular intervals. The maximum logging rate
* depends on the quality of your SD card and the time required to
* read sensor data. This example has been tested at 500 Hz with
* good SD card on an Uno. 4000 HZ is possible on a Due.
*
* If your SD card has a long write latency, it may be necessary to use
* slower sample rates. Using a Mega Arduino helps overcome latency
* problems since 12 512 byte buffers will be used.
*
* Data is written to the file using a SD multiple block write command.
*/
#include <SPI.h>
#include "SdFat.h"
#include "FreeStack.h"
#include "UserTypes.h"
#ifdef __AVR_ATmega328P__
#include "MinimumSerial.h"
MinimumSerial MinSerial;
#define Serial MinSerial
#endif // __AVR_ATmega328P__
//==============================================================================
// Start of configuration constants.
//==============================================================================
// Abort run on an overrun. Data before the overrun will be saved.
#define ABORT_ON_OVERRUN 1
//------------------------------------------------------------------------------
//Interval between data records in microseconds.
const uint32_t LOG_INTERVAL_USEC = 2000;
//------------------------------------------------------------------------------
// Set USE_SHARED_SPI non-zero for use of an SPI sensor.
// May not work for some cards.
#ifndef USE_SHARED_SPI
#define USE_SHARED_SPI 0
#endif // USE_SHARED_SPI
//------------------------------------------------------------------------------
// Pin definitions.
//
// SD chip select pin.
const uint8_t SD_CS_PIN = SS;
//
// Digital pin to indicate an error, set to -1 if not used.
// The led blinks for fatal errors. The led goes on solid for
// overrun errors and logging continues unless ABORT_ON_OVERRUN
// is non-zero.
#ifdef ERROR_LED_PIN
#undef ERROR_LED_PIN
#endif // ERROR_LED_PIN
const int8_t ERROR_LED_PIN = -1;
//------------------------------------------------------------------------------
// File definitions.
//
// Maximum file size in blocks.
// The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks.
// This file is flash erased using special SD commands. The file will be
// truncated if logging is stopped early.
const uint32_t FILE_BLOCK_COUNT = 256000;
//
// log file base name if not defined in UserTypes.h
#ifndef FILE_BASE_NAME
#define FILE_BASE_NAME "data"
#endif // FILE_BASE_NAME
//------------------------------------------------------------------------------
// Buffer definitions.
//
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional
// buffers.
//
#ifndef RAMEND
// Assume ARM. Use total of ten 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 10;
//
#elif RAMEND < 0X8FF
#error Too little SRAM
//
#elif RAMEND < 0X10FF
// Use total of two 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 2;
//
#elif RAMEND < 0X20FF
// Use total of four 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 4;
//
#else // RAMEND
// Use total of 12 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 12;
#endif // RAMEND
//==============================================================================
// End of configuration constants.
//==============================================================================
// Temporary log file. Will be deleted if a reset or power failure occurs.
#define TMP_FILE_NAME FILE_BASE_NAME "##.bin"
// Size of file base name.
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7;
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin";
SdFat sd;
SdBaseFile binFile;
// Number of data records in a block.
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t);
//Compute fill so block size is 512 bytes. FILL_DIM may be zero.
const uint16_t FILL_DIM = 512 - 4 - DATA_DIM*sizeof(data_t);
struct block_t {
uint16_t count;
uint16_t overrun;
data_t data[DATA_DIM];
uint8_t fill[FILL_DIM];
};
//==============================================================================
// Error messages stored in flash.
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();}
//------------------------------------------------------------------------------
//
void fatalBlink() {
while (true) {
yield();
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
delay(200);
digitalWrite(ERROR_LED_PIN, LOW);
delay(200);
}
}
}
//------------------------------------------------------------------------------
// read data file and check for overruns
void checkOverrun() {
bool headerPrinted = false;
block_t block;
uint32_t bn = 0;
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
binFile.rewind();
Serial.println();
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
Serial.println(F("Checking overrun errors - type any character to stop"));
while (binFile.read(&block, 512) == 512) {
if (block.count == 0) {
break;
}
if (block.overrun) {
if (!headerPrinted) {
Serial.println();
Serial.println(F("Overruns:"));
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount"));
headerPrinted = true;
}
Serial.print(bn);
Serial.print(',');
Serial.print(binFile.firstBlock() + bn);
Serial.print(',');
Serial.println(block.overrun);
}
bn++;
}
if (!headerPrinted) {
Serial.println(F("No errors found"));
} else {
Serial.println(F("Done"));
}
}
//-----------------------------------------------------------------------------
// Convert binary file to csv file.
void binaryToCsv() {
uint8_t lastPct = 0;
block_t block;
uint32_t t0 = millis();
uint32_t syncCluster = 0;
SdFile csvFile;
char csvName[FILE_NAME_DIM];
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
Serial.println();
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
// Create a new csvFile.
strcpy(csvName, binName);
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
error("open csvFile failed");
}
binFile.rewind();
Serial.print(F("Writing: "));
Serial.print(csvName);
Serial.println(F(" - type any character to stop"));
printHeader(&csvFile);
uint32_t tPct = millis();
while (!Serial.available() && binFile.read(&block, 512) == 512) {
uint16_t i;
if (block.count == 0 || block.count > DATA_DIM) {
break;
}
if (block.overrun) {
csvFile.print(F("OVERRUN,"));
csvFile.println(block.overrun);
}
for (i = 0; i < block.count; i++) {
printData(&csvFile, &block.data[i]);
}
if (csvFile.curCluster() != syncCluster) {
csvFile.sync();
syncCluster = csvFile.curCluster();
}
if ((millis() - tPct) > 1000) {
uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100);
if (pct != lastPct) {
tPct = millis();
lastPct = pct;
Serial.print(pct, DEC);
Serial.println('%');
}
}
if (Serial.available()) {
break;
}
}
csvFile.close();
Serial.print(F("Done: "));
Serial.print(0.001*(millis() - t0));
Serial.println(F(" Seconds"));
}
//-----------------------------------------------------------------------------
void createBinFile() {
// max number of blocks to erase per erase call
const uint32_t ERASE_SIZE = 262144L;
uint32_t bgnBlock, endBlock;
// Delete old tmp file.
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("Deleting tmp file " TMP_FILE_NAME));
if (!sd.remove(TMP_FILE_NAME)) {
error("Can't remove tmp file");
}
}
// Create new file.
Serial.println(F("\nCreating new file"));
binFile.close();
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
error("createContiguous failed");
}
// Get the address of the file on the SD.
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
error("contiguousRange failed");
}
// Flash erase all data in the file.
Serial.println(F("Erasing all data"));
uint32_t bgnErase = bgnBlock;
uint32_t endErase;
while (bgnErase < endBlock) {
endErase = bgnErase + ERASE_SIZE;
if (endErase > endBlock) {
endErase = endBlock;
}
if (!sd.card()->erase(bgnErase, endErase)) {
error("erase failed");
}
bgnErase = endErase + 1;
}
}
//------------------------------------------------------------------------------
// dump data file to Serial
void dumpData() {
block_t block;
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
binFile.rewind();
Serial.println();
Serial.println(F("Type any character to stop"));
delay(1000);
printHeader(&Serial);
while (!Serial.available() && binFile.read(&block , 512) == 512) {
if (block.count == 0) {
break;
}
if (block.overrun) {
Serial.print(F("OVERRUN,"));
Serial.println(block.overrun);
}
for (uint16_t i = 0; i < block.count; i++) {
printData(&Serial, &block.data[i]);
}
}
Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
// log data
void logData() {
createBinFile();
recordBinFile();
renameBinFile();
}
//------------------------------------------------------------------------------
void openBinFile() {
char name[FILE_NAME_DIM];
strcpy(name, binName);
Serial.println(F("\nEnter two digit version"));
Serial.write(name, BASE_NAME_SIZE);
for (int i = 0; i < 2; i++) {
while (!Serial.available()) {
yield();
}
char c = Serial.read();
Serial.write(c);
if (c < '0' || c > '9') {
Serial.println(F("\nInvalid digit"));
return;
}
name[BASE_NAME_SIZE + i] = c;
}
Serial.println(&name[BASE_NAME_SIZE+2]);
if (!sd.exists(name)) {
Serial.println(F("File does not exist"));
return;
}
binFile.close();
strcpy(binName, name);
if (!binFile.open(binName, O_RDONLY)) {
Serial.println(F("open failed"));
return;
}
Serial.println(F("File opened"));
}
//------------------------------------------------------------------------------
void recordBinFile() {
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1;
// Index of last queue location.
const uint8_t QUEUE_LAST = QUEUE_DIM - 1;
// Allocate extra buffer space.
block_t block[BUFFER_BLOCK_COUNT - 1];
block_t* curBlock = 0;
block_t* emptyStack[BUFFER_BLOCK_COUNT];
uint8_t emptyTop;
uint8_t minTop;
block_t* fullQueue[QUEUE_DIM];
uint8_t fullHead = 0;
uint8_t fullTail = 0;
// Use SdFat's internal buffer.
emptyStack[0] = (block_t*)sd.vol()->cacheClear();
if (emptyStack[0] == 0) {
error("cacheClear failed");
}
// Put rest of buffers on the empty stack.
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) {
emptyStack[i] = &block[i - 1];
}
emptyTop = BUFFER_BLOCK_COUNT;
minTop = BUFFER_BLOCK_COUNT;
// Start a multiple block write.
if (!sd.card()->writeStart(binFile.firstBlock())) {
error("writeStart failed");
}
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
Serial.println(F("Logging - type any character to stop"));
bool closeFile = false;
uint32_t bn = 0;
uint32_t maxLatency = 0;
uint32_t overrun = 0;
uint32_t overrunTotal = 0;
uint32_t logTime = micros();
while(1) {
// Time for next data record.
logTime += LOG_INTERVAL_USEC;
if (Serial.available()) {
closeFile = true;
}
if (closeFile) {
if (curBlock != 0) {
// Put buffer in full queue.
fullQueue[fullHead] = curBlock;
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
curBlock = 0;
}
} else {
if (curBlock == 0 && emptyTop != 0) {
curBlock = emptyStack[--emptyTop];
if (emptyTop < minTop) {
minTop = emptyTop;
}
curBlock->count = 0;
curBlock->overrun = overrun;
overrun = 0;
}
if ((int32_t)(logTime - micros()) < 0) {
error("Rate too fast");
}
int32_t delta;
do {
delta = micros() - logTime;
} while (delta < 0);
if (curBlock == 0) {
overrun++;
overrunTotal++;
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
}
#if ABORT_ON_OVERRUN
Serial.println(F("Overrun abort"));
break;
#endif // ABORT_ON_OVERRUN
} else {
#if USE_SHARED_SPI
sd.card()->spiStop();
#endif // USE_SHARED_SPI
acquireData(&curBlock->data[curBlock->count++]);
#if USE_SHARED_SPI
sd.card()->spiStart();
#endif // USE_SHARED_SPI
if (curBlock->count == DATA_DIM) {
fullQueue[fullHead] = curBlock;
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
curBlock = 0;
}
}
}
if (fullHead == fullTail) {
// Exit loop if done.
if (closeFile) {
break;
}
} else if (!sd.card()->isBusy()) {
// Get address of block to write.
block_t* pBlock = fullQueue[fullTail];
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0;
// Write block to SD.
uint32_t usec = micros();
if (!sd.card()->writeData((uint8_t*)pBlock)) {
error("write data failed");
}
usec = micros() - usec;
if (usec > maxLatency) {
maxLatency = usec;
}
// Move block to empty queue.
emptyStack[emptyTop++] = pBlock;
bn++;
if (bn == FILE_BLOCK_COUNT) {
// File full so stop
break;
}
}
}
if (!sd.card()->writeStop()) {
error("writeStop failed");
}
Serial.print(F("Min Free buffers: "));
Serial.println(minTop);
Serial.print(F("Max block write usec: "));
Serial.println(maxLatency);
Serial.print(F("Overruns: "));
Serial.println(overrunTotal);
// Truncate file if recording stopped early.
if (bn != FILE_BLOCK_COUNT) {
Serial.println(F("Truncating file"));
if (!binFile.truncate(512L * bn)) {
error("Can't truncate file");
}
}
}
//------------------------------------------------------------------------------
void recoverTmpFile() {
uint16_t count;
if (!binFile.open(TMP_FILE_NAME, O_RDWR)) {
return;
}
if (binFile.read(&count, 2) != 2 || count != DATA_DIM) {
error("Please delete existing " TMP_FILE_NAME);
}
Serial.println(F("\nRecovering data in tmp file " TMP_FILE_NAME));
uint32_t bgnBlock = 0;
uint32_t endBlock = binFile.fileSize()/512 - 1;
// find last used block.
while (bgnBlock < endBlock) {
uint32_t midBlock = (bgnBlock + endBlock + 1)/2;
binFile.seekSet(512*midBlock);
if (binFile.read(&count, 2) != 2) error("read");
if (count == 0 || count > DATA_DIM) {
endBlock = midBlock - 1;
} else {
bgnBlock = midBlock;
}
}
// truncate after last used block.
if (!binFile.truncate(512*(bgnBlock + 1))) {
error("Truncate " TMP_FILE_NAME " failed");
}
renameBinFile();
}
//-----------------------------------------------------------------------------
void renameBinFile() {
while (sd.exists(binName)) {
if (binName[BASE_NAME_SIZE + 1] != '9') {
binName[BASE_NAME_SIZE + 1]++;
} else {
binName[BASE_NAME_SIZE + 1] = '0';
if (binName[BASE_NAME_SIZE] == '9') {
error("Can't create file name");
}
binName[BASE_NAME_SIZE]++;
}
}
if (!binFile.rename(binName)) {
error("Can't rename file");
}
Serial.print(F("File renamed: "));
Serial.println(binName);
Serial.print(F("File size: "));
Serial.print(binFile.fileSize()/512);
Serial.println(F(" blocks"));
}
//------------------------------------------------------------------------------
void testSensor() {
const uint32_t interval = 200000;
int32_t diff;
data_t data;
Serial.println(F("\nTesting - type any character to stop\n"));
// Wait for Serial Idle.
delay(1000);
printHeader(&Serial);
uint32_t m = micros();
while (!Serial.available()) {
m += interval;
do {
diff = m - micros();
} while (diff > 0);
acquireData(&data);
printData(&Serial, &data);
}
}
//------------------------------------------------------------------------------
void setup(void) {
if (ERROR_LED_PIN >= 0) {
pinMode(ERROR_LED_PIN, OUTPUT);
}
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
Serial.print(F("\nFreeStack: "));
Serial.println(FreeStack());
Serial.print(F("Records/block: "));
Serial.println(DATA_DIM);
if (sizeof(block_t) != 512) {
error("Invalid block size");
}
// Allow userSetup access to SPI bus.
pinMode(SD_CS_PIN, OUTPUT);
digitalWrite(SD_CS_PIN, HIGH);
// Setup sensors.
userSetup();
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
sd.initErrorPrint(&Serial);
fatalBlink();
}
// recover existing tmp file.
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
while (!Serial.available()) {
yield();
}
if (Serial.read() == 'Y') {
recoverTmpFile();
} else {
error("'Y' not typed, please manually delete " TMP_FILE_NAME);
}
}
}
//------------------------------------------------------------------------------
void loop(void) {
// Read any Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
Serial.println();
Serial.println(F("type:"));
Serial.println(F("b - open existing bin file"));
Serial.println(F("c - convert file to csv"));
Serial.println(F("d - dump data to Serial"));
Serial.println(F("e - overrun error details"));
Serial.println(F("l - list files"));
Serial.println(F("r - record data"));
Serial.println(F("t - test without logging"));
while(!Serial.available()) {
yield();
}
#if WDT_YIELD_TIME_MICROS
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
while (true) {}
#endif
char c = tolower(Serial.read());
// Discard extra Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, LOW);
}
if (c == 'b') {
openBinFile();
} else if (c == 'c') {
binaryToCsv();
} else if (c == 'd') {
dumpData();
} else if (c == 'e') {
checkOverrun();
} else if (c == 'l') {
Serial.println(F("\nls:"));
sd.ls(&Serial, LS_SIZE);
} else if (c == 'r') {
logData();
} else if (c == 't') {
testSensor();
} else {
Serial.println(F("Invalid entry"));
}
}

View File

@@ -0,0 +1,41 @@
#include "UserTypes.h"
// User data functions. Modify these functions for your data items.
// Start time for data
static uint32_t startMicros;
// Acquire a data record.
void acquireData(data_t* data) {
data->time = micros();
for (int i = 0; i < ADC_DIM; i++) {
data->adc[i] = analogRead(i);
}
}
// Print a data record.
void printData(Print* pr, data_t* data) {
if (startMicros == 0) {
startMicros = data->time;
}
pr->print(data->time - startMicros);
for (int i = 0; i < ADC_DIM; i++) {
pr->write(',');
pr->print(data->adc[i]);
}
pr->println();
}
// Print data header.
void printHeader(Print* pr) {
startMicros = 0;
pr->print(F("micros"));
for (int i = 0; i < ADC_DIM; i++) {
pr->print(F(",adc"));
pr->print(i);
}
pr->println();
}
// Sensor setup
void userSetup() {
}

View File

@@ -0,0 +1,15 @@
#ifndef UserTypes_h
#define UserTypes_h
#include "Arduino.h"
// User data types. Modify for your data items.
#define FILE_BASE_NAME "adc4pin"
const uint8_t ADC_DIM = 4;
struct data_t {
uint32_t time;
uint16_t adc[ADC_DIM];
};
void acquireData(data_t* data);
void printData(Print* pr, data_t* data);
void printHeader(Print* pr);
void userSetup();
#endif // UserTypes_h

View File

@@ -0,0 +1,655 @@
/**
* This program logs data to a binary file. Functions are included
* to convert the binary file to a csv text file.
*
* Samples are logged at regular intervals. The maximum logging rate
* depends on the quality of your SD card and the time required to
* read sensor data. This example has been tested at 500 Hz with
* good SD card on an Uno. 4000 HZ is possible on a Due.
*
* If your SD card has a long write latency, it may be necessary to use
* slower sample rates. Using a Mega Arduino helps overcome latency
* problems since 12 512 byte buffers will be used.
*
* Data is written to the file using a SD multiple block write command.
*/
#include <SPI.h>
#include "SdFat.h"
#include "FreeStack.h"
#include "UserTypes.h"
#ifdef __AVR_ATmega328P__
#include "MinimumSerial.h"
MinimumSerial MinSerial;
#define Serial MinSerial
#endif // __AVR_ATmega328P__
//==============================================================================
// Start of configuration constants.
//==============================================================================
// Abort run on an overrun. Data before the overrun will be saved.
#define ABORT_ON_OVERRUN 1
//------------------------------------------------------------------------------
//Interval between data records in microseconds.
const uint32_t LOG_INTERVAL_USEC = 2000;
//------------------------------------------------------------------------------
// Set USE_SHARED_SPI non-zero for use of an SPI sensor.
// May not work for some cards.
#ifndef USE_SHARED_SPI
#define USE_SHARED_SPI 0
#endif // USE_SHARED_SPI
//------------------------------------------------------------------------------
// Pin definitions.
//
// SD chip select pin.
const uint8_t SD_CS_PIN = SS;
//
// Digital pin to indicate an error, set to -1 if not used.
// The led blinks for fatal errors. The led goes on solid for
// overrun errors and logging continues unless ABORT_ON_OVERRUN
// is non-zero.
#ifdef ERROR_LED_PIN
#undef ERROR_LED_PIN
#endif // ERROR_LED_PIN
const int8_t ERROR_LED_PIN = -1;
//------------------------------------------------------------------------------
// File definitions.
//
// Maximum file size in blocks.
// The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks.
// This file is flash erased using special SD commands. The file will be
// truncated if logging is stopped early.
const uint32_t FILE_BLOCK_COUNT = 256000;
//
// log file base name if not defined in UserTypes.h
#ifndef FILE_BASE_NAME
#define FILE_BASE_NAME "data"
#endif // FILE_BASE_NAME
//------------------------------------------------------------------------------
// Buffer definitions.
//
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional
// buffers.
//
#ifndef RAMEND
// Assume ARM. Use total of ten 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 10;
//
#elif RAMEND < 0X8FF
#error Too little SRAM
//
#elif RAMEND < 0X10FF
// Use total of two 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 2;
//
#elif RAMEND < 0X20FF
// Use total of four 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 4;
//
#else // RAMEND
// Use total of 12 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 12;
#endif // RAMEND
//==============================================================================
// End of configuration constants.
//==============================================================================
// Temporary log file. Will be deleted if a reset or power failure occurs.
#define TMP_FILE_NAME FILE_BASE_NAME "##.bin"
// Size of file base name.
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7;
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin";
SdFat sd;
SdBaseFile binFile;
// Number of data records in a block.
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t);
//Compute fill so block size is 512 bytes. FILL_DIM may be zero.
const uint16_t FILL_DIM = 512 - 4 - DATA_DIM*sizeof(data_t);
struct block_t {
uint16_t count;
uint16_t overrun;
data_t data[DATA_DIM];
uint8_t fill[FILL_DIM];
};
//==============================================================================
// Error messages stored in flash.
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();}
//------------------------------------------------------------------------------
//
void fatalBlink() {
while (true) {
yield();
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
delay(200);
digitalWrite(ERROR_LED_PIN, LOW);
delay(200);
}
}
}
//------------------------------------------------------------------------------
// read data file and check for overruns
void checkOverrun() {
bool headerPrinted = false;
block_t block;
uint32_t bn = 0;
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
binFile.rewind();
Serial.println();
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
Serial.println(F("Checking overrun errors - type any character to stop"));
while (binFile.read(&block, 512) == 512) {
if (block.count == 0) {
break;
}
if (block.overrun) {
if (!headerPrinted) {
Serial.println();
Serial.println(F("Overruns:"));
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount"));
headerPrinted = true;
}
Serial.print(bn);
Serial.print(',');
Serial.print(binFile.firstBlock() + bn);
Serial.print(',');
Serial.println(block.overrun);
}
bn++;
}
if (!headerPrinted) {
Serial.println(F("No errors found"));
} else {
Serial.println(F("Done"));
}
}
//-----------------------------------------------------------------------------
// Convert binary file to csv file.
void binaryToCsv() {
uint8_t lastPct = 0;
block_t block;
uint32_t t0 = millis();
uint32_t syncCluster = 0;
SdFile csvFile;
char csvName[FILE_NAME_DIM];
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
Serial.println();
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
// Create a new csvFile.
strcpy(csvName, binName);
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
error("open csvFile failed");
}
binFile.rewind();
Serial.print(F("Writing: "));
Serial.print(csvName);
Serial.println(F(" - type any character to stop"));
printHeader(&csvFile);
uint32_t tPct = millis();
while (!Serial.available() && binFile.read(&block, 512) == 512) {
uint16_t i;
if (block.count == 0 || block.count > DATA_DIM) {
break;
}
if (block.overrun) {
csvFile.print(F("OVERRUN,"));
csvFile.println(block.overrun);
}
for (i = 0; i < block.count; i++) {
printData(&csvFile, &block.data[i]);
}
if (csvFile.curCluster() != syncCluster) {
csvFile.sync();
syncCluster = csvFile.curCluster();
}
if ((millis() - tPct) > 1000) {
uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100);
if (pct != lastPct) {
tPct = millis();
lastPct = pct;
Serial.print(pct, DEC);
Serial.println('%');
}
}
if (Serial.available()) {
break;
}
}
csvFile.close();
Serial.print(F("Done: "));
Serial.print(0.001*(millis() - t0));
Serial.println(F(" Seconds"));
}
//-----------------------------------------------------------------------------
void createBinFile() {
// max number of blocks to erase per erase call
const uint32_t ERASE_SIZE = 262144L;
uint32_t bgnBlock, endBlock;
// Delete old tmp file.
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("Deleting tmp file " TMP_FILE_NAME));
if (!sd.remove(TMP_FILE_NAME)) {
error("Can't remove tmp file");
}
}
// Create new file.
Serial.println(F("\nCreating new file"));
binFile.close();
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
error("createContiguous failed");
}
// Get the address of the file on the SD.
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
error("contiguousRange failed");
}
// Flash erase all data in the file.
Serial.println(F("Erasing all data"));
uint32_t bgnErase = bgnBlock;
uint32_t endErase;
while (bgnErase < endBlock) {
endErase = bgnErase + ERASE_SIZE;
if (endErase > endBlock) {
endErase = endBlock;
}
if (!sd.card()->erase(bgnErase, endErase)) {
error("erase failed");
}
bgnErase = endErase + 1;
}
}
//------------------------------------------------------------------------------
// dump data file to Serial
void dumpData() {
block_t block;
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
binFile.rewind();
Serial.println();
Serial.println(F("Type any character to stop"));
delay(1000);
printHeader(&Serial);
while (!Serial.available() && binFile.read(&block , 512) == 512) {
if (block.count == 0) {
break;
}
if (block.overrun) {
Serial.print(F("OVERRUN,"));
Serial.println(block.overrun);
}
for (uint16_t i = 0; i < block.count; i++) {
printData(&Serial, &block.data[i]);
}
}
Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
// log data
void logData() {
createBinFile();
recordBinFile();
renameBinFile();
}
//------------------------------------------------------------------------------
void openBinFile() {
char name[FILE_NAME_DIM];
strcpy(name, binName);
Serial.println(F("\nEnter two digit version"));
Serial.write(name, BASE_NAME_SIZE);
for (int i = 0; i < 2; i++) {
while (!Serial.available()) {
yield();
}
char c = Serial.read();
Serial.write(c);
if (c < '0' || c > '9') {
Serial.println(F("\nInvalid digit"));
return;
}
name[BASE_NAME_SIZE + i] = c;
}
Serial.println(&name[BASE_NAME_SIZE+2]);
if (!sd.exists(name)) {
Serial.println(F("File does not exist"));
return;
}
binFile.close();
strcpy(binName, name);
if (!binFile.open(binName, O_RDONLY)) {
Serial.println(F("open failed"));
return;
}
Serial.println(F("File opened"));
}
//------------------------------------------------------------------------------
void recordBinFile() {
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1;
// Index of last queue location.
const uint8_t QUEUE_LAST = QUEUE_DIM - 1;
// Allocate extra buffer space.
block_t block[BUFFER_BLOCK_COUNT - 1];
block_t* curBlock = 0;
block_t* emptyStack[BUFFER_BLOCK_COUNT];
uint8_t emptyTop;
uint8_t minTop;
block_t* fullQueue[QUEUE_DIM];
uint8_t fullHead = 0;
uint8_t fullTail = 0;
// Use SdFat's internal buffer.
emptyStack[0] = (block_t*)sd.vol()->cacheClear();
if (emptyStack[0] == 0) {
error("cacheClear failed");
}
// Put rest of buffers on the empty stack.
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) {
emptyStack[i] = &block[i - 1];
}
emptyTop = BUFFER_BLOCK_COUNT;
minTop = BUFFER_BLOCK_COUNT;
// Start a multiple block write.
if (!sd.card()->writeStart(binFile.firstBlock())) {
error("writeStart failed");
}
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
Serial.println(F("Logging - type any character to stop"));
bool closeFile = false;
uint32_t bn = 0;
uint32_t maxLatency = 0;
uint32_t overrun = 0;
uint32_t overrunTotal = 0;
uint32_t logTime = micros();
while(1) {
// Time for next data record.
logTime += LOG_INTERVAL_USEC;
if (Serial.available()) {
closeFile = true;
}
if (closeFile) {
if (curBlock != 0) {
// Put buffer in full queue.
fullQueue[fullHead] = curBlock;
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
curBlock = 0;
}
} else {
if (curBlock == 0 && emptyTop != 0) {
curBlock = emptyStack[--emptyTop];
if (emptyTop < minTop) {
minTop = emptyTop;
}
curBlock->count = 0;
curBlock->overrun = overrun;
overrun = 0;
}
if ((int32_t)(logTime - micros()) < 0) {
error("Rate too fast");
}
int32_t delta;
do {
delta = micros() - logTime;
} while (delta < 0);
if (curBlock == 0) {
overrun++;
overrunTotal++;
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
}
#if ABORT_ON_OVERRUN
Serial.println(F("Overrun abort"));
break;
#endif // ABORT_ON_OVERRUN
} else {
#if USE_SHARED_SPI
sd.card()->spiStop();
#endif // USE_SHARED_SPI
acquireData(&curBlock->data[curBlock->count++]);
#if USE_SHARED_SPI
sd.card()->spiStart();
#endif // USE_SHARED_SPI
if (curBlock->count == DATA_DIM) {
fullQueue[fullHead] = curBlock;
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
curBlock = 0;
}
}
}
if (fullHead == fullTail) {
// Exit loop if done.
if (closeFile) {
break;
}
} else if (!sd.card()->isBusy()) {
// Get address of block to write.
block_t* pBlock = fullQueue[fullTail];
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0;
// Write block to SD.
uint32_t usec = micros();
if (!sd.card()->writeData((uint8_t*)pBlock)) {
error("write data failed");
}
usec = micros() - usec;
if (usec > maxLatency) {
maxLatency = usec;
}
// Move block to empty queue.
emptyStack[emptyTop++] = pBlock;
bn++;
if (bn == FILE_BLOCK_COUNT) {
// File full so stop
break;
}
}
}
if (!sd.card()->writeStop()) {
error("writeStop failed");
}
Serial.print(F("Min Free buffers: "));
Serial.println(minTop);
Serial.print(F("Max block write usec: "));
Serial.println(maxLatency);
Serial.print(F("Overruns: "));
Serial.println(overrunTotal);
// Truncate file if recording stopped early.
if (bn != FILE_BLOCK_COUNT) {
Serial.println(F("Truncating file"));
if (!binFile.truncate(512L * bn)) {
error("Can't truncate file");
}
}
}
//------------------------------------------------------------------------------
void recoverTmpFile() {
uint16_t count;
if (!binFile.open(TMP_FILE_NAME, O_RDWR)) {
return;
}
if (binFile.read(&count, 2) != 2 || count != DATA_DIM) {
error("Please delete existing " TMP_FILE_NAME);
}
Serial.println(F("\nRecovering data in tmp file " TMP_FILE_NAME));
uint32_t bgnBlock = 0;
uint32_t endBlock = binFile.fileSize()/512 - 1;
// find last used block.
while (bgnBlock < endBlock) {
uint32_t midBlock = (bgnBlock + endBlock + 1)/2;
binFile.seekSet(512*midBlock);
if (binFile.read(&count, 2) != 2) error("read");
if (count == 0 || count > DATA_DIM) {
endBlock = midBlock - 1;
} else {
bgnBlock = midBlock;
}
}
// truncate after last used block.
if (!binFile.truncate(512*(bgnBlock + 1))) {
error("Truncate " TMP_FILE_NAME " failed");
}
renameBinFile();
}
//-----------------------------------------------------------------------------
void renameBinFile() {
while (sd.exists(binName)) {
if (binName[BASE_NAME_SIZE + 1] != '9') {
binName[BASE_NAME_SIZE + 1]++;
} else {
binName[BASE_NAME_SIZE + 1] = '0';
if (binName[BASE_NAME_SIZE] == '9') {
error("Can't create file name");
}
binName[BASE_NAME_SIZE]++;
}
}
if (!binFile.rename(binName)) {
error("Can't rename file");
}
Serial.print(F("File renamed: "));
Serial.println(binName);
Serial.print(F("File size: "));
Serial.print(binFile.fileSize()/512);
Serial.println(F(" blocks"));
}
//------------------------------------------------------------------------------
void testSensor() {
const uint32_t interval = 200000;
int32_t diff;
data_t data;
Serial.println(F("\nTesting - type any character to stop\n"));
// Wait for Serial Idle.
delay(1000);
printHeader(&Serial);
uint32_t m = micros();
while (!Serial.available()) {
m += interval;
do {
diff = m - micros();
} while (diff > 0);
acquireData(&data);
printData(&Serial, &data);
}
}
//------------------------------------------------------------------------------
void setup(void) {
if (ERROR_LED_PIN >= 0) {
pinMode(ERROR_LED_PIN, OUTPUT);
}
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
Serial.print(F("\nFreeStack: "));
Serial.println(FreeStack());
Serial.print(F("Records/block: "));
Serial.println(DATA_DIM);
if (sizeof(block_t) != 512) {
error("Invalid block size");
}
// Allow userSetup access to SPI bus.
pinMode(SD_CS_PIN, OUTPUT);
digitalWrite(SD_CS_PIN, HIGH);
// Setup sensors.
userSetup();
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
sd.initErrorPrint(&Serial);
fatalBlink();
}
// recover existing tmp file.
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
while (!Serial.available()) {
yield();
}
if (Serial.read() == 'Y') {
recoverTmpFile();
} else {
error("'Y' not typed, please manually delete " TMP_FILE_NAME);
}
}
}
//------------------------------------------------------------------------------
void loop(void) {
// Read any Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
Serial.println();
Serial.println(F("type:"));
Serial.println(F("b - open existing bin file"));
Serial.println(F("c - convert file to csv"));
Serial.println(F("d - dump data to Serial"));
Serial.println(F("e - overrun error details"));
Serial.println(F("l - list files"));
Serial.println(F("r - record data"));
Serial.println(F("t - test without logging"));
while(!Serial.available()) {
yield();
}
#if WDT_YIELD_TIME_MICROS
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
while (true) {}
#endif
char c = tolower(Serial.read());
// Discard extra Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, LOW);
}
if (c == 'b') {
openBinFile();
} else if (c == 'c') {
binaryToCsv();
} else if (c == 'd') {
dumpData();
} else if (c == 'e') {
checkOverrun();
} else if (c == 'l') {
Serial.println(F("\nls:"));
sd.ls(&Serial, LS_SIZE);
} else if (c == 'r') {
logData();
} else if (c == 't') {
testSensor();
} else {
Serial.println(F("Invalid entry"));
}
}

View File

@@ -0,0 +1 @@
// Empty file with name LowLatencyLoggerADXL345.ino to make IDE happy.

View File

@@ -0,0 +1,70 @@
#include "UserTypes.h"
// User data functions. Modify these functions for your data items.
// Start time for data
static uint32_t startMicros;
const uint8_t ADXL345_CS = 9;
const uint8_t POWER_CTL = 0x2D; //Power Control Register
const uint8_t DATA_FORMAT = 0x31;
const uint8_t DATAX0 = 0x32; //X-Axis Data 0
const uint8_t DATAX1 = 0x33; //X-Axis Data 1
const uint8_t DATAY0 = 0x34; //Y-Axis Data 0
const uint8_t DATAY1 = 0x35; //Y-Axis Data 1
const uint8_t DATAZ0 = 0x36; //Z-Axis Data 0
const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1
void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) {
// Max SPI clock frequency is 5 MHz with CPOL = 1 and CPHA = 1.
SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3));
digitalWrite(ADXL345_CS, LOW);
SPI.transfer(registerAddress);
SPI.transfer(value);
digitalWrite(ADXL345_CS, HIGH);
SPI.endTransaction();
}
void userSetup() {
SPI.begin();
pinMode(ADXL345_CS, OUTPUT);
digitalWrite(ADXL345_CS, HIGH);
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
writeADXL345Register(DATA_FORMAT, 0x01);
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
writeADXL345Register(POWER_CTL, 0x08); //Measurement mode
}
// Acquire a data record.
void acquireData(data_t* data) {
// Max SPI clock frequency is 5 MHz with CPOL = 1 and CPHA = 1.
SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3));
data->time = micros();
digitalWrite(ADXL345_CS, LOW);
// Read multiple bytes so or 0XC0 with address.
SPI.transfer(DATAX0 | 0XC0);
data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8);
data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8);
data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8);
digitalWrite(ADXL345_CS, HIGH);
SPI.endTransaction();
}
// Print a data record.
void printData(Print* pr, data_t* data) {
if (startMicros == 0) {
startMicros = data->time;
}
pr->print(data->time - startMicros);
for (int i = 0; i < ACCEL_DIM; i++) {
pr->write(',');
pr->print(data->accel[i]);
}
pr->println();
}
// Print data header.
void printHeader(Print* pr) {
startMicros = 0;
pr->println(F("micros,ax,ay,az"));
}

View File

@@ -0,0 +1,17 @@
#ifndef UserTypes_h
#define UserTypes_h
#include "Arduino.h"
#include "SPI.h"
#define USE_SHARED_SPI 1
#define FILE_BASE_NAME "ADXL4G"
// User data types. Modify for your data items.
const uint8_t ACCEL_DIM = 3;
struct data_t {
uint32_t time;
int16_t accel[ACCEL_DIM];
};
void acquireData(data_t* data);
void printData(Print* pr, data_t* data);
void printHeader(Print* pr);
void userSetup();
#endif // UserTypes_h

View File

@@ -0,0 +1 @@
Test of shared SPI for LowLatencyLogger.

View File

@@ -0,0 +1,655 @@
/**
* This program logs data to a binary file. Functions are included
* to convert the binary file to a csv text file.
*
* Samples are logged at regular intervals. The maximum logging rate
* depends on the quality of your SD card and the time required to
* read sensor data. This example has been tested at 500 Hz with
* good SD card on an Uno. 4000 HZ is possible on a Due.
*
* If your SD card has a long write latency, it may be necessary to use
* slower sample rates. Using a Mega Arduino helps overcome latency
* problems since 12 512 byte buffers will be used.
*
* Data is written to the file using a SD multiple block write command.
*/
#include <SPI.h>
#include "SdFat.h"
#include "FreeStack.h"
#include "UserTypes.h"
#ifdef __AVR_ATmega328P__
#include "MinimumSerial.h"
MinimumSerial MinSerial;
#define Serial MinSerial
#endif // __AVR_ATmega328P__
//==============================================================================
// Start of configuration constants.
//==============================================================================
// Abort run on an overrun. Data before the overrun will be saved.
#define ABORT_ON_OVERRUN 1
//------------------------------------------------------------------------------
//Interval between data records in microseconds.
const uint32_t LOG_INTERVAL_USEC = 2000;
//------------------------------------------------------------------------------
// Set USE_SHARED_SPI non-zero for use of an SPI sensor.
// May not work for some cards.
#ifndef USE_SHARED_SPI
#define USE_SHARED_SPI 0
#endif // USE_SHARED_SPI
//------------------------------------------------------------------------------
// Pin definitions.
//
// SD chip select pin.
const uint8_t SD_CS_PIN = SS;
//
// Digital pin to indicate an error, set to -1 if not used.
// The led blinks for fatal errors. The led goes on solid for
// overrun errors and logging continues unless ABORT_ON_OVERRUN
// is non-zero.
#ifdef ERROR_LED_PIN
#undef ERROR_LED_PIN
#endif // ERROR_LED_PIN
const int8_t ERROR_LED_PIN = -1;
//------------------------------------------------------------------------------
// File definitions.
//
// Maximum file size in blocks.
// The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks.
// This file is flash erased using special SD commands. The file will be
// truncated if logging is stopped early.
const uint32_t FILE_BLOCK_COUNT = 256000;
//
// log file base name if not defined in UserTypes.h
#ifndef FILE_BASE_NAME
#define FILE_BASE_NAME "data"
#endif // FILE_BASE_NAME
//------------------------------------------------------------------------------
// Buffer definitions.
//
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional
// buffers.
//
#ifndef RAMEND
// Assume ARM. Use total of ten 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 10;
//
#elif RAMEND < 0X8FF
#error Too little SRAM
//
#elif RAMEND < 0X10FF
// Use total of two 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 2;
//
#elif RAMEND < 0X20FF
// Use total of four 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 4;
//
#else // RAMEND
// Use total of 12 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 12;
#endif // RAMEND
//==============================================================================
// End of configuration constants.
//==============================================================================
// Temporary log file. Will be deleted if a reset or power failure occurs.
#define TMP_FILE_NAME FILE_BASE_NAME "##.bin"
// Size of file base name.
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7;
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin";
SdFat sd;
SdBaseFile binFile;
// Number of data records in a block.
const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t);
//Compute fill so block size is 512 bytes. FILL_DIM may be zero.
const uint16_t FILL_DIM = 512 - 4 - DATA_DIM*sizeof(data_t);
struct block_t {
uint16_t count;
uint16_t overrun;
data_t data[DATA_DIM];
uint8_t fill[FILL_DIM];
};
//==============================================================================
// Error messages stored in flash.
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();}
//------------------------------------------------------------------------------
//
void fatalBlink() {
while (true) {
yield();
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
delay(200);
digitalWrite(ERROR_LED_PIN, LOW);
delay(200);
}
}
}
//------------------------------------------------------------------------------
// read data file and check for overruns
void checkOverrun() {
bool headerPrinted = false;
block_t block;
uint32_t bn = 0;
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
binFile.rewind();
Serial.println();
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
Serial.println(F("Checking overrun errors - type any character to stop"));
while (binFile.read(&block, 512) == 512) {
if (block.count == 0) {
break;
}
if (block.overrun) {
if (!headerPrinted) {
Serial.println();
Serial.println(F("Overruns:"));
Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount"));
headerPrinted = true;
}
Serial.print(bn);
Serial.print(',');
Serial.print(binFile.firstBlock() + bn);
Serial.print(',');
Serial.println(block.overrun);
}
bn++;
}
if (!headerPrinted) {
Serial.println(F("No errors found"));
} else {
Serial.println(F("Done"));
}
}
//-----------------------------------------------------------------------------
// Convert binary file to csv file.
void binaryToCsv() {
uint8_t lastPct = 0;
block_t block;
uint32_t t0 = millis();
uint32_t syncCluster = 0;
SdFile csvFile;
char csvName[FILE_NAME_DIM];
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
Serial.println();
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
// Create a new csvFile.
strcpy(csvName, binName);
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
error("open csvFile failed");
}
binFile.rewind();
Serial.print(F("Writing: "));
Serial.print(csvName);
Serial.println(F(" - type any character to stop"));
printHeader(&csvFile);
uint32_t tPct = millis();
while (!Serial.available() && binFile.read(&block, 512) == 512) {
uint16_t i;
if (block.count == 0 || block.count > DATA_DIM) {
break;
}
if (block.overrun) {
csvFile.print(F("OVERRUN,"));
csvFile.println(block.overrun);
}
for (i = 0; i < block.count; i++) {
printData(&csvFile, &block.data[i]);
}
if (csvFile.curCluster() != syncCluster) {
csvFile.sync();
syncCluster = csvFile.curCluster();
}
if ((millis() - tPct) > 1000) {
uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100);
if (pct != lastPct) {
tPct = millis();
lastPct = pct;
Serial.print(pct, DEC);
Serial.println('%');
}
}
if (Serial.available()) {
break;
}
}
csvFile.close();
Serial.print(F("Done: "));
Serial.print(0.001*(millis() - t0));
Serial.println(F(" Seconds"));
}
//-----------------------------------------------------------------------------
void createBinFile() {
// max number of blocks to erase per erase call
const uint32_t ERASE_SIZE = 262144L;
uint32_t bgnBlock, endBlock;
// Delete old tmp file.
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("Deleting tmp file " TMP_FILE_NAME));
if (!sd.remove(TMP_FILE_NAME)) {
error("Can't remove tmp file");
}
}
// Create new file.
Serial.println(F("\nCreating new file"));
binFile.close();
if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
error("createContiguous failed");
}
// Get the address of the file on the SD.
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
error("contiguousRange failed");
}
// Flash erase all data in the file.
Serial.println(F("Erasing all data"));
uint32_t bgnErase = bgnBlock;
uint32_t endErase;
while (bgnErase < endBlock) {
endErase = bgnErase + ERASE_SIZE;
if (endErase > endBlock) {
endErase = endBlock;
}
if (!sd.card()->erase(bgnErase, endErase)) {
error("erase failed");
}
bgnErase = endErase + 1;
}
}
//------------------------------------------------------------------------------
// dump data file to Serial
void dumpData() {
block_t block;
if (!binFile.isOpen()) {
Serial.println();
Serial.println(F("No current binary file"));
return;
}
binFile.rewind();
Serial.println();
Serial.println(F("Type any character to stop"));
delay(1000);
printHeader(&Serial);
while (!Serial.available() && binFile.read(&block , 512) == 512) {
if (block.count == 0) {
break;
}
if (block.overrun) {
Serial.print(F("OVERRUN,"));
Serial.println(block.overrun);
}
for (uint16_t i = 0; i < block.count; i++) {
printData(&Serial, &block.data[i]);
}
}
Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
// log data
void logData() {
createBinFile();
recordBinFile();
renameBinFile();
}
//------------------------------------------------------------------------------
void openBinFile() {
char name[FILE_NAME_DIM];
strcpy(name, binName);
Serial.println(F("\nEnter two digit version"));
Serial.write(name, BASE_NAME_SIZE);
for (int i = 0; i < 2; i++) {
while (!Serial.available()) {
yield();
}
char c = Serial.read();
Serial.write(c);
if (c < '0' || c > '9') {
Serial.println(F("\nInvalid digit"));
return;
}
name[BASE_NAME_SIZE + i] = c;
}
Serial.println(&name[BASE_NAME_SIZE+2]);
if (!sd.exists(name)) {
Serial.println(F("File does not exist"));
return;
}
binFile.close();
strcpy(binName, name);
if (!binFile.open(binName, O_RDONLY)) {
Serial.println(F("open failed"));
return;
}
Serial.println(F("File opened"));
}
//------------------------------------------------------------------------------
void recordBinFile() {
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1;
// Index of last queue location.
const uint8_t QUEUE_LAST = QUEUE_DIM - 1;
// Allocate extra buffer space.
block_t block[BUFFER_BLOCK_COUNT - 1];
block_t* curBlock = 0;
block_t* emptyStack[BUFFER_BLOCK_COUNT];
uint8_t emptyTop;
uint8_t minTop;
block_t* fullQueue[QUEUE_DIM];
uint8_t fullHead = 0;
uint8_t fullTail = 0;
// Use SdFat's internal buffer.
emptyStack[0] = (block_t*)sd.vol()->cacheClear();
if (emptyStack[0] == 0) {
error("cacheClear failed");
}
// Put rest of buffers on the empty stack.
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) {
emptyStack[i] = &block[i - 1];
}
emptyTop = BUFFER_BLOCK_COUNT;
minTop = BUFFER_BLOCK_COUNT;
// Start a multiple block write.
if (!sd.card()->writeStart(binFile.firstBlock())) {
error("writeStart failed");
}
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
Serial.println(F("Logging - type any character to stop"));
bool closeFile = false;
uint32_t bn = 0;
uint32_t maxLatency = 0;
uint32_t overrun = 0;
uint32_t overrunTotal = 0;
uint32_t logTime = micros();
while(1) {
// Time for next data record.
logTime += LOG_INTERVAL_USEC;
if (Serial.available()) {
closeFile = true;
}
if (closeFile) {
if (curBlock != 0) {
// Put buffer in full queue.
fullQueue[fullHead] = curBlock;
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
curBlock = 0;
}
} else {
if (curBlock == 0 && emptyTop != 0) {
curBlock = emptyStack[--emptyTop];
if (emptyTop < minTop) {
minTop = emptyTop;
}
curBlock->count = 0;
curBlock->overrun = overrun;
overrun = 0;
}
if ((int32_t)(logTime - micros()) < 0) {
error("Rate too fast");
}
int32_t delta;
do {
delta = micros() - logTime;
} while (delta < 0);
if (curBlock == 0) {
overrun++;
overrunTotal++;
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
}
#if ABORT_ON_OVERRUN
Serial.println(F("Overrun abort"));
break;
#endif // ABORT_ON_OVERRUN
} else {
#if USE_SHARED_SPI
sd.card()->spiStop();
#endif // USE_SHARED_SPI
acquireData(&curBlock->data[curBlock->count++]);
#if USE_SHARED_SPI
sd.card()->spiStart();
#endif // USE_SHARED_SPI
if (curBlock->count == DATA_DIM) {
fullQueue[fullHead] = curBlock;
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
curBlock = 0;
}
}
}
if (fullHead == fullTail) {
// Exit loop if done.
if (closeFile) {
break;
}
} else if (!sd.card()->isBusy()) {
// Get address of block to write.
block_t* pBlock = fullQueue[fullTail];
fullTail = fullTail < QUEUE_LAST ? fullTail + 1 : 0;
// Write block to SD.
uint32_t usec = micros();
if (!sd.card()->writeData((uint8_t*)pBlock)) {
error("write data failed");
}
usec = micros() - usec;
if (usec > maxLatency) {
maxLatency = usec;
}
// Move block to empty queue.
emptyStack[emptyTop++] = pBlock;
bn++;
if (bn == FILE_BLOCK_COUNT) {
// File full so stop
break;
}
}
}
if (!sd.card()->writeStop()) {
error("writeStop failed");
}
Serial.print(F("Min Free buffers: "));
Serial.println(minTop);
Serial.print(F("Max block write usec: "));
Serial.println(maxLatency);
Serial.print(F("Overruns: "));
Serial.println(overrunTotal);
// Truncate file if recording stopped early.
if (bn != FILE_BLOCK_COUNT) {
Serial.println(F("Truncating file"));
if (!binFile.truncate(512L * bn)) {
error("Can't truncate file");
}
}
}
//------------------------------------------------------------------------------
void recoverTmpFile() {
uint16_t count;
if (!binFile.open(TMP_FILE_NAME, O_RDWR)) {
return;
}
if (binFile.read(&count, 2) != 2 || count != DATA_DIM) {
error("Please delete existing " TMP_FILE_NAME);
}
Serial.println(F("\nRecovering data in tmp file " TMP_FILE_NAME));
uint32_t bgnBlock = 0;
uint32_t endBlock = binFile.fileSize()/512 - 1;
// find last used block.
while (bgnBlock < endBlock) {
uint32_t midBlock = (bgnBlock + endBlock + 1)/2;
binFile.seekSet(512*midBlock);
if (binFile.read(&count, 2) != 2) error("read");
if (count == 0 || count > DATA_DIM) {
endBlock = midBlock - 1;
} else {
bgnBlock = midBlock;
}
}
// truncate after last used block.
if (!binFile.truncate(512*(bgnBlock + 1))) {
error("Truncate " TMP_FILE_NAME " failed");
}
renameBinFile();
}
//-----------------------------------------------------------------------------
void renameBinFile() {
while (sd.exists(binName)) {
if (binName[BASE_NAME_SIZE + 1] != '9') {
binName[BASE_NAME_SIZE + 1]++;
} else {
binName[BASE_NAME_SIZE + 1] = '0';
if (binName[BASE_NAME_SIZE] == '9') {
error("Can't create file name");
}
binName[BASE_NAME_SIZE]++;
}
}
if (!binFile.rename(binName)) {
error("Can't rename file");
}
Serial.print(F("File renamed: "));
Serial.println(binName);
Serial.print(F("File size: "));
Serial.print(binFile.fileSize()/512);
Serial.println(F(" blocks"));
}
//------------------------------------------------------------------------------
void testSensor() {
const uint32_t interval = 200000;
int32_t diff;
data_t data;
Serial.println(F("\nTesting - type any character to stop\n"));
// Wait for Serial Idle.
delay(1000);
printHeader(&Serial);
uint32_t m = micros();
while (!Serial.available()) {
m += interval;
do {
diff = m - micros();
} while (diff > 0);
acquireData(&data);
printData(&Serial, &data);
}
}
//------------------------------------------------------------------------------
void setup(void) {
if (ERROR_LED_PIN >= 0) {
pinMode(ERROR_LED_PIN, OUTPUT);
}
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
Serial.print(F("\nFreeStack: "));
Serial.println(FreeStack());
Serial.print(F("Records/block: "));
Serial.println(DATA_DIM);
if (sizeof(block_t) != 512) {
error("Invalid block size");
}
// Allow userSetup access to SPI bus.
pinMode(SD_CS_PIN, OUTPUT);
digitalWrite(SD_CS_PIN, HIGH);
// Setup sensors.
userSetup();
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
sd.initErrorPrint(&Serial);
fatalBlink();
}
// recover existing tmp file.
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
while (!Serial.available()) {
yield();
}
if (Serial.read() == 'Y') {
recoverTmpFile();
} else {
error("'Y' not typed, please manually delete " TMP_FILE_NAME);
}
}
}
//------------------------------------------------------------------------------
void loop(void) {
// Read any Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
Serial.println();
Serial.println(F("type:"));
Serial.println(F("b - open existing bin file"));
Serial.println(F("c - convert file to csv"));
Serial.println(F("d - dump data to Serial"));
Serial.println(F("e - overrun error details"));
Serial.println(F("l - list files"));
Serial.println(F("r - record data"));
Serial.println(F("t - test without logging"));
while(!Serial.available()) {
yield();
}
#if WDT_YIELD_TIME_MICROS
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
while (true) {}
#endif
char c = tolower(Serial.read());
// Discard extra Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, LOW);
}
if (c == 'b') {
openBinFile();
} else if (c == 'c') {
binaryToCsv();
} else if (c == 'd') {
dumpData();
} else if (c == 'e') {
checkOverrun();
} else if (c == 'l') {
Serial.println(F("\nls:"));
sd.ls(&Serial, LS_SIZE);
} else if (c == 'r') {
logData();
} else if (c == 't') {
testSensor();
} else {
Serial.println(F("Invalid entry"));
}
}

View File

@@ -0,0 +1,2 @@
// Empty file with name LowLatencyLoggerMPU6050.ino to make IDE happy.

View File

@@ -0,0 +1,51 @@
// User data functions. Modify these functions for your data items.
#include "UserTypes.h"
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
//------------------------------------------------------------------------------
MPU6050 mpu;
static uint32_t startMicros;
// Acquire a data record.
void acquireData(data_t* data) {
data->time = micros();
mpu.getMotion6(&data->ax, &data->ay, &data->az,
&data->gx, &data->gy, &data->gz);
}
// setup AVR I2C
void userSetup() {
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000);
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
mpu.initialize();
}
// Print a data record.
void printData(Print* pr, data_t* data) {
if (startMicros == 0) {
startMicros = data->time;
}
pr->print(data->time- startMicros);
pr->write(',');
pr->print(data->ax);
pr->write(',');
pr->print(data->ay);
pr->write(',');
pr->print(data->az);
pr->write(',');
pr->print(data->gx);
pr->write(',');
pr->print(data->gy);
pr->write(',');
pr->println(data->gz);
}
// Print data header.
void printHeader(Print* pr) {
startMicros = 0;
pr->println(F("micros,ax,ay,az,gx,gy,gz"));
}

View File

@@ -0,0 +1,18 @@
#ifndef UserTypes_h
#define UserTypes_h
#include "Arduino.h"
#define FILE_BASE_NAME "mpuraw"
struct data_t {
unsigned long time;
int16_t ax;
int16_t ay;
int16_t az;
int16_t gx;
int16_t gy;
int16_t gz;
};
void acquireData(data_t* data);
void printData(Print* pr, data_t* data);
void printHeader(Print* pr);
void userSetup();
#endif // UserTypes_h

View File

@@ -0,0 +1,152 @@
/*
* This program is a simple Print benchmark.
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
#include "FreeStack.h"
// SD chip select pin
const uint8_t chipSelect = SS;
// number of lines to print
const uint16_t N_PRINT = 20000;
// file system
SdFat sd;
// test file
SdFile file;
// Serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
}
//------------------------------------------------------------------------------
void loop() {
uint32_t maxLatency;
uint32_t minLatency;
uint32_t totalLatency;
// Read any existing Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
// F stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
delay(400); // catch Due reset problem
cout << F("FreeStack: ") << FreeStack() << endl;
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;
cout << F("Starting print test. Please wait.\n\n");
// do write test
for (int test = 0; test < 6; test++) {
char fileName[13] = "bench0.txt";
fileName[5] = '0' + test;
// open or create file - truncate existing file.
if (!file.open(fileName, O_RDWR | O_CREAT | O_TRUNC)) {
error("open failed");
}
maxLatency = 0;
minLatency = 999999;
totalLatency = 0;
switch(test) {
case 0:
cout << F("Test of println(uint16_t)\n");
break;
case 1:
cout << F("Test of printField(uint16_t, char)\n");
break;
case 2:
cout << F("Test of println(uint32_t)\n");
break;
case 3:
cout << F("Test of printField(uint32_t, char)\n");
break;
case 4:
cout << F("Test of println(float)\n");
break;
case 5:
cout << F("Test of printField(float, char)\n");
break;
}
uint32_t t = millis();
for (uint16_t i = 0; i < N_PRINT; i++) {
uint32_t m = micros();
switch(test) {
case 0:
file.println(i);
break;
case 1:
file.printField(i, '\n');
break;
case 2:
file.println(12345678UL + i);
break;
case 3:
file.printField(12345678UL + i, '\n');
break;
case 4:
file.println((float)0.01*i);
break;
case 5:
file.printField((float)0.01*i, '\n');
break;
}
if (file.getWriteError()) {
error("write failed");
}
m = micros() - m;
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m;
}
file.close();
t = millis() - t;
double s = file.fileSize();
cout << F("Time ") << 0.001*t << F(" sec\n");
cout << F("File size ") << 0.001*s << F(" KB\n");
cout << F("Write ") << s/t << F(" KB/sec\n");
cout << F("Maximum latency: ") << maxLatency;
cout << F(" usec, Minimum Latency: ") << minLatency;
cout << F(" usec, Avg Latency: ");
cout << totalLatency/N_PRINT << F(" usec\n\n");
}
cout << F("Done!\n\n");
}

View File

@@ -0,0 +1,180 @@
/*
* This program illustrates raw write functions in SdFat that
* can be used for high speed data logging.
*
* This program simulates logging from a source that produces
* data at a constant rate of RATE_KB_PER_SEC.
*
* Note: Apps should create a very large file then truncates it
* to the length that is used for a logging. It only takes
* a few seconds to erase a 500 MB file since the card only
* marks the blocks as erased; no data transfer is required.
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
#include "FreeStack.h"
// SD chip select pin
const uint8_t chipSelect = SS;
const uint32_t RATE_KB_PER_SEC = 100;
const uint32_t TEST_TIME_SEC = 100;
// Time between printing progress dots
const uint32_t DOT_TIME_MS = 5000UL;
// number of blocks in the contiguous file
const uint32_t BLOCK_COUNT = (1000*RATE_KB_PER_SEC*TEST_TIME_SEC + 511)/512;
// file system
SdFat sd;
// test file
SdFile file;
// file extent
uint32_t bgnBlock, endBlock;
// Serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
void setup(void) {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
}
//------------------------------------------------------------------------------
void loop(void) {
// Read any extra Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
// F stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
cout << F("FreeStack: ") << FreeStack() << endl;
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// delete possible existing file
sd.remove("RawWrite.txt");
// create a contiguous file
if (!file.createContiguous("RawWrite.txt", 512UL*BLOCK_COUNT)) {
error("createContiguous failed");
}
// get the location of the file's blocks
if (!file.contiguousRange(&bgnBlock, &endBlock)) {
error("contiguousRange failed");
}
//*********************NOTE**************************************
// NO SdFile calls are allowed while cache is used for raw writes
//***************************************************************
// clear the cache and use it as a 512 byte buffer
uint8_t* pCache = (uint8_t*)sd.vol()->cacheClear();
// fill cache with eight lines of 64 bytes each
memset(pCache, ' ', 512);
for (uint16_t i = 0; i < 512; i += 64) {
// put line number at end of line then CR/LF
pCache[i + 61] = '0' + (i/64);
pCache[i + 62] = '\r';
pCache[i + 63] = '\n';
}
cout << F("Start raw write of ") << file.fileSize()/1000UL << F(" KB\n");
cout << F("Target rate: ") << RATE_KB_PER_SEC << F(" KB/sec\n");
cout << F("Target time: ") << TEST_TIME_SEC << F(" seconds\n");
// tell card to setup for multiple block write with pre-erase
if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) {
error("writeStart failed");
}
// init stats
delay(1000);
uint32_t dotCount = 0;
uint32_t maxQueuePrint = 0;
uint32_t maxWriteTime = 0;
uint32_t minWriteTime = 9999999;
uint32_t totalWriteTime = 0;
uint32_t maxQueueSize = 0;
uint32_t nWrite = 0;
uint32_t b = 0;
// write data
uint32_t startTime = millis();
while (nWrite < BLOCK_COUNT) {
uint32_t nProduced = RATE_KB_PER_SEC*(millis() - startTime)/512UL;
uint32_t queueSize = nProduced - nWrite;
if (queueSize == 0) continue;
if (queueSize > maxQueueSize) {
maxQueueSize = queueSize;
}
if ((millis() - startTime - dotCount*DOT_TIME_MS) > DOT_TIME_MS) {
if (maxQueueSize != maxQueuePrint) {
cout << F("\nQ: ") << maxQueueSize << endl;
maxQueuePrint = maxQueueSize;
} else {
cout << ".";
if (++dotCount%10 == 0) {
cout << endl;
}
}
}
// put block number at start of first line in block
uint32_t n = b++;
for (int8_t d = 5; d >= 0; d--) {
pCache[d] = n || d == 5 ? n % 10 + '0' : ' ';
n /= 10;
}
// write a 512 byte block
uint32_t tw = micros();
if (!sd.card()->writeData(pCache)) {
error("writeData failed");
}
tw = micros() - tw;
totalWriteTime += tw;
// check for max write time
if (tw > maxWriteTime) {
maxWriteTime = tw;
}
if (tw < minWriteTime) {
minWriteTime = tw;
}
nWrite++;
}
uint32_t endTime = millis();
uint32_t avgWriteTime = totalWriteTime/BLOCK_COUNT;
// end multiple block write mode
if (!sd.card()->writeStop()) {
error("writeStop failed");
}
cout << F("\nDone\n");
cout << F("maxQueueSize: ") << maxQueueSize << endl;
cout << F("Elapsed time: ") << setprecision(3)<< 1.e-3*(endTime - startTime);
cout << F(" seconds\n");
cout << F("Min block write time: ") << minWriteTime << F(" micros\n");
cout << F("Max block write time: ") << maxWriteTime << F(" micros\n");
cout << F("Avg block write time: ") << avgWriteTime << F(" micros\n");
// close file for next pass of loop
file.close();
Serial.println();
}

View File

@@ -0,0 +1,212 @@
// Functions to read a CSV text file one field at a time.
//
#include <limits.h>
#include <SPI.h>
// next line for SD.h
//#include <SD.h>
// next two lines for SdFat
#include <SdFat.h>
SdFat SD;
#define CS_PIN SS
// example can use comma or semicolon
#define CSV_DELIM ','
File file;
/*
* Read a file one field at a time.
*
* file - File to read.
*
* str - Character array for the field.
*
* size - Size of str array.
*
* delim - csv delimiter.
*
* return - negative value for failure.
* delimiter, '\n' or zero(EOF) for success.
*/
int csvReadText(File* file, char* str, size_t size, char delim) {
char ch;
int rtn;
size_t n = 0;
while (true) {
// check for EOF
if (!file->available()) {
rtn = 0;
break;
}
if (file->read(&ch, 1) != 1) {
// read error
rtn = -1;
break;
}
// Delete CR.
if (ch == '\r') {
continue;
}
if (ch == delim || ch == '\n') {
rtn = ch;
break;
}
if ((n + 1) >= size) {
// string too long
rtn = -2;
n--;
break;
}
str[n++] = ch;
}
str[n] = '\0';
return rtn;
}
//------------------------------------------------------------------------------
int csvReadInt32(File* file, int32_t* num, char delim) {
char buf[20];
char* ptr;
int rtn = csvReadText(file, buf, sizeof(buf), delim);
if (rtn < 0) return rtn;
*num = strtol(buf, &ptr, 10);
if (buf == ptr) return -3;
while(isspace(*ptr)) ptr++;
return *ptr == 0 ? rtn : -4;
}
//------------------------------------------------------------------------------
int csvReadInt16(File* file, int16_t* num, char delim) {
int32_t tmp;
int rtn = csvReadInt32(file, &tmp, delim);
if (rtn < 0) return rtn;
if (tmp < INT_MIN || tmp > INT_MAX) return -5;
*num = tmp;
return rtn;
}
//------------------------------------------------------------------------------
int csvReadUint32(File* file, uint32_t* num, char delim) {
char buf[20];
char* ptr;
int rtn = csvReadText(file, buf, sizeof(buf), delim);
if (rtn < 0) return rtn;
*num = strtoul(buf, &ptr, 10);
if (buf == ptr) return -3;
while(isspace(*ptr)) ptr++;
return *ptr == 0 ? rtn : -4;
}
//------------------------------------------------------------------------------
int csvReadUint16(File* file, uint16_t* num, char delim) {
uint32_t tmp;
int rtn = csvReadUint32(file, &tmp, delim);
if (rtn < 0) return rtn;
if (tmp > UINT_MAX) return -5;
*num = tmp;
return rtn;
}
//------------------------------------------------------------------------------
int csvReadDouble(File* file, double* num, char delim) {
char buf[20];
char* ptr;
int rtn = csvReadText(file, buf, sizeof(buf), delim);
if (rtn < 0) return rtn;
*num = strtod(buf, &ptr);
if (buf == ptr) return -3;
while(isspace(*ptr)) ptr++;
return *ptr == 0 ? rtn : -4;
}
//------------------------------------------------------------------------------
int csvReadFloat(File* file, float* num, char delim) {
double tmp;
int rtn = csvReadDouble(file, &tmp, delim);
if (rtn < 0)return rtn;
// could test for too large.
*num = tmp;
return rtn;
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
Serial.println("Type any character to start");
while (!Serial.available()) {
yield();
}
// Initialize the SD.
if (!SD.begin(CS_PIN)) {
Serial.println("begin failed");
return;
}
// Remove existing file.
SD.remove("READTEST.TXT");
// Create the file.
file = SD.open("READTEST.TXT", FILE_WRITE);
if (!file) {
Serial.println("open failed");
return;
}
// Write test data.
file.print(F(
#if CSV_DELIM == ','
"36,23.20,20.70,57.60,79.50,01:08:14,23.06.16\r\n"
"37,23.21,20.71,57.61,79.51,02:08:14,23.07.16\r\n"
#elif CSV_DELIM == ';'
"36;23.20;20.70;57.60;79.50;01:08:14;23.06.16\r\n"
"37;23.21;20.71;57.61;79.51;02:08:14;23.07.16\r\n"
#else
#error "Bad CSV_DELIM"
#endif
));
// Rewind the file for read.
file.seek(0);
// Read the file and print fields.
int16_t tcalc;
float t1, t2, h1, h2;
// Must be dim 9 to allow for zero byte.
char timeS[9], dateS[9];
while (file.available()) {
if (csvReadInt16(&file, &tcalc, CSV_DELIM) != CSV_DELIM
|| csvReadFloat(&file, &t1, CSV_DELIM) != CSV_DELIM
|| csvReadFloat(&file, &t2, CSV_DELIM) != CSV_DELIM
|| csvReadFloat(&file, &h1, CSV_DELIM) != CSV_DELIM
|| csvReadFloat(&file, &h2, CSV_DELIM) != CSV_DELIM
|| csvReadText(&file, timeS, sizeof(timeS), CSV_DELIM) != CSV_DELIM
|| csvReadText(&file, dateS, sizeof(dateS), CSV_DELIM) != '\n') {
Serial.println("read error");
int ch;
int nr = 0;
// print part of file after error.
while ((ch = file.read()) > 0 && nr++ < 100) {
Serial.write(ch);
}
break;
}
Serial.print(tcalc);
Serial.print(CSV_DELIM);
Serial.print(t1);
Serial.print(CSV_DELIM);
Serial.print(t2);
Serial.print(CSV_DELIM);
Serial.print(h1);
Serial.print(CSV_DELIM);
Serial.print(h2);
Serial.print(CSV_DELIM);
Serial.print(timeS);
Serial.print(CSV_DELIM);
Serial.println(dateS);
}
file.close();
}
//------------------------------------------------------------------------------
void loop() {
}

View File

@@ -0,0 +1,139 @@
// Read a two dimensional array from a CSV file.
//
#include <SPI.h>
#include <SdFat.h>
#define CS_PIN SS
// 5 X 4 array
#define ROW_DIM 5
#define COL_DIM 4
SdFat SD;
File file;
/*
* Read a file one field at a time.
*
* file - File to read.
*
* str - Character array for the field.
*
* size - Size of str array.
*
* delim - String containing field delimiters.
*
* return - length of field including terminating delimiter.
*
* Note, the last character of str will not be a delimiter if
* a read error occurs, the field is too long, or the file
* does not end with a delimiter. Consider this an error
* if not at end-of-file.
*
*/
size_t readField(File* file, char* str, size_t size, const char* delim) {
char ch;
size_t n = 0;
while ((n + 1) < size && file->read(&ch, 1) == 1) {
// Delete CR.
if (ch == '\r') {
continue;
}
str[n++] = ch;
if (strchr(delim, ch)) {
break;
}
}
str[n] = '\0';
return n;
}
//------------------------------------------------------------------------------
#define errorHalt(msg) {Serial.println(F(msg)); while (true) {}}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
Serial.println("Type any character to start");
while (!Serial.available()) {
yield();
}
// Initialize the SD.
if (!SD.begin(CS_PIN)) {
errorHalt("begin failed");
}
// Create or open the file.
file = SD.open("READNUM.TXT", FILE_WRITE);
if (!file) {
errorHalt("open failed");
}
// Rewind file so test data is not appended.
file.rewind();
// Write test data.
file.print(F(
"11,12,13,14\r\n"
"21,22,23,24\r\n"
"31,32,33,34\r\n"
"41,42,43,44\r\n"
"51,52,53,54" // Allow missing endl at eof.
));
// Rewind the file for read.
file.rewind();
// Array for data.
int array[ROW_DIM][COL_DIM];
int i = 0; // First array index.
int j = 0; // Second array index
size_t n; // Length of returned field with delimiter.
char str[20]; // Must hold longest field with delimiter and zero byte.
char *ptr; // Test for valid field.
// Read the file and store the data.
for (i = 0; i < ROW_DIM; i++) {
for (j = 0; j < COL_DIM; j++) {
n = readField(&file, str, sizeof(str), ",\n");
if (n == 0) {
errorHalt("Too few lines");
}
array[i][j] = strtol(str, &ptr, 10);
if (ptr == str) {
errorHalt("bad number");
}
while (*ptr == ' ') {
ptr++;
}
if (*ptr != ',' && *ptr != '\n' && *ptr != '\0') {
errorHalt("extra characters in field");
}
if (j < (COL_DIM-1) && str[n-1] != ',') {
errorHalt("line with too few fields");
}
}
// Allow missing endl at eof.
if (str[n-1] != '\n' && file.available()) {
errorHalt("missing endl");
}
}
// Print the array.
for (i = 0; i < ROW_DIM; i++) {
for (j = 0; j < COL_DIM; j++) {
if (j) {
Serial.print(' ');
}
Serial.print(array[i][j]);
}
Serial.println();
}
Serial.println("Done");
file.close();
}
//------------------------------------------------------------------------------
void loop() {
}

View File

@@ -0,0 +1,121 @@
/*
* This example reads a simple CSV, comma-separated values, file.
* Each line of the file has a label and three values, a long and two floats.
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD chip select pin
const uint8_t chipSelect = SS;
// file system object
SdFat sd;
// create Serial stream
ArduinoOutStream cout(Serial);
char fileName[] = "testfile.csv";
//------------------------------------------------------------------------------
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
// read and print CSV test file
void readFile() {
long lg = 0;
float f1, f2;
char text[10];
char c1, c2, c3; // space for commas.
// open input file
ifstream sdin(fileName);
// check for open error
if (!sdin.is_open()) {
error("open");
}
// read until input fails
while (1) {
// Get text field.
sdin.get(text, sizeof(text), ',');
// Assume EOF if fail.
if (sdin.fail()) {
break;
}
// Get commas and numbers.
sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2;
// Skip CR/LF.
sdin.skipWhite();
if (sdin.fail()) {
error("bad input");
}
// error in line if not commas
if (c1 != ',' || c2 != ',' || c3 != ',') {
error("comma");
}
// print in six character wide columns
cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl;
}
// Error in an input line if file is not at EOF.
if (!sdin.eof()) {
error("readFile");
}
}
//------------------------------------------------------------------------------
// write test file
void writeFile() {
// create or open and truncate output file
ofstream sdout(fileName);
// write file from string stored in flash
sdout << F(
"Line 1,1,2.3,4.5\n"
"Line 2,6,7.8,9.0\n"
"Line 3,9,8.7,6.5\n"
"Line 4,-4,-3.2,-1\n") << flush;
// check for any errors
if (!sdout) {
error("writeFile");
}
sdout.close();
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// create test file
writeFile();
cout << endl;
// read and print test
readFile();
cout << "\nDone!" << endl;
}
void loop() {}

View File

@@ -0,0 +1,81 @@
/*
SD card read/write
This example shows how to read and write data to and from an SD card file
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
created Nov 2010
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe
This example code is in the public domain.
*/
#include <SPI.h>
//#include <SD.h>
#include "SdFat.h"
SdFat SD;
#define SD_CS_PIN SS
File myFile;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
if (!SD.begin(SD_CS_PIN)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("test.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
Serial.print("Writing to test.txt...");
myFile.println("testing 1, 2, 3.");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
// re-open the file for reading:
myFile = SD.open("test.txt");
if (myFile) {
Serial.println("test.txt:");
// read from the file until there's nothing else in it:
while (myFile.available()) {
Serial.write(myFile.read());
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}
void loop() {
// nothing happens after setup
}

View File

@@ -0,0 +1,175 @@
/*
* Example use of two SPI ports on an STM32 board.
* Note SPI speed is limited to 18 MHz.
*/
#include <SPI.h>
#include "SdFat.h"
#include "FreeStack.h"
#error See new Version 2 STM32 example
// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes
// Use first SPI port
SdFat sd1;
// SdFatEX sd1;
const uint8_t SD1_CS = PA4; // chip select for sd1
// Use second SPI port
SPIClass SPI_2(2);
SdFat sd2(&SPI_2);
// SdFatEX sd2(&SPI_2);
const uint8_t SD2_CS = PB12; // chip select for sd2
const uint8_t BUF_DIM = 100;
uint8_t buf[BUF_DIM];
const uint32_t FILE_SIZE = 1000000;
const uint16_t NWRITE = FILE_SIZE/BUF_DIM;
//------------------------------------------------------------------------------
// print error msg, any SD error codes, and halt.
// store messages in flash
#define errorExit(msg) errorHalt(F(msg))
#define initError(msg) initErrorHalt(F(msg))
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
}
// fill buffer with known data
for (size_t i = 0; i < sizeof(buf); i++) {
buf[i] = i;
}
Serial.println(F("type any character to start"));
while (!Serial.available()) {
}
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
// initialize the first card
if (!sd1.begin(SD1_CS, SD_SCK_MHZ(18))) {
sd1.initError("sd1:");
}
// create Dir1 on sd1 if it does not exist
if (!sd1.exists("/Dir1")) {
if (!sd1.mkdir("/Dir1")) {
sd1.errorExit("sd1.mkdir");
}
}
// initialize the second card
if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) {
sd2.initError("sd2:");
}
// create Dir2 on sd2 if it does not exist
if (!sd2.exists("/Dir2")) {
if (!sd2.mkdir("/Dir2")) {
sd2.errorExit("sd2.mkdir");
}
}
// list root directory on both cards
Serial.println(F("------sd1 root-------"));
sd1.ls();
Serial.println(F("------sd2 root-------"));
sd2.ls();
// make /Dir1 the default directory for sd1
if (!sd1.chdir("/Dir1")) {
sd1.errorExit("sd1.chdir");
}
// remove test.bin from /Dir1 directory of sd1
if (sd1.exists("test.bin")) {
if (!sd1.remove("test.bin")) {
sd2.errorExit("remove test.bin");
}
}
// make /Dir2 the default directory for sd2
if (!sd2.chdir("/Dir2")) {
sd2.errorExit("sd2.chdir");
}
// remove rename.bin from /Dir2 directory of sd2
if (sd2.exists("rename.bin")) {
if (!sd2.remove("rename.bin")) {
sd2.errorExit("remove rename.bin");
}
}
// list current directory on both cards
Serial.println(F("------sd1 Dir1-------"));
sd1.ls();
Serial.println(F("------sd2 Dir2-------"));
sd2.ls();
Serial.println(F("---------------------"));
// set the current working directory for open() to sd1
sd1.chvol();
// create or open /Dir1/test.bin and truncate it to zero length
SdFile file1;
if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) {
sd1.errorExit("file1");
}
Serial.println(F("Writing test.bin to sd1"));
// write data to /Dir1/test.bin on sd1
for (uint16_t i = 0; i < NWRITE; i++) {
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
sd1.errorExit("sd1.write");
}
}
// set the current working directory for open() to sd2
sd2.chvol();
// create or open /Dir2/copy.bin and truncate it to zero length
SdFile file2;
if (!file2.open("copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) {
sd2.errorExit("file2");
}
Serial.println(F("Copying test.bin to copy.bin"));
// copy file1 to file2
file1.rewind();
uint32_t t = millis();
while (1) {
int n = file1.read(buf, sizeof(buf));
if (n < 0) {
sd1.errorExit("read1");
}
if (n == 0) {
break;
}
if ((int)file2.write(buf, n) != n) {
sd2.errorExit("write2");
}
}
t = millis() - t;
Serial.print(F("File size: "));
Serial.println(file2.fileSize());
Serial.print(F("Copy time: "));
Serial.print(t);
Serial.println(F(" millis"));
// close test.bin
file1.close();
file2.close();
// list current directory on both cards
Serial.println(F("------sd1 -------"));
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("------sd2 -------"));
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("---------------------"));
Serial.println(F("Renaming copy.bin"));
// rename the copy
if (!sd2.rename("copy.bin", "rename.bin")) {
sd2.errorExit("sd2.rename");
}
// list current directory on both cards
Serial.println(F("------sd1 -------"));
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("------sd2 -------"));
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("---------------------"));
Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,215 @@
// Benchmark comparing SdFile and StdioStream.
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// Define PRINT_FIELD nonzero to use printField.
#define PRINT_FIELD 0
// Number of lines to list on Serial.
#define STDIO_LIST_COUNT 0
#define VERIFY_CONTENT 0
const uint8_t SD_CS_PIN = SS;
SdFat sd;
SdFile printFile;
StdioStream stdioFile;
float f[100];
char buf[20];
const char* label[] =
{ "uint8_t 0 to 255, 100 times ", "uint16_t 0 to 20000",
"uint32_t 0 to 20000", "uint32_t 1000000000 to 1000010000",
"float nnn.ffff, 10000 times"
};
//------------------------------------------------------------------------------
void setup() {
uint32_t printSize = 0;
uint32_t stdioSize = 0;
uint32_t printTime = 0;
uint32_t stdioTime = 0;
Serial.begin(9600);
while (!Serial) {
yield();
}
Serial.println(F("Type any character to start"));
while (!Serial.available()) {
yield();
}
Serial.println(F("Starting test"));
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
sd.errorHalt();
}
for (uint8_t i = 0; i < 100; i++) {
f[i] = 123.0 + 0.1234*i;
}
for (uint8_t dataType = 0; dataType < 5; dataType++) {
for (uint8_t fileType = 0; fileType < 2; fileType++) {
if (!fileType) {
if (!printFile.open("print.txt", O_RDWR | O_CREAT | O_TRUNC)) {
Serial.println(F("open fail"));
return;
}
printTime = millis();
switch (dataType) {
case 0:
for (uint16_t i =0; i < 100; i++) {
for (uint8_t j = 0; j < 255; j++) {
printFile.println(j);
}
}
break;
case 1:
for (uint16_t i = 0; i < 20000; i++) {
printFile.println(i);
}
break;
case 2:
for (uint32_t i = 0; i < 20000; i++) {
printFile.println(i);
}
break;
case 3:
for (uint16_t i = 0; i < 10000; i++) {
printFile.println(i + 1000000000UL);
}
break;
case 4:
for (int j = 0; j < 100; j++) {
for (uint8_t i = 0; i < 100; i++) {
printFile.println(f[i], 4);
}
}
break;
default:
break;
}
printFile.sync();
printTime = millis() - printTime;
printFile.rewind();
printSize = printFile.fileSize();
} else {
if (!stdioFile.fopen("stream.txt", "w+")) {
Serial.println(F("fopen fail"));
return;
}
stdioTime = millis();
switch (dataType) {
case 0:
for (uint16_t i =0; i < 100; i++) {
for (uint8_t j = 0; j < 255; j++) {
#if PRINT_FIELD
stdioFile.printField(j, '\n');
#else // PRINT_FIELD
stdioFile.println(j);
#endif // PRINT_FIELD
}
}
break;
case 1:
for (uint16_t i = 0; i < 20000; i++) {
#if PRINT_FIELD
stdioFile.printField(i, '\n');
#else // PRINT_FIELD
stdioFile.println(i);
#endif // PRINT_FIELD
}
break;
case 2:
for (uint32_t i = 0; i < 20000; i++) {
#if PRINT_FIELD
stdioFile.printField(i, '\n');
#else // PRINT_FIELD
stdioFile.println(i);
#endif // PRINT_FIELD
}
break;
case 3:
for (uint16_t i = 0; i < 10000; i++) {
uint32_t n = i + 1000000000UL;
#if PRINT_FIELD
stdioFile.printField(n, '\n');
#else // PRINT_FIELD
stdioFile.println(n);
#endif // PRINT_FIELD
}
break;
case 4:
for (int j = 0; j < 100; j++) {
for (uint8_t i = 0; i < 100; i++) {
#if PRINT_FIELD
stdioFile.printField(f[i], '\n', 4);
#else // PRINT_FIELD
stdioFile.println(f[i], 4);
#endif // PRINT_FIELD
}
}
break;
default:
break;
}
stdioFile.fflush();
stdioTime = millis() - stdioTime;
stdioSize = stdioFile.ftell();
if (STDIO_LIST_COUNT) {
size_t len;
stdioFile.rewind();
for (int i = 0; i < STDIO_LIST_COUNT; i++) {
stdioFile.fgets(buf, sizeof(buf), &len);
Serial.print(len);
Serial.print(',');
Serial.print(buf);
}
}
}
}
Serial.println(label[dataType]);
if (VERIFY_CONTENT && printSize == stdioSize) {
printFile.rewind();
stdioFile.rewind();
for (uint32_t i = 0; i < stdioSize; i++) {
if (printFile.read() != stdioFile.getc()) {
Serial.print(F("Files differ at pos: "));
Serial.println(i);
return;
}
}
}
Serial.print(F("fileSize: "));
if (printSize != stdioSize) {
Serial.print(printSize);
Serial.print(F(" != "));
}
Serial.println(stdioSize);
Serial.print(F("print millis: "));
Serial.println(printTime);
Serial.print(F("stdio millis: "));
Serial.println(stdioTime);
Serial.print(F("ratio: "));
Serial.println((float)printTime/(float)stdioTime);
Serial.println();
printFile.close();
stdioFile.fclose();
}
Serial.println(F("Done"));
}
void loop() {}

View File

@@ -0,0 +1,162 @@
/*
* This program tests the dateTimeCallback() function
* and the timestamp() function.
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
SdFat sd;
SdFile file;
// Default SD chip select is SS pin
const uint8_t chipSelect = SS;
// create Serial stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
/*
* date/time values for debug
* normally supplied by a real-time clock or GPS
*/
// date 1-Oct-14
uint16_t year = 2014;
uint8_t month = 10;
uint8_t day = 1;
// time 20:30:40
uint8_t hour = 20;
uint8_t minute = 30;
uint8_t second = 40;
//------------------------------------------------------------------------------
/*
* User provided date time callback function.
* See SdFile::dateTimeCallback() for usage.
*/
void dateTime(uint16_t* date, uint16_t* time) {
// User gets date and time from GPS or real-time
// clock in real callback function
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(year, month, day);
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(hour, minute, second);
}
//------------------------------------------------------------------------------
/*
* Function to print all timestamps.
*/
void printTimestamps(SdFile& f) {
cout << F("Creation: ");
f.printCreateDateTime(&Serial);
cout << endl << F("Modify: ");
f.printModifyDateTime(&Serial);
cout << endl << F("Access: ");
f.printAccessDateTime(&Serial);
cout << endl;
}
//------------------------------------------------------------------------------
void setup(void) {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// remove files if they exist
sd.remove("callback.txt");
sd.remove("default.txt");
sd.remove("stamp.txt");
// create a new file with default timestamps
if (!file.open("default.txt", O_WRONLY | O_CREAT)) {
error("open default.txt failed");
}
cout << F("\nOpen with default times\n");
printTimestamps(file);
// close file
file.close();
/*
* Test the date time callback function.
*
* dateTimeCallback() sets the function
* that is called when a file is created
* or when a file's directory entry is
* modified by sync().
*
* The callback can be disabled by the call
* SdFile::dateTimeCallbackCancel()
*/
// set date time callback function
SdFile::dateTimeCallback(dateTime);
// create a new file with callback timestamps
if (!file.open("callback.txt", O_WRONLY | O_CREAT)) {
error("open callback.txt failed");
}
cout << ("\nOpen with callback times\n");
printTimestamps(file);
// change call back date
day += 1;
// must add two to see change since FAT second field is 5-bits
second += 2;
// modify file by writing a byte
file.write('t');
// force dir update
file.sync();
cout << F("\nTimes after write\n");
printTimestamps(file);
// close file
file.close();
/*
* Test timestamp() function
*
* Cancel callback so sync will not
* change access/modify timestamp
*/
SdFile::dateTimeCallbackCancel();
// create a new file with default timestamps
if (!file.open("stamp.txt", O_WRONLY | O_CREAT)) {
error("open stamp.txt failed");
}
// set creation date time
if (!file.timestamp(T_CREATE, 2014, 11, 10, 1, 2, 3)) {
error("set create time failed");
}
// set write/modification date time
if (!file.timestamp(T_WRITE, 2014, 11, 11, 4, 5, 6)) {
error("set write time failed");
}
// set access date
if (!file.timestamp(T_ACCESS, 2014, 11, 12, 7, 8, 9)) {
error("set access time failed");
}
cout << F("\nTimes after timestamp() calls\n");
printTimestamps(file);
file.close();
cout << F("\nDone\n");
}
void loop() {}

View File

@@ -0,0 +1,170 @@
/*
* Warning This example requires extra RAM and may crash on Uno.
* Example use of two SD cards.
*/
#include <SPI.h>
#include "SdFat.h"
#include "FreeStack.h"
SdFat sd1;
const uint8_t SD1_CS = 10; // chip select for sd1
SdFat sd2;
const uint8_t SD2_CS = 4; // chip select for sd2
const uint8_t BUF_DIM = 100;
uint8_t buf[BUF_DIM];
const uint32_t FILE_SIZE = 1000000;
const uint32_t NWRITE = FILE_SIZE/BUF_DIM;
//------------------------------------------------------------------------------
// print error msg, any SD error codes, and halt.
// store messages in flash
#define errorExit(msg) errorHalt(F(msg))
#define initError(msg) initErrorHalt(F(msg))
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
Serial.print(F("FreeStack: "));
Serial.println(FreeStack());
// fill buffer with known data
for (size_t i = 0; i < sizeof(buf); i++) {
buf[i] = i;
}
Serial.println(F("type any character to start"));
while (!Serial.available()) {
yield();
}
// disable sd2 while initializing sd1
pinMode(SD2_CS, OUTPUT);
digitalWrite(SD2_CS, HIGH);
// initialize the first card
if (!sd1.begin(SD1_CS)) {
sd1.initError("sd1:");
}
// create Dir1 on sd1 if it does not exist
if (!sd1.exists("/Dir1")) {
if (!sd1.mkdir("/Dir1")) {
sd1.errorExit("sd1.mkdir");
}
}
// initialize the second card
if (!sd2.begin(SD2_CS)) {
sd2.initError("sd2:");
}
// create Dir2 on sd2 if it does not exist
if (!sd2.exists("/Dir2")) {
if (!sd2.mkdir("/Dir2")) {
sd2.errorExit("sd2.mkdir");
}
}
// list root directory on both cards
Serial.println(F("------sd1 root-------"));
sd1.ls();
Serial.println(F("------sd2 root-------"));
sd2.ls();
// make /Dir1 the default directory for sd1
if (!sd1.chdir("/Dir1")) {
sd1.errorExit("sd1.chdir");
}
// make /Dir2 the default directory for sd2
if (!sd2.chdir("/Dir2")) {
sd2.errorExit("sd2.chdir");
}
// list current directory on both cards
Serial.println(F("------sd1 Dir1-------"));
sd1.ls();
Serial.println(F("------sd2 Dir2-------"));
sd2.ls();
Serial.println(F("---------------------"));
// remove rename.bin from /Dir2 directory of sd2
if (sd2.exists("rename.bin")) {
if (!sd2.remove("rename.bin")) {
sd2.errorExit("remove rename.bin");
}
}
// set the current working directory for open() to sd1
sd1.chvol();
// create or open /Dir1/test.bin and truncate it to zero length
SdFile file1;
if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) {
sd1.errorExit("file1");
}
Serial.println(F("Writing test.bin to sd1"));
// write data to /Dir1/test.bin on sd1
for (uint32_t i = 0; i < NWRITE; i++) {
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
sd1.errorExit("sd1.write");
}
}
// set the current working directory for open() to sd2
sd2.chvol();
// create or open /Dir2/copy.bin and truncate it to zero length
SdFile file2;
if (!file2.open("copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) {
sd2.errorExit("file2");
}
Serial.println(F("Copying test.bin to copy.bin"));
// copy file1 to file2
file1.rewind();
uint32_t t = millis();
while (1) {
int n = file1.read(buf, sizeof(buf));
if (n < 0) {
sd1.errorExit("read1");
}
if (n == 0) {
break;
}
if ((int)file2.write(buf, n) != n) {
sd2.errorExit("write2");
}
}
t = millis() - t;
Serial.print(F("File size: "));
Serial.println(file2.fileSize());
Serial.print(F("Copy time: "));
Serial.print(t);
Serial.println(F(" millis"));
// close test.bin
file1.close();
file2.close();
// list current directory on both cards
Serial.println(F("------sd1 -------"));
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("------sd2 -------"));
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("---------------------"));
Serial.println(F("Renaming copy.bin"));
// rename the copy
if (!sd2.rename("copy.bin", "rename.bin")) {
sd2.errorExit("sd2.rename");
}
// list current directory on both cards
Serial.println(F("------sd1 -------"));
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("------sd2 -------"));
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
Serial.println(F("---------------------"));
Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,82 @@
/*
* This program demonstrates the freeClusterCount() call.
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
/*
* SD chip select pin. Common values are:
*
* Arduino Ethernet shield, pin 4.
* SparkFun SD shield, pin 8.
* Adafruit Datalogging shield, pin 10.
* Default SD chip select is the SPI SS pin.
*/
const uint8_t chipSelect = SS;
#define TEST_FILE "Cluster.test"
// file system
SdFat sd;
// test file
SdFile file;
// Serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
void printFreeSpace() {
cout << F("freeClusterCount() call time: ");
uint32_t m = micros();
uint32_t volFree = sd.vol()->freeClusterCount();
cout << micros() - m << F(" micros\n");
cout << F("freeClusters: ") << volFree << setprecision(3) << endl;
float fs = 0.000512*volFree*sd.vol()->blocksPerCluster();
cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n\n");
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
if (!MAINTAIN_FREE_CLUSTER_COUNT) {
cout << F("Please edit SdFatConfig.h and set\n");
cout << F("MAINTAIN_FREE_CLUSTER_COUNT nonzero for\n");
cout << F("maximum freeClusterCount() performance.\n\n");
}
// F stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// Insure no TEST_FILE.
sd.remove(TEST_FILE);
cout << F("\nFirst call to freeClusterCount scans the FAT.\n\n");
printFreeSpace();
cout << F("Create and write to ") << TEST_FILE << endl;
if (!file.open(TEST_FILE, O_WRONLY | O_CREAT)) {
sd.errorHalt(F("Create failed"));
}
file.print(F("Cause a cluster to be allocated"));
file.close();
cout << F("\nSecond freeClusterCount call is faster if\n");
cout << F("MAINTAIN_FREE_CLUSTER_COUNT is nonzero.\n\n");
printFreeSpace();
cout << F("Remove ") << TEST_FILE << endl << endl;
sd.remove(TEST_FILE);
printFreeSpace();
cout << F("Done") << endl;
}
//------------------------------------------------------------------------------
void loop() {}

View File

@@ -0,0 +1,150 @@
/*
* Simple data logger.
*/
#include <SPI.h>
#include "SdFat.h"
// SD chip select pin. Be sure to disable any other SPI devices such as Enet.
const uint8_t chipSelect = SS;
// Interval between data records in milliseconds.
// The interval must be greater than the maximum SD write latency plus the
// time to acquire and write data to the SD to avoid overrun errors.
// Run the bench example to check the quality of your SD card.
const uint32_t SAMPLE_INTERVAL_MS = 1000;
// Log file base name. Must be six characters or less.
#define FILE_BASE_NAME "Data"
//------------------------------------------------------------------------------
// File system object.
SdFat sd;
// Log file.
SdFile file;
// Time in micros for next data record.
uint32_t logTime;
//==============================================================================
// User functions. Edit writeHeader() and logData() for your requirements.
const uint8_t ANALOG_COUNT = 4;
//------------------------------------------------------------------------------
// Write data header.
void writeHeader() {
file.print(F("micros"));
for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
file.print(F(",adc"));
file.print(i, DEC);
}
file.println();
}
//------------------------------------------------------------------------------
// Log a data record.
void logData() {
uint16_t data[ANALOG_COUNT];
// Read all channels to avoid SD write latency between readings.
for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
data[i] = analogRead(i);
}
// Write data to file. Start with log time in micros.
file.print(logTime);
// Write ADC data to CSV record.
for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
file.write(',');
file.print(data[i]);
}
file.println();
}
//==============================================================================
// Error messages stored in flash.
#define error(msg) sd.errorHalt(F(msg))
//------------------------------------------------------------------------------
void setup() {
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
char fileName[13] = FILE_BASE_NAME "00.csv";
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
delay(1000);
Serial.println(F("Type any character to start"));
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// Find an unused file name.
if (BASE_NAME_SIZE > 6) {
error("FILE_BASE_NAME too long");
}
while (sd.exists(fileName)) {
if (fileName[BASE_NAME_SIZE + 1] != '9') {
fileName[BASE_NAME_SIZE + 1]++;
} else if (fileName[BASE_NAME_SIZE] != '9') {
fileName[BASE_NAME_SIZE + 1] = '0';
fileName[BASE_NAME_SIZE]++;
} else {
error("Can't create file name");
}
}
if (!file.open(fileName, O_WRONLY | O_CREAT | O_EXCL)) {
error("file.open");
}
// Read any Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
Serial.print(F("Logging to: "));
Serial.println(fileName);
Serial.println(F("Type any character to stop"));
// Write data header.
writeHeader();
// Start on a multiple of the sample interval.
logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1;
logTime *= 1000UL*SAMPLE_INTERVAL_MS;
}
//------------------------------------------------------------------------------
void loop() {
// Time for next record.
logTime += 1000UL*SAMPLE_INTERVAL_MS;
// Wait for log time.
int32_t diff;
do {
diff = micros() - logTime;
} while (diff < 0);
// Check for data rate too high.
if (diff > 10) {
error("Missed data record");
}
logData();
// Force data to SD and update the directory entry to avoid data loss.
if (!file.sync() || file.getWriteError()) {
error("write error");
}
if (Serial.available()) {
// Close file and stop.
file.close();
Serial.println(F("Done"));
while (true) {}
}
}

View File

@@ -0,0 +1,88 @@
// Demo of fgets function to read lines from a file.
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD chip select pin
const uint8_t chipSelect = SS;
SdFat sd;
// print stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// store error strings in flash memory
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
void demoFgets() {
char line[25];
int n;
// open test file
SdFile rdfile("fgets.txt", O_RDONLY);
// check for open error
if (!rdfile.isOpen()) {
error("demoFgets");
}
cout << endl << F(
"Lines with '>' end with a '\\n' character\n"
"Lines with '#' do not end with a '\\n' character\n"
"\n");
// read lines from the file
while ((n = rdfile.fgets(line, sizeof(line))) > 0) {
if (line[n - 1] == '\n') {
cout << '>' << line;
} else {
cout << '#' << line << endl;
}
}
}
//------------------------------------------------------------------------------
void makeTestFile() {
// create or open test file
SdFile wrfile("fgets.txt", O_WRONLY | O_CREAT | O_TRUNC);
// check for open error
if (!wrfile.isOpen()) {
error("MakeTestFile");
}
// write test file
wrfile.print(F(
"Line with CRLF\r\n"
"Line with only LF\n"
"Long line that will require an extra read\n"
"\n" // empty line
"Line at EOF without NL"
));
wrfile.close();
}
//------------------------------------------------------------------------------
void setup(void) {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
delay(400); // catch Due reset problem
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
makeTestFile();
demoFgets();
cout << F("\nDone\n");
}
void loop(void) {}

View File

@@ -0,0 +1,73 @@
/*
* Print a table with various formatting options
* Format dates
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// create Serial stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// print a table to demonstrate format manipulators
void example(void) {
const int max = 10;
const int width = 4;
for (int row = 1; row <= max; row++) {
for (int col = 1; col <= max; col++) {
cout << setw(width) << row * col << (col == max ? '\n' : ' ');
}
}
cout << endl;
}
//------------------------------------------------------------------------------
// print a date as mm/dd/yyyy with zero fill in mm and dd
// shows how to set and restore the fill character
void showDate(int m, int d, int y) {
// convert two digit year
if (y < 100) {
y += 2000;
}
// set new fill to '0' save old fill character
char old = cout.fill('0');
// print date
cout << setw(2) << m << '/' << setw(2) << d << '/' << y << endl;
// restore old fill character
cout.fill(old);
}
//------------------------------------------------------------------------------
void setup(void) {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
delay(2000);
cout << endl << "default formatting" << endl;
example();
cout << showpos << "showpos" << endl;
example();
cout << hex << left << showbase << "hex left showbase" << endl;
example();
cout << internal << setfill('0') << uppercase;
cout << "uppercase hex internal showbase fill('0')" <<endl;
example();
// restore default format flags and fill character
cout.flags(ios::dec | ios::right | ios::skipws);
cout.fill(' ');
cout << "showDate example" <<endl;
showDate(7, 4, 11);
showDate(12, 25, 11);
}
void loop(void) {}

View File

@@ -0,0 +1,84 @@
/*
* Example of getline from section 27.7.1.3 of the C++ standard
* Demonstrates the behavior of getline for various exceptions.
* See http://www.cplusplus.com/reference/iostream/istream/getline/
*
* Note: This example is meant to demonstrate subtleties the standard and
* may not the best way to read a file.
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD chip select pin
const uint8_t chipSelect = SS;
// file system object
SdFat sd;
// create a serial stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
void makeTestFile() {
ofstream sdout("getline.txt");
// use flash for text to save RAM
sdout << F(
"short line\n"
"\n"
"17 character line\n"
"too long for buffer\n"
"line with no nl");
sdout.close();
}
//------------------------------------------------------------------------------
void testGetline() {
const int line_buffer_size = 18;
char buffer[line_buffer_size];
ifstream sdin("getline.txt");
int line_number = 0;
while (sdin.getline(buffer, line_buffer_size, '\n') || sdin.gcount()) {
int count = sdin.gcount();
if (sdin.fail()) {
cout << "Partial long line";
sdin.clear(sdin.rdstate() & ~ios_base::failbit);
} else if (sdin.eof()) {
cout << "Partial final line"; // sdin.fail() is false
} else {
count--; // Dont include newline in count
cout << "Line " << ++line_number;
}
cout << " (" << count << " chars): " << buffer << endl;
}
}
//------------------------------------------------------------------------------
void setup(void) {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
// F stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// make the test file
makeTestFile();
// run the example
testGetline();
cout << "\nDone!\n";
}
//------------------------------------------------------------------------------
void loop(void) {}

View File

@@ -0,0 +1,43 @@
// Example to wipe all data from an already formatted SD.
#error wipe is not supported in SdFat V2. Use bool format(print_t* pr = nullptr).
#include <SPI.h>
#include "SdFat.h"
const int chipSelect = SS;
SdFat sd;
void setup() {
int c;
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
Serial.println("Type 'Y' to wipe all data.");
while (!Serial.available()) {
yield();
}
c = Serial.read();
if (c != 'Y') {
sd.errorHalt("Quitting, you did not type 'Y'.");
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// Use wipe() for no dot progress indicator.
if (!sd.wipe(&Serial)) {
sd.errorHalt("Wipe failed.");
}
// Must reinitialize after wipe.
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.errorHalt("Second init failed.");
}
Serial.println("Done");
}
void loop() {
}