SD card read/write with Arduino

Hi Charlie,

Thanks for clarifying that for me, I think I get it now.

I think I can probably work with the 512 byte write each time... My program is monitoring a temperature reading and the voltages of a battery and a solar panel, in an enclosure housing a camera to take a few photos each day, producing 10 bytes of data per minute. I could probably drop this down to 8 bytes if writing 10 bytes/min causes problems with the 512 byte sector size, but I'd prefer not to.

Here's my code (over 2 posts):

#include <Wire.h>
#include <math.h>
#include <DateTime.h>
#include <avr/pgmspace.h>
#include <wprogram.h>
#include <microfat2.h>
#include <mmc.h>

#define ThermistorPIN 0            // input read pin for LM335 is Analog Pin 0
float temperature = 0;                 // variable which will be calculated in process
long val=0;                       // variable to store the value coming from the thermistor
float temp;

unsigned long sector = 0;
unsigned long byteSize;

unsigned long WriteAddress = 0;    // initial address for writing to the SD card.
byte value;                        // will be used...
int incomingByte = 0;                 // ...for incoming serial data
byte tempBytes[10];                // for sending data to the SD card
byte sector_buffer[512];           // for sending AND retrieving data to/from the SD card

int hour;
int minute;
int second;
int month;
int day_of_week;
int day;
int year;

char* dow[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

float Thermistor(int RawADC) {  //  Inputs ADC Value from Thermistor and outputs Temperature in Celsius
 long Resistance;
 float Temp;                                      // Dual-Purpose variable to save space.
 Resistance=((10240000/RawADC) - 10000);          // Assuming a 10k Thermistor.  Calculation is actually: Resistance = (1024/ADC)
 Temp = log(Resistance);                          // Saving the Log(resistance) so not to calculate it 4 times later. "Temp" means "Temporary" on this line.
 Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));   // Now it means both "Temporary" and "Temperature"
 Temp = Temp - 273.15;  // Convert Kelvin to Celsius                                            Now it only means "Temperature"
 return Temp;           // Return the Temperature
}

void print_prompt_P(const char *data) {         // print prompts and messages from program flash to save SRAM
   while (pgm_read_byte(data) != 0x00)
     Serial.print(pgm_read_byte(data++));
}

void TakePhoto() {
  digitalWrite(3,HIGH); // first, switch on the fans to blow away dust or bugs...
  delay(10000);
  digitalWrite(3,LOW);
  delay(2000);
  digitalWrite(9,HIGH); // then send power to the camera
  delay(500);           // time for current to get to camera - not sure if this is needed...
  digitalWrite(6,HIGH); // trigger the solenoid for 100 msec
  delay(100);
  digitalWrite(6,LOW);
  delay(15000);         // wait 15 seconds for the camera to take a photo then shutdown
  digitalWrite(9,LOW);  // stop sending power to the camera.
}

void DataDump () {
  int DaysToDump = WriteAddress;
  Serial.println("");
  print_prompt_P(PSTR("          Data dump initiated: dumping "));
  Serial.print(DaysToDump/10);
  print_prompt_P(PSTR(" days of data.\n"));
    print_prompt_P(PSTR("Date,Time,Temp,VBatt,VPanel\n"));
      for(int p=0;p<DaysToDump;p=p+10) {
        sector_buffer[p]=digitalRead(13);
        Serial.print(sector_buffer[p]);

        Serial.print(analogRead(0));  // this bit dumps the date in 'dd/mm/yy' format...
          print_prompt_P(PSTR("/"));
//          ++f;
          Serial.print(analogRead(0));
          print_prompt_P(PSTR("/"));
          delay(50);
          Serial.print(analogRead(0));
          print_prompt_P(PSTR(","));
          Serial.print(analogRead(0));  // this bit prints the time, as ',HH:MM,'...
          print_prompt_P(PSTR(":"));
//          ++f;
          Serial.print(analogRead(0));
          print_prompt_P(PSTR(","));
//          ++f;
          delay(50);
          Serial.print(analogRead(0));  // this bit prints the temperature...
          print_prompt_P(PSTR(","));
//          ++f;
          delay(50);
          Serial.print(analogRead(0));  // this bit prints the Battery Voltage in 'VV.vv,' format...
          print_prompt_P(PSTR("."));
//          ++f;
          Serial.print(analogRead(0));
          print_prompt_P(PSTR(","));
//          ++f;
          delay(50);
          Serial.print(analogRead(0));  // this bit prints the Solar Panel Voltage in 'VV.vv,' format...
          print_prompt_P(PSTR("."));
//          ++f;
          Serial.print(analogRead(0));
//          ++f;
          delay(50);
        Serial.println();
      }
  WriteAddress = 0;
}

void error(const char* s)
{
  print_prompt_P(PSTR("Error: "));
  print_prompt_P(s);
  print_prompt_P(PSTR("<press reset>\n"));
  for( /* ever */ ; ; ) 
  {
    digitalWrite(13, (millis() / 250) & 1);
  }
}

