First Commit
This commit is contained in:
@@ -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) {}
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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() {}
|
||||
@@ -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
|
||||
@@ -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"));
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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() {}
|
||||
@@ -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() {}
|
||||
77
libraries/SdFat/examples/examplesV1/#attic/append/append.ino
Normal file
77
libraries/SdFat/examples/examplesV1/#attic/append/append.ino
Normal 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() {}
|
||||
@@ -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() {}
|
||||
149
libraries/SdFat/examples/examplesV1/#attic/benchSD/benchSD.ino
Normal file
149
libraries/SdFat/examples/examplesV1/#attic/benchSD/benchSD.ino
Normal 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();
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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() {}
|
||||
@@ -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() {}
|
||||
34
libraries/SdFat/examples/examplesV1/#attic/readme.txt
Normal file
34
libraries/SdFat/examples/examplesV1/#attic/readme.txt
Normal 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.
|
||||
@@ -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
|
||||
@@ -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__
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
LFN,NAME.TXT is not 8.3 since it has a comma.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,2 @@
|
||||
Not_8_3.txt has a Long File Name
|
||||
since the basename is mixed case.
|
||||
@@ -0,0 +1 @@
|
||||
OK%83.TXT is a valid 8.3 name.
|
||||
@@ -0,0 +1 @@
|
||||
STD_8_3.TXT - a vanilla 8.3 name.
|
||||
@@ -0,0 +1,2 @@
|
||||
With Blank.txt
|
||||
Just another example of a Long File Name.
|
||||
@@ -0,0 +1,2 @@
|
||||
"With Two.dots.txt"
|
||||
Lots of reasons this is a Long File Name.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// Empty file with name LowLatencyLoggerADXL345.ino to make IDE happy.
|
||||
@@ -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"));
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
Test of shared SPI for LowLatencyLogger.
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Empty file with name LowLatencyLoggerMPU6050.ino to make IDE happy.
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
180
libraries/SdFat/examples/examplesV1/RawWrite/RawWrite.ino
Normal file
180
libraries/SdFat/examples/examplesV1/RawWrite/RawWrite.ino
Normal 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();
|
||||
}
|
||||
212
libraries/SdFat/examples/examplesV1/ReadCsv/ReadCsv.ino
Normal file
212
libraries/SdFat/examples/examplesV1/ReadCsv/ReadCsv.ino
Normal 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() {
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
81
libraries/SdFat/examples/examplesV1/ReadWrite/ReadWrite.ino
Normal file
81
libraries/SdFat/examples/examplesV1/ReadWrite/ReadWrite.ino
Normal 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
|
||||
}
|
||||
|
||||
|
||||
175
libraries/SdFat/examples/examplesV1/STM32Test/STM32Test.ino
Normal file
175
libraries/SdFat/examples/examplesV1/STM32Test/STM32Test.ino
Normal 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() {}
|
||||
215
libraries/SdFat/examples/examplesV1/StdioBench/StdioBench.ino
Normal file
215
libraries/SdFat/examples/examplesV1/StdioBench/StdioBench.ino
Normal 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() {}
|
||||
162
libraries/SdFat/examples/examplesV1/Timestamp/Timestamp.ino
Normal file
162
libraries/SdFat/examples/examplesV1/Timestamp/Timestamp.ino
Normal 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() {}
|
||||
170
libraries/SdFat/examples/examplesV1/TwoCards/TwoCards.ino
Normal file
170
libraries/SdFat/examples/examplesV1/TwoCards/TwoCards.ino
Normal 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() {}
|
||||
@@ -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() {}
|
||||
150
libraries/SdFat/examples/examplesV1/dataLogger/dataLogger.ino
Normal file
150
libraries/SdFat/examples/examplesV1/dataLogger/dataLogger.ino
Normal 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) {}
|
||||
}
|
||||
}
|
||||
88
libraries/SdFat/examples/examplesV1/fgets/fgets.ino
Normal file
88
libraries/SdFat/examples/examplesV1/fgets/fgets.ino
Normal 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) {}
|
||||
@@ -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) {}
|
||||
84
libraries/SdFat/examples/examplesV1/getline/getline.ino
Normal file
84
libraries/SdFat/examples/examplesV1/getline/getline.ino
Normal 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--; // Don’t 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) {}
|
||||
43
libraries/SdFat/examples/examplesV1/wipe/wipe.ino
Normal file
43
libraries/SdFat/examples/examplesV1/wipe/wipe.ino
Normal 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() {
|
||||
}
|
||||
Reference in New Issue
Block a user