void SDWrite() {
  // Pass in the sector-sized buffer we'll be using ourselves later.
  // uFat doesn't own it, it just needs to use it temporarily.
  // We also pass in the address of a function that is used to read
  // sectors from our device.
  //  
  if (!microfat2::initialize(sector_buffer, &mmc::readSectors))
  {
  error(PSTR("uFat init failed.\n"));
  }
  prog_char string_0[] PROGMEM = "DATA    BIN";

  if (microfat2::locateFileStart(PSTR("DATA    BIN"), sector, byteSize)) // check if the target file exists
  {
    if (byteSize >= 512)                                                 // check it's bigger than 512 bytes
    {
      if (RES_OK == mmc::readSectors(sector_buffer, sector, 1))          // checks a single-SECTOR read works OK
      {
          print_prompt_P(PSTR("Data read test SUCCESSFUL.\n"));
          delay(500);
        if (RES_OK == mmc::writeSectors(sector_buffer, sector, 1))       // checks a single-SECTOR write works OK
        {
          print_prompt_P(PSTR("Data write test SUCCESSFUL.\n"));
          delay(500);
        }
        else
        {
          print_prompt_P(PSTR("Failed to write updated data."));         // if the write fails, end here...?
        }
      }
      else
      {
        print_prompt_P(PSTR("Failed to read data.bin."));
      }
    }
    else
    {
      error(PSTR("Found data.bin, but it's too small."));
    }
  }
  else
  {
    print_prompt_P(PSTR("data.bin not present on card."));
  }
  Serial.println("Initialisation COMPLETE!!");
}

void setup() {

 pinMode(3,OUTPUT);       // FANS control - YELLOW wire
 digitalWrite(3,LOW);
 pinMode(6,OUTPUT);       // SOLENOID power control - WHITE wire
 digitalWrite(6,LOW);
 pinMode(9,OUTPUT);       // CAMERA power control - BLUE wire
 digitalWrite(9,LOW);
// pinMode(13,OUTPUT);
 Serial.begin(115200);
 Wire.begin();

// The rest of this setup() procedure is checking that the SD card is present & working.

  while(mmc::initialize() != RES_OK)
  {
    print_prompt_P(PSTR("SD init failed. retrying...\n"));
    delay(100);
  }
  SDWrite;
}

void loop() {

 // Below required to reset the register address to 0.
 Wire.beginTransmission(104); // transmit to device #104, the ds 1307
 Wire.send(0x00);
 Wire.endTransmission();      // stop transmitting

 Wire.requestFrom(104, 7);    // request 7 bytes from slave ds1307, we'll assume it'll send them all even though it doesn't have to
 second = Wire.receive(); 
 minute = Wire.receive(); 
 hour = Wire.receive(); 
 day_of_week=Wire.receive(); 
 day = Wire.receive(); 
 month = Wire.receive(); 
 year = Wire.receive(); 

 // Convert all the BCD values that might have "tens" to decimal.
 second = second/16 * 10 + second % 16;
 minute = minute/16 * 10 + minute % 16;
 hour = hour/16 * 10 + hour % 16;
 day = day/16 * 10 + day % 16;
 month = month/16 * 10 + month % 16;
 year = 2000 + year/16 * 10 + year % 16;
 
// Change times below to desired photo times. Copy & paste to add more photos per day.
// NOTE: for some reason, 8's & 9's cause an error, so don't use them on their own below;
// 18 & 19 work fine, but 08 & 09 do not.

 if (hour == 6) { if (minute == 00) { if (second == 00) { TakePhoto();}}}
 if (hour == 12) { if (minute == 00) { if (second == 00) { TakePhoto();}}}
 if (hour == 14) { if (minute == 00) { if (second == 00) { TakePhoto();}}}
 if (hour == 18) { if (minute == 00) { if (second == 00) { TakePhoto();}}}

 Serial.print(hour);
 print_prompt_P(PSTR(":"));
 if (minute < 10) { print_prompt_P(PSTR("0")); }
 Serial.print(minute);
 print_prompt_P(PSTR(":"));
 if (second < 10) { print_prompt_P(PSTR("0")); }
 Serial.print(second);
 print_prompt_P(PSTR(" on "));
 Serial.print(dow[day_of_week]);
 print_prompt_P(PSTR(", "));
 Serial.print(day);
 print_prompt_P(PSTR("/"));
 Serial.print(month);
 print_prompt_P(PSTR("/"));
 Serial.print(year);
 Serial.println();
  byte span = 20;  int aRead = 0;
  for (byte i = 0; i < span; i++) {        //loop to get average of 20 readings
    aRead = aRead + analogRead(ThermistorPIN);
    }
  aRead = aRead / span;
  temperature = Thermistor(aRead);

...continued next post...