SD card read/write with Arduino

Hi,
as soon as I try and run this example I receive the following error:
My Documents\arduino-0018\libraries\SDcard\arduino sd card example.cpp:161: error: 'DEC' was not declared in this scope

This makes no sense to me as "DEC" seems to be used in the correct way throughout the example. Can anyone help?

Many thanks!

NOTE: I'm using Arduino_0018 and the "libraries" folder is not within the "hardware" folder, does this matter?

Hi to all,

This is N.Nandhakumar again. After a lot of struggle I have purchased the EM-406a GPS module, Adafruit GPS shield V1.1, Freeduino ATMega328 for my project.
I soldered the GPS shield , interfaced it with the Arduino and the GPS logged data successfully into the SD card.
Now, I have a problem. I want to read back the logged data from the log file stored in SD card for doing some processing, comparison with the data. I made use of SdFat library's SdFatRead example code and I was able to read back the file contents .

/*
 * This sketch reads and prints the file
 * PRINT00.TXT created by SdFatPrint.pde or
 * WRITE00.TXT created by SdFatWrite.pde
 */
#include <SdFat.h>
#include <SdFatUtil.h>

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
void error_P(const char *str)
{
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}
void setup(void)
{
  Serial.begin(9600);
  Serial.println();
  Serial.println("type any character to start");
  while (!Serial.available());
  Serial.println();
  
  // initialize the SD card
  if (!card.init()) error("card.init");
  
  // initialize a FAT volume
  if (!volume.init(card)) error("volume.init");
  
  // open the root directory
  if (!root.openRoot(volume)) error("openRoot");
  
  // open a file
  if (file.open(root, "PRINT00.TXT", O_READ)) {
    Serial.println("Opened PRINT00.TXT");
  }
  else if (file.open(root, "WRITE00.TXT", O_READ)) {
    Serial.println("Opened WRITE00.TXT");    
  }
  else{
    error("file.open");
  }
  Serial.println();
  
  // copy file to serial port
  int16_t n;
  uint8_t buf[7];// nothing special about 7, just a lucky number.
  while ((n = file.read(buf, sizeof(buf))) > 0) {
    for (uint8_t i = 0; i < n; i++) Serial.print(buf[i]);
  }
  /* easier way
  int16_t c;
  while ((c = file.read()) > 0) Serial.print((char)c);
  */
  Serial.println("\nDone");
}

void loop(void) {}

The problem is that the file contents are read in raw format using uint8_t [ I googled about this datatype . But I am not clear with this datatype and how it works :frowning: ] . It will be more useful If I could read the data from the file in char or string format .

I searched for a better way to convert the uint8_t data to char datatype. But I get only junk characters.

I need the forum's help to solve this issue. Please give your suggestions on this.
Please help me out. Waiting for your valuable suggestions.

My works on the project at : http://walkingwithtux.wordpress.com/2010/05/02/tran-duino-under-progress/

Regards

N.Nandhakumar :slight_smile:

Hi,

Do you think you could post here what is the content of the file you are reading and what you are seeing in the serial window? I'm not sure if I can help you out, but I think that information would be helpful.

Good luck!

Hi cemath,

Thanks for the reply. THe code which I edited in the SdFatRead Example is

  int16_t n;
  char msg;
  uint8_t buf[7];// nothing special about 7, just a lucky number.
  while ((n = file.read(buf, sizeof(buf))) > 0)
  {
    for(uint8_t i=0;i < n;i++)
       msg = (char)buf[i];
       Serial.print(msg);
  }
  Serial.println("\nDone");

I am trying to read the file contents , convert them to char type from uint8_t type.

The file which I am trying to read from SD card is the GPSLOG00.TXT . the file contains GPS sentences logged into a text file from GPS.
The contents are

$GPRMC,112209.290,A,1307.3214,N,08013.3322,E,,,030510,,*0D
$GPRMC,112210.290,A,1307.3089,N,08013.3367,E,,,030510,,*02
$GPRMC,112211.290,A,1307.3299,N,08013.3306,E,,,030510,,*07
$GPRMC,112212.290,A,1307.3512,N,08013.3207,E,,,030510,,*00
$GPRMC,112213.290,A,1307.3630,N,08013.3141,E,,,030510,,*03
$GPRMC,112214.290,A,1307.3468,N,08013.3262,E,,,030510,,*09
$GPRMC,112215.290,A,1307.3531,N,08013.3234,E,,,030510,,*06
$GPRMC,112216.290,A,1307.3595,N,08013.3207,E,,,030510,,*0B
$GPRMC,112217.290,A,1307.3534,N,08013.3249,E,,,030510,,*0B
$GPRMC,112218.290,A,1307.3502,N,08013.3273,E,,,030510,,*08
$GPRMC,112219.290,A,1307.3489,N,08013.3282,E,,,030510,,*05
$GPRMC,112220.290,A,1307.3705,N,08013.3196,E,,,030510,,*0E
$GPRMC,112221.290,A,1307.3383,N,08013.3295,E,,,030510,,*05
$GPRMC,112222.290,A,1307.3405,N,08013.3251,E,,,030510,,*07
$GPRMC,112223.290,A,1307.3420,N,08013.3292,E,,,030510,,*0E
$GPRMC,112224.290,A,1307.3526,N,08013.3229,E,,,030510,,*0E
$GPRMC,112225.290,A,1307.3553,N,08013.3228,E,,,030510,,*0C
$GPRMC,112226.290,A,1307.3535,N,08013.3259,E,,,030510,,*09
$GPRMC,112227.290,A,1307.3417,N,08013.3269,E,1.42,312.24,030510,,*0B
$GPRMC,112228.000,A,1307.3425,N,08013.3270,E,0.33,308.80,030510,,*04
$GPRMC,112229.000,A,1307.3422,N,08013.3278,E,0.04,240.51,030510,,*0F
$GPRMC,112230.000,A,1307.3417,N,08013.3283,E,0.23,173.43,030510,,*00
$GPRMC,112231.000,A,1307.3416,N,08013.3285,E,0.34,164.87,030510,,*0E
$GPRMC,112232.000,A,1307.3413,N,08013.3285,E,0.53,166.36,030510,,*01
$GPRMC,112233.000,A,1307.3410,N,08013.3286,E,0.63,159.12,030510,,*09
$GPRMC,112234.000,A,1307.3406,N,08013.3288,E,0.72,157.94,030510,,*07
$GPRMC,112235.000,A,1307.3404,N,08013.3290,E,0.78,157.79,030510,,*04
$GPRMC,112236.000,A,1307.3400,N,08013.3295,E,1.01,158.20,030510,,*0A
$GPRMC,112237.000,A,1307.3394,N,08013.3298,E,0.58,151.13,030510,,*08
$GPRMC,112238.000,A,1307.3391,N,08013.3302,E,0.23,124.27,030510,,*09
$GPRMC,112239.000,A,1307.3392,N,08013.3305,E,0.23,144.30,030510,,*0C
$GPRMC,112240.000,A,1307.3393,N,08013.3306,E,0.19,0.60,030510,,*0D
$GPRMC,112241.000,A,1307.3391,N,08013.3308,E,0.55,344.99,030510,,*0D
$GPRMC,112242.000,A,1307.3393,N,08013.3309,E,0.65,346.12,030510,,*0F
$GPRMC,112243.000,A,1307.3391,N,08013.3311,E,0.58,327.75,030510,,*0D
$GPRMC,112244.000,A,1307.3392,N,08013.3312,E,0.31,315.15,030510,,*02
$GPRMC,112245.000,A,1307.3390,N,08013.3311,E,0.43,334.94,030510,,*0D
$GPRMC,112246.000,A,1307.3393,N,08013.3305,E,0.45,322.00,030510,,*04
$GPRMC,112247.000,A,1307.3396,N,08013.3301,E,0.76,330.60,030510,,*01
$GPRMC,112248.000,A,1307.3397,N,08013.3297,E,0.27,314.78,030510,,*0A
$GPRMC,112249.000,A,1307.3400,N,08013.3295,E,0.36,20.55,030510,,*3B
$GPRMC,112250.000,A,1307.3400,N,08013.3290,E,0.29,263.99,030510,,*0D
$GPRMC,112251.000,A,1307.3397,N,08013.3288,E,0.48,229.59,030510,,*09
$GPRMC,112252.000,A,1307.3398,N,08013.3284,E,0.41,316.03,030510,,*02
$GPRMC,112253.000,A,1307.3398,N,08013.3280,E,0.80,313.98,030510,,*0D
$GPRMC,112254.000,A,1307.3401,N,08013.3277,E,1.11,318.17,030510,,*00
$GPRMC,112255.000,A,1307.3403,N,08013.3274,E,1.02,322.50,030510,,*08
$GPRMC,112256.000,A,1307.3406,N,08013.3268,E,0.51,310.56,030510,,*03
$GPRMC,112257.000,A,1307.3405,N,08013.3265,E,0.20,172.19,030510,,*07
$GPRMC,112258.000,A,1307.3405,N,08013.3265,E,0.25,131.33,030510,,*02
$GPRMC,112259.000,A,1307.3405,N,08013.3263,E,0.50,286.05,030510,,*0D
$GPRMC,112300.000,A,1307.3405,N,08013.3263,E,0.43,318.18,030510,,*08
$GPRMC,112301.000,A,1307.3406,N,08013.3262,E,0.14,166.60,030510,,*0D
$GPRMC,112302.000,A,1307.3403,N,08013.3263,E,0.23,93.57,030510,,*31
$GPRMC,112303.000,A,1307.3400,N,08013.3267,E,0.58,150.75,030510,,*05
$GPRMC,112304.000,A,1307.3396,N,08013.3268,E,0.73,157.31,030510,,*0B
$GPRMC,112305.000,A,1307.3395,N,08013.3270,E,0.23,144.14,030510,,*00
$GPRMC,112306.000,A,1307.3395,N,08013.3272,E,0.21,139.18,030510,,*05
$GPRMC,112307.000,A,1307.3393,N,08013.3272,E,0.28,356.23,030510,,*08
$GPRMC,112308.000,A,1307.3391,N,08013.3272,E,0.12,3.89,030510,,*0F
$GPRMC,112309.000,A,1307.3390,N,08013.3273,E,0.15,342.02,030510,,*0C
$GPRMC,112310.000,A,1307.3388,N,08013.3272,E,0.03,14.17,030510,,*3F
$GPRMC,112311.000,A,1307.3388,N,08013.3272,E,0.51,352.24,030510,,*08
$GPRMC,112312.000,A,1307.3389,N,08013.3272,E,0.43,339.14,030510,,*07
$GPRMC,112313.000,A,1307.3389,N,08013.3272,E,0.11,290.20,030510,,*04
$GPRMC,112314.000,A,1307.3388,N,08013.3270,E,0.21,235.81,030510,,*07
$GPRMC,112315.000,A,1307.3388,N,08013.3270,E,0.17,241.13,030510,,*0B
$GPRMC,112316.000,A,1307.3386,N,08013.3272,E,0.18,122.90,030510,,*06
$GPRMC,112317.000,A,1307.3385,N,08013.3275,E,0.25,29.10,030510,,*3F
$GPRMC,112318.000,A,1307.3385,N,08013.3277,E,0.07,201.14,030510,,*0E
$GPRMC,112319.000,A,1307.3385,N,08013.3278,E,0.48,0.37,030510,,*09
$GPRMC,112320.000,A,1307.3385,N,08013.3281,E,0.40,346.95,030510,,*04
$GPRMC,112321.000,A,1307.3387,N,08013.3281,E,0.78,340.65,030510,,*05
$GPRMC,112322.000,A,1307.3388,N,08013.3282,E,0.23,328.18,030510,,*00
$GPRMC,112323.000,A,1307.3388,N,08013.3283,E,0.79,341.04,030510,,*0D
$GPRMC,112324.000,A,1307.3389,N,08013.3282,E,0.48,321.94,030510,,*07
$GPRMC,112325.000,A,1307.3390,N,08013.3280,E,0.11,297.44,030510,,*01
$GPRMC,112326.000,A,1307.3390,N,08013.3275,E,0.27,211.88,030510,,*03
$GPRMC,112327.000,A,1307.3389,N,08013.3270,E,0.43,291.88,030510,,*05
$GPRMC,112328.000,A,1307.3388,N,08013.3266,E,0.57,203.58,030510,,*0F
$GPRMC,112329.000,A,1307.3386,N,08013.3265,E,0.12,265.01,030510,,*0E
$GPRMC,112330.000,A,1307.3389,N,08013.3261,E,0.10,192.25,030510,,*02
$GPRMC,112331.000,A,1307.3391,N,08013.3259,E,0.37,338.75,030510,,*03
$GPRMC,112332.000,A,1307.3392,N,08013.3257,E,0.36,345.45,030510,,*05
$GPRMC,112333.000,A,1307.3392,N,08013.3256,E,0.19,286.73,030510,,*03
$GPRMC,112334.000,A,1307.3395,N,08013.3254,E,0.52,327.34,030510,,*07
$GPRMC,112335.000,A,1307.3395,N,08013.3253,E,0.21,248.92,030510,,*01
$GPRMC,112336.000,A,1307.3395,N,08013.3252,E,0.21,301.84,030510,,*08
$GPRMC,112337.000,A,1307.3395,N,08013.3252,E,0.09,264.22,030510,,*0D
$GPRMC,112338.000,A,1307.3396,N,08013.3252,E,0.16,346.95,030510,,*02
$GPRMC,112339.000,A,1307.3397,N,08013.3252,E,0.11,277.62,030510,,*0E
$GPRMC,112340.000,A,1307.3396,N,08013.3252,E,0.31,346.83,030510,,*0F
$GPRMC,112341.000,A,1307.3394,N,08013.3253,E,0.23,330.10,030510,,*05
$GPRMC,112342.000,A,1307.3394,N,08013.3254,E,0.20,327.85,030510,,*08
$GPRMC,112343.000,A,1307.3392,N,08013.3255,E,0.12,193.25,030510,,*08
$GPRMC,112344.000,A,1307.3391,N,08013.3256,E,0.29,347.05,030510,,*0E
$GPRMC,112345.000,A,1307.3389,N,08013.3257,E,0.40,340.46,030510,,*08
$GPRMC,112346.000,A,1307.3388,N,08013.3258,E,0.20,327.57,030510,,*02
$GPRMC,112347.000,A,1307.3388,N,08013.3258,E,0.35,339.64,030510,,*08
$GPRMC,112348.000,A,1307.3389,N,08013.3259,E,0.13,322.44,030510,,*0B
$GPRMC,112349.000,A,1307.3389,N,08013.3260,E,0.08,272.78,030510,,*01
$GPRMC,112350.000,A,1307.3388,N,08013.3260,E,0.12,204.95,030510,,*01
$GPRMC,112351.000,A,1307.3387,N,08013.3261,E,0.05,209.98,030510,,*08

When I uploaded the code , The serial monitor contents are,

Opened GPSLOG00.TXT



,.11023*R2,.,3,0$12391,00M1A302,,G190,3E53C4,4860,P207N.,1
,.19073*R2,.,3,0$12321,00M1A302,,G190,3E5EC1,3890,P207N.,1
,.12023*R2,.,3,0$12331,00M2A302,,G190,3E30,P207N.,03*R2,.,30000M3A302..50C1,488381
,.1105360$10301,,,,G100,3E10,P207N.,53*R2,.,31800M3A302..58C8,380221
,.1905300$10331,,3*R2,.,30400M4A303..5FC3,381571
,.1902150$10301,,,,G100,3E30,P207N.,33*R2,.,30400M4A302.51
,.1000990$10371,,,,G100,3E30,P207N.,13*R2,.,31800M5A302..58C6,486551
,.1005090$10351,,,,G100,3E20,P207N.,13*R3,.,30600M0A302.51
,.1007850$10361,,,,G100,3E10,P207N.,33*R3,.,30600M0A302.90$10301,,,,G100,3E13*R3,.,30200M1A302..57C3,387121
,.1800110$10381,,,,G100,3E10,P207N.,903M1A302..5EC9,3874,,G100,3E30,P207N.,43*R3,.,30800M2A302..5DC4,388491
,.1900140$10301,,,,G100,3E20,P207N.,03*R3,.,30500M3A302..52C1,385371
,.1907650$10321,,,,G100,3E30,P207N.,43*R3,.,30100M3A302..5DC8,385191
,.1902120$10361,,,,G100,3E30,P207N.,23*R3,.,30300M4A302..5EC5,385441
,.1808070$10381,,,,G100,3E30,P207N.,73*R3,.,30400M5A302..58C2,386241
,.1807940$10311,,0,P207N.,.5FC6,387180$10381,,3*R3,.,30.5FC9,388160$10371,,,,G100,3E10,P207N.,63*R4,.,30700M0A302..5BC5,389131
,.1603360$10311,,,,G100,3E10,P207N.,903M1A303..58C1,380401
,.1604860$10301,,0,P207N.,53*R4,.,30.52C6,380430$10331,,0,P207N.,73*R4,.,30.5FC0,380121
,.1601750$10311,,,,G100,3E10,P207N.,33*R4,.,30800M2A303.71
,.15093,,G100,3E10,

Done

The contents in the serial monitor are of different format [ I think its of HEX type]
and the full contents are also not fully converted and displayed in the serial monitor. Please help me out and tell me where I made wrong in the code. I also need to convert the full contents into char, since the log file is very huge.

Thanks in advance
N.Nandhakumar

Hi Nnk,

I found a pattern while comparing the data in your file and the data that is displayed in the monitor window. If you break the file data in blocks of 7 characters (the last caracter of a line, break line, counts as a caracter), and you keep only the last caracter of each block, then you can reconstruct what you are seeing in the monitor window. The problem must be in the example code you use to display the caracters, but I do not see it. I suspect that by reading a single byte at a time (using uint8_t buf[1] instead of uint8_t buf[7]) it should correct the problem, but that doesnt not explain it.

I suggest that you debug the code step by step by displaying the value of 'n' and other variables to verify that the program is doing what it should.

Also, I think that going throught the 'char' intermediate step is not necessary, because the formats 'uint8_t' and 'char' are very similar. I think uint8_t is equivalent to the format 'byte', which is 8 bits unsigned, and char is 8 bits signed. But since the caracters in ASCII are encoded on a range from 0 to 127, signed or unsigned bytes make no difference.

I hope that you find the solution to your problem quickly!

Good luck,

cemath

Hi guys

i am in a serious situation, i can't graduate if i don't get done with the datalogging...
so please, help , i very appreciate it
here is the case:
i am having a project that display a speed and record it
the former part is done, however, the later part is very difficult to me
i am trying to save the speed into the SDcard, don't need to read or copy, just write.
here is the code for the former part:

#define FREQ_PIN 3
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
void setup() {
  pinMode(FREQ_PIN, INPUT);
 lcd.begin(16, 2);
}

void loop()
{

unsigned long t1 = pulseIn(FREQ_PIN, HIGH);// Time how long it takes to go HIGH again
unsigned long t2 = pulseIn(FREQ_PIN, LOW); // and how long it takes to go low again.
 double t = t1 + t2;
         double f = 1000000.0/t;
           double w = 2 * 3.14 * f;
           double v = w * 0.05;
       lcd.setCursor(0,0);
         lcd.println(v);
         delay(2000);
}

Help please, I very very appreciate it.

i have the exactly the same circuit with this post, but i don't know how to write, thanks a lot

The code above is created by members, they are Pauls, Groove, AWOL and mem, i couldn't do this without their help, thanks a lot :slight_smile:

Hi

Thanks for the reply Cemath. Really sorry the late post. I tried out as you said. But still I am not getting the expected output. I found that increasing or decreasing the buffer size [ default 7 ] as no effect on the output. The only change is that suppose if I set buffer[10], then while reading the data, I get a junk character or symbol after each tenth character. So, I googled again and seeked help from other forums too. Finally I found the sdfatlib library doing the GPS logging and SDcard read/write functionality. Google Code Archive - Long-term storage for Google Code Project Hosting..
I tried the examples provided by that library and it worked fine. I was able to read data in char datatype. But since, the GPS sentences logged by GPS are of varied length due to absence of some fields, I had problems in parsing the sentence from the file and extracting the latitude and longitude alone for further processing.
So, I tried the otherway around and modified the GPS logging code[ SdFatGPSLogger example from the sdfatlib library] to log only the latitude and longitude into the sdcard textfile. I worked fine. The log file has contents like below,

13.1224,80.2222
13.1224,80.2222
13.1224,80.2222
13.1224,80.2222
13.1224,80.2222
13.1224,80.2222
13.1224,80.2222
13.1224,80.2222
13.1224,80.2222
13.1224,80.2223
13.1224,80.2223
13.1224,80.2223
13.1224,80.2223
13.1224,80.2223
13.1224,80.2223
13.1224,80.2223
13.1224,80.2222
13.1224,80.2222
13.1224,80.2222
13.1225,80.2222
13.1225,80.2222
13.1226,80.2222
13.1226,80.2222
13.1226,80.2222
13.1226,80.2222
13.1226,80.2222
13.1226,80.2222
13.1226,80.2221
13.1227,80.2221
13.1227,80.2221
13.1227,80.2220
13.1227,80.2220
13.1227,80.2219
13.1227,80.2219
13.1227,80.2218
13.1227,80.2218
13.1227,80.2217
13.1227,80.2217
13.1227,80.2216
13.1227,80.2216
13.1227,80.2215
13.1227,80.2215
13.1227,80.2214
13.1227,80.2214
13.1227,80.2214
13.1227,80.2213
13.1227,80.2213
13.1227,80.2213
13.1227,80.2213
13.1228,80.2213

Now, I stored this file in sdcard and tried to read it using the SdFatRead example. the example read the contents. Since, the file contents in each line are of same size, I used a fixed buffer size and used the comma delimiter to parse the data. I stored the latitude and longitude temporarily within the loop using two char arrays. I tried printing it as I read from the card. It worked.
Now, my problem is that , As I read the above data line by line from the card I need to compare it with another char array, so that If the arrays matches, I will send some data to print on the LCD display.
I used string functions. no use. Since the comparsion is between two char arrays, Inclusion of loops doesnot have data in it to compare. I really dont where is the mistake happening. I am stuck in this issue. I have posted the modified code here [ with no comparsion part] . Please help me out.

/*
 * This sketch reads and prints the file
 * PRINT00.TXT created by SdFatPrint.pde or
 * WRITE00.TXT created by SdFatWrite.pde
 */
 #include <stdio.h>
#include <SdFat.h>
#include <SdFatUtil.h>

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
void error_P(const char *str)
{
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}
void setup(void)
{
  Serial.begin(4800);
  Serial.println();
  Serial.println("type any character to start");
  while (!Serial.available());
  Serial.println();
  
  // initialize the SD card
  if (!card.init()) error("card.init");
  
  // initialize a FAT volume
  if (!volume.init(card)) error("volume.init");
  
  // open the root directory
  if (!root.openRoot(volume)) error("openRoot");
  
  // open a file
  if (file.open(root, "GPSLOG00.TXT", O_READ)) {
    Serial.println("Opened GPSLOG00.TXT");
  }
  else if (file.open(root, "WRITE00.TXT", O_READ)) {
    Serial.println("Opened WRITE00.TXT");    
  }
  else{
    error("file.open");
  }
  Serial.println();
  
  // copy file to serial port
  int16_t n;
//  uint8_t buf[19];// nothing special about 7, just a lucky number.
  char buf[15];
  char lat[8],lon[8];
  char temp;
int linecount=494,count=0; // char *latitude="13.1224",*longitude="80.2222";
  char array1[linecount][8],array2[linecount][8];
 while ((n = file.read((uint8_t *)buf, sizeof(buf))) > 0) 
  {
    for(int i=0;i<n;i++)
    {
     if(buf[i]!=',')
      {
        lat[i]=buf[i];
        Serial.print(lat[i]); // This place is where comparsion will occur
      }
      else
      {
        i=i+1;
        lon[i]=buf[i]; // This place is where comparsion will occur 
        Serial.print(lon[i]);
      }
    }
  }
  /* easier way
  int16_t c;
  while ((c = file.read()) > 0) Serial.print((char)c);
  */
 Serial.println("\nDone");
}

void loop(void) {}

My doubt may be silly. But please help me out since I need to complete the project as part of my academics.
Thanks in advance
Regards
N.Nandhakumar

Dear All
I don't meant to hijack this thread, but I also got same problem with sdfatlib, and when I put sdfatlib as search keyword it refer to this thread.

So here we go.
I have a data file like this :

1474,254
1616,253
2998,254
3153,253
3925,254
4094,253
4642,254
4827,253
5255,254
5459,253
5810,254
6039,253
6336,254
6597,253
6851,254
7160,253
7379,254
7768,253
7957,254
8547,253
8705,254
10133,255
10293,254
10831,255
11031,254
11365,255
11617,254
11866,255
12210,254
12405,255
13096,254
13240,255
13600,256
13744,255
14413,256
14614,255
14931,256
15209,255
15431,256
15918,255
16077,256
16803,257
16963,256
17434,257
17664,256
17922,257
18298,256
18474,257
19442,258
19619,257
19981,258
20254,257
20468,258
21838,259
22057,258
22318,259
22746,258
22905,259
23336,260
23494,259
23917,260
24187,259
24395,260
25432,261
25643,260
25905,261
26455,260
26592,261
26613,262
26757,261
27244,262
27523,261
27721,262
28491,263
28690,262
28963,263
30133,264
30414,263
30605,264
31214,265
31406,264
31682,265
32712,266
32995,265
33180,266
33666,267
33852,266
34131,267
35051,268
35337,267
35517,268
35902,269
36082,268
36364,269
37199,270
37488,269
37662,270
37963,271
38138,270
38424,271
39185,272
39479,271
39648,272
39873,273
40042,272
40335,273
41035,274
41333,273
41497,274
41662,275
41826,274
42122,275
42768,276
43072,275
43231,276
43333,277
43491,276
43797,277
44401,278
44717,277
44870,278
44891,279
45039,278
45369,279
45949,280
46294,279
46436,280
46457,281
46621,280
46899,281
47374,282
47659,281
47817,282
47869,283
48025,282
48314,283
48764,284
49059,283
49211,284
49232,285
49389,284
49670,285
50078,286
50366,285
50518,286
50539,287
50699,286
50967,287
51327,288
51599,287
51754,288
51775,289
51932,288
52199,289
52528,290
52797,289
52951,290
52972,291
53133,290
53385,291
53673,292
53928,291
54086,292
54107,293
54268,292
54517,293
54773,294
55019,293
55179,294
55200,295
55361,294
55603,295
55835,296
56079,295
56238,296
56259,297
56427,296
56655,297
56844,298
57069,297
57236,298
57257,299
57418,298
57651,299
57837,300
58073,299
58231,300
58252,301
58427,300
58636,301
58760,302
58962,301
59140,302
59184,303
59356,302
59565,303
59682,304
59889,303
60061,304
60082,305
60251,304
60461,305
60567,306
60773,305
60943,306
60964,307
61139,306
61338,307
61406,308
61594,307
61777,308
61798,309
61965,308
62170,309
62247,310
62447,309
62617,310
62638,311
62827,310
63006,311
63027,312
63205,311
63392,312
63429,313
63616,312
63793,313
63814,314
64001,313
64177,314
64198,315
64391,314
64561,315
64582,316
64788,315
64947,316
64968,317
65219,316
65354,317
65375,318

and here is what I got from SdFatTail.pde

Question :
How to put each "field" of the output into to different variable (ie: var1, var2) rather than printing it out.

here is the code (small edited from original sample)

/*
 * This sketch reads and prints the tail of all files
 * created by SdFatAppend.pde, SdFatPrint.pde, and
 * SdFatWrite.pde.
 */
#include <SdFat.h>
#include <SdFatUtil.h>

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
void error_P(const char *str)
{
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}

void setup(void)
{
  Serial.begin(9600);
  Serial.println();
  Serial.println("type any character to start");
  while (!Serial.available());
  Serial.println();
  
  // initialize the SD card
  if (!card.init()) error("card.init");
  
  // initialize a FAT volume
  if (!volume.init(card)) error("volume.init");
  
  // open the root directory
  if (!root.openRoot(volume)) error("openRoot");
}

/*
 * Print tail of all SdFat example files
 */
void loop(void)
{
  dir_t dir;
  char name[13];
  
  // read next directory entry
  if (root.readDir(dir) != sizeof(dir)) {
    Serial.println("End of Directory");
    while(1);
  }
  
  // check for file name "APPEND.TXT", "PRINT*.TXT"
  // or "WRITE*.TXT"
  // first 8 bytes are blank filled name
  // last three bytes are blank filled extension
  if ((strncmp((char *)dir.name, "MYSTEP", 6)) ||
      strncmp((char *)&dir.name[8], "TXT", 3)) {
        return;
  }
  // format file name
  SdFile::dirName(dir, name);
  
  // remember position in root dir
  uint32_t pos = root.curPosition();
  
  // open file
  if (!file.open(root, name, O_READ)) error("file.open");
  
  // restore root position
  if (!root.seekSet(pos)) error("root.seekSet");
  
  // print file name message
  Serial.print("Tail of: ");
  Serial.println(name);
  
  // position to tail of file
  //if (file.fileSize() > 100) {
  //  if (!file.seekSet(file.fileSize() - 100)) error("file.seekSet");
  //}
  int16_t c;
  // find end of line  
  //while ((c = file.read()) > 0 && c != '\n');
  
  // print rest of file
  while ((c = file.read()) > 0) 
  {
    Serial.print((char)c);
  }
  file.close();
  Serial.println();
}

Sincerely
-bino-

Does it work with a micro-SD ? I been trying use it but it didnt work out :frowning:

I'm having the same problem as alecjcook. when i put the #includes in an otherwise skeleton sketch i get:

C:\Program Files\arduino-0018\libraries\SDcard\arduino sd card example.cpp:161: error: 'DEC' was not declared in this scope

along with 'HEX' not declared, 'Serial' not declared, 'delay' not declared ...

does the cpp file need some tweaking maybe?

Hi, delete #include <FatStructs.h> and the problems disappear.

I have other problem how i can change the pins 10-13 to 5-8?I modified Sd2PinMap but not working.

Thanks

Hi everyone,
I'm sorry to re-ressurect this, but this library seems to be the best (of the thousand SD Libraries) to fit my needs...

I've got the same problem as post number 136 in page 10 (I would post the link, but I'm not allowed).

Seems like it is not compiling in Arduino 19. I tried out the soluton proposed in the next post, but it didn't help :frowning:

EDIT: Ok, it's because it doesn't work on ATmega328...

Guys, how do I get this working with the Arduino Mega (Atmega1280) Thanks.

H there,

First of all, thanls so much for this great tutorial. Some weeks ago i developed my own SD Card shield and it runs perfectly.

However, now i have a problem: sometimes it does not write data into the SD.

Let me explain. I made a datalogger based on a RTC ds1337 and a SD card wired such as showed in this topic. No apparenty it is not the problem.
I have the arduino sleeping most of the time, and ones per hour the rtc get up the arduino and measure by different senors, and save the data into a SD.

From time to time i remove the SD card from its slot and check the new data. I observed that when i do it (remove and put the SD card into the slot again, it does not save the data). BUT if i remove the slot, but if i remove the power to arduino just immediately after to put the SD card into the slot, OR push the reset button (at the end it is the same)... the next data are saved. :-/

So, i am a little bit lost in this situation. It is not a big problem, because to remove the power and connecting it again is enough, or pushing the reset button but it is not ideal for my project..., right?

I use the filelogger library.

The code is prettly long, so i put here just only the essential lines..

#include <FileLogger.h>                      // Librería para almacenamiento de dtos en microSD
#include <DallasTemperature.h>               // Librería para el sensor de temperatura DS18B20
#include <OneWire.h>
#include <Wire.h>
#include <DS1337.h>                          // Librería para el RTC DS1307
#include <Sensirion.h>                       // Librería para el sensor de temperatura y humedad SH15
#include <avr/power.h>
#include <avr/sleep.h>                       // Librería para modo dormir del microcontrolado

// Definición de los pin analógicos
#define luz 0                                // Luminosidad LDR
#define suelo 1                              // Humedad del suelo
#define lluvia 2                             // Intensidad de la lluvia
#define bateria 3                            // Voltaje de entrada

// Definición de los pin digitales
#define alarma 2                             // Alarmas del RTC DS1337
#define viento 3                             // Anemómetro QRD1114
#define tempsuelo 4                          // Temperatura del suelo DS18B20
#define temphumdat 5                         // Temperatura/humedad/Punto de rocío SH15
#define temphumcl 6                          // Temperatura-humedad SH15
#define vida 7                               // Led de vida
#define power 8                              // Alimentación de los sensores
#define temp 9                               // Temperatura al sol DS18B20

// Activación de librerías
DS1337 RTC = DS1337();                       // Configuración librería RTC DS1337
OneWire oneWire(temp);                       // Configuración librería termómetro DS18B20
DallasTemperature sensors(&oneWire);
OneWire oneWire2(tempsuelo);                 // Configuración librería termómetro DS18B20
DallasTemperature sensors2(&oneWire2);
DeviceAddress tempDeviceAddress;             // Define la dirección de cada sensor de temperatura
Sensirion SH15 = Sensirion(temphumdat, temphumcl);          

// Definición de constantes
const float pi = 3.14159265;                 // Numero PI
const float volt = 0.0108480556;             // Resolución voltaje de entrada
const int periodo2 = 10000;                  // Periodo de medida del anemómetro (ms)
const int radio = 65;                        // Radio de anemómetro (mm)
const int temp_precision = 12;               // Resolución de los sensores de temperarura (bits)
const int sensibilidad = 30;                 // Sensibilidad del disdrometro
const float area=0.001654;                   // Area de medida del sensor disdrómetro (m^2)

// Definición de variables
long contador = 1;                           // Contador de datos almacenados
unsigned int year = 0000;                    // Año
int month = 00;                              // Mes
int day = 00;                                // Día
int hora = 00;                               // Hora
int minuto = 00;                             // Minuto
int segundo = 00;                            // Segundos
float batt = 0;                              // Voltage de entrada
float innerVcc;                              // Voltaje interno
float innertemp;                             // Temperarura interna
float ambtemp = 0;                           // Temperatura ambiental SH15 (ºC)
float humedad = 0;                           // Humedad ambiental SH15 (%)
float TRocio = 0;                            // Punto de rocio (ºC)
unsigned long BWCounter = 0;                 // Pulsos Blanco/Negro del anemómetro
float velviento = 0;                         // Velocidad del viento (m/s)
long lux = 0;                                // Luminosidad (LUX)
float tempext = 0;                           // Temperatura (ºC)
float suelohum = 0;                          // Humedad del suelo (%)
float TSAr = 0;                              // Temperatura del suelo arriba (ºC)
float TSAb = 0;                              // Temperatura del suelo abajo (ºC)
unsigned long intensidad = 0;                // Intensidad de lluvia (gotas/horas/m2)


void setup(){
  // Inicializacion RTC
  RTC.start();
  // Configuración de la alarma
  RTC.enable_interrupt();
  RTC.setSeconds(55);
  RTC.setMinutes(59);
  RTC.setAlarmRepeat(EVERY_HOUR);
  RTC.writeAlarm();
  pinMode(alarma, INPUT);                   // Receptor de alarmas
  digitalWrite(alarma, HIGH);
  // Configuración de pins
  pinMode(power, OUTPUT);                    // Alimentacion de sensores
  pinMode(vida, OUTPUT);                     // Led de vida
  // Inicializacion del puerto serie
  Serial.begin(9600);                        // Inicia comunicaciones
  // Inicialización de sensores
  sensors.begin();                           // Inicia el sensor de temperatura DS18B20
  sensors2.begin();
  // Mostrar cabecera por puerto serie (*)
  SplashScreen();
}

void loop(){
  digitalWrite(power, HIGH);
  delay(5000);
  digitalWrite(vida, HIGH);                // Enciende el led mientras realiza las lecturas
  tiempo();                                // Toma la hora
  delay(20);
  readVcc();                               // Lee el voltaje interior
  delay(20);
  midebateria();                           // Mide el voltaje de entrada
  delay(20);
  readTemp();                              // Mide la temperatura interna
  delay(20);
  midegotas();                             // Mide la intensidad de lluvia (disdrómetro)
  delay(20);
  temperatura();                           // Mide la temperatura exterior (DS18B20)
  delay(20);
  sueloH();                                // Mide la humedad del suelo
  delay(20);
  suelotemp();                             // Mide la temperatura del suelo
  delay (20);
  temphumroc();                            // Lee temperatura y humedad (SH15) y calcula el punto de rocío
  delay (20);
  luminosidad();                           // Mide la luminosidad (LDR)
  delay  (20);
  velocidadviento();                       // Mide la velocidad del viento (QRD1114)
  mostrar();                             // Muestra los datos a través del puerto serial
  grabar();                              // Graba los datos en formato ascii en un soporte microSD
  delay(5000); 
  contador = contador + 1;                 // Actualiza el contador de medidas
  digitalWrite(vida, LOW);                 // Apaga el led al acabar el proceso
  digitalWrite(power, LOW);
  //attachInterrupt(0, Despertar, FALLING);
  delay(500);
  Dormir();
}


void Despertar(){
}

void Dormir(){
  attachInterrupt(0, Despertar, FALLING);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  RTC.enable_interrupt();
  sleep_mode();
  // -->FASE DE BAJO CONSUMO DEL DISPOSITIVO<--
  sleep_disable();
  RTC.disable_interrupt();
  detachInterrupt(0);
}

// Lee el Reloj de Tiempo Real (DS1337)
void tiempo(){
  // Lee el RTC
  RTC.readTime();
  // Memoriza la fecha actual
  day = RTC.getDays();
  month = RTC.getMonths();
  year = RTC.getYears();
  //Memoriza la hora actual
  hora = RTC.getHours();
  minuto = RTC.getMinutes();
  segundo = RTC.getSeconds();
  return;
} 

// Lee el voltaje de entrada
float midebateria(){
  batt = (analogRead(bateria))*volt;
  return batt;
}

Code follows with other functions to measure the different sensors. I think that they are not essential here...

[Sorry, comments are in Spanish]

So,any idea about what could be the idea and how could i solve it by software or hardware?

Thanks!!

I've been meaning to intergrate an SD card inot one of my projects a while
ago, only I had no idea how to. Now, all I need is to find somewhere that
sells the SD card holder. I do not have anything to dismantle so I would
have to buy one.

Here's the one I used:

This one is better, but more expensive:

And here's the sketch I used to test my SD card with direct write/reading. (no FAT) Its VERY fast to handle the SD card, as I needed speed, and not FAT16 or anything like that.

/* 

  Basic instructions for recording data in an SD Card in native mode
  The SD card (3.3 V) must be properly interfaced to Arduino (5 V)
  Didier Longueville invenit et fecit February 2010
  
  
  Serial 115200
  
*/

// Ports for Arduino Mega //
int PIN_CS = PINB0;      // chip select
int PIN_MOSI = PINB2;    // master out slave in
int PIN_MISO = PINB3;    // master in slave out
int PIN_CLOCK = PINB1;   // clock

int blockPosition = 30;

/********************** SPI SECTION BELOW **********************/

// SPI Variables
byte clr;     // dummy variable used to clear some of the SPI registers
byte spi_err; // SPI timeout flag, must be cleared manually

// send an SPI command, includes time out management
// returns spi_err: "0" is "no error"
byte spi_cmd(volatile char data) 
{
  spi_err = 0; // reset spi error
  SPDR = data; // start the transmission by loading the output byte into the spi data register
  int i = 0;
  while (!(SPSR & (1<<SPIF))) 
  {
    i++;
    if (i >= 0xFF) 
    {
      spi_err = 1;
      return(0x00);
    }
  }
  // returned value
  return(SPDR); 
}

// initialize SPI port 
void spi_initialize(void) 
{
  SPCR = (1<<SPE) | (1<<MSTR); // spi enabled, master mode
  clr = SPSR; // dummy read registers to clear previous results
  clr = SPDR;
}

/********************** SD CARD SECTION BELOW **********************/

// SD Card variables
#define blockSize 512          // block size (default 512 bytes)
byte vBlock[blockSize];        // set vector containing data that will be recorded on SD Card
byte vBuffer[16];

#define GO_IDLE_STATE 0x00     // resets the SD card
#define SEND_CSD 0x09          // sends card-specific data
#define SEND_CID 0x0A          // sends card identification 
#define READ_SINGLE_BLOCK 0x11 // reads a block at byte address 
#define WRITE_BLOCK 0x18       // writes a block at byte address
#define SEND_OP_COND 0x29      // starts card initialization
#define APP_CMD 0x37           // prefix for application command

// Send a SD command, num is the actual index, NOT OR'ed with 0x40. 
// arg is all four bytes of the argument
byte sdc_cmd(byte commandIndex, long arg) 
{
  PORTB &= ~(1<<PIN_CS);   // assert chip select for the card
  spi_cmd(0xFF);           // dummy byte
  commandIndex |= 0x40;    // command token OR'ed with 0x40 
  spi_cmd(commandIndex);   // send command
  for (int i=3; i>=0; i--) 
  {
    spi_cmd(arg>>(i*8));   // send argument in little endian form (MSB first)
  }
  spi_cmd(0x95);           // checksum valid for GO_IDLE_STATE, not needed thereafter, so we can hardcode this value
  spi_cmd(0xFF);           // dummy byte gives card time to process
  byte res = spi_cmd(0xFF);
  return (res);  // query return value from card
}

// initialize SD card 
// retuns 1 if successful
byte sdc_initialize(void) 
{
  // set slow clock: 1/128 base frequency (125Khz in this case)
  SPCR |=  (1<<SPR1) | (1<<SPR0); // set slow clock: 1/128 base frequency (125Khz in this case)
  SPSR &= ~(1<<SPI2X);            // No doubled clock frequency
  // wake up SD card
  PORTB |=  (1<<PIN_CS);          // deasserts card for warmup
  PORTB |=  (1<<PIN_MOSI);        // set MOSI high
  for(byte i=0; i<10; i++) 
  {
    spi_cmd(0xFF);                // send 10 times 8 pulses for a warmup (74 minimum)
  }
  // set idle mode
  byte retries=0;
  PORTB &= ~(1<<PIN_CS);          // assert chip select for the card
  while(sdc_cmd(GO_IDLE_STATE, 0) != 0x01) 
  { 
    // while SD card is not in iddle state
    retries++;
    if (retries >= 0xFF) 
    {
      return(NULL); // timed out!
    }
  }
  // at this stage, the card is in idle mode and ready for start up
  retries = 0;
  sdc_cmd(APP_CMD, 0); // startup sequence for SD cards 55/41
  while (sdc_cmd(SEND_OP_COND, 0) != 0x00) 
  {
    retries++;
    if (retries >= 0xFF) 
    {
      return(NULL); // timed out!
    }
    sdc_cmd(APP_CMD, 0); 
  }
  // set fast clock, 1/4 CPU clock frequency (4Mhz in this case)
  SPCR &= ~((1<<SPR1) | (1<<SPR0)); // Clock Frequency: f_OSC / 4 
  SPSR |=  (1<<SPI2X);              // Doubled Clock Frequency: f_OSC / 2 
  return (0x01); // returned value (success)
}

// clear block content
void sdc_clearVector(void) 
{
  for (int i=0; i<blockSize; i++) 
  {
    vBlock[i] = 0;
  }
}

// get nbr of blocks on SD memory card from
long sdc_totalNbrBlocks(void) 
{
  sdc_readRegister(SEND_CSD);
  // compute size
  long C_Size = ((vBuffer[0x08] & 0xC0) >> 6) | ((vBuffer[0x07] & 0xFF) << 2) | ((vBuffer[0x06] & 0x03) << 10);
  long C_Mult = ((vBuffer[0x08] & 0x80) >> 7) | ((vBuffer[0x08] & 0x03) << 2);
  return ((C_Size+1) << (C_Mult+2)); 
}

// read SD card register content and store it in vBuffer
void sdc_readRegister(byte sentCommand) 
{
  byte res=sdc_cmd(sentCommand, 0); 
  while(res != 0x00) res=spi_cmd(0xFF); // retry

  // wait for data token
  while (spi_cmd(0xFF) != 0xFE); 
  // read data
  for (int i=0; i<16; i++) 
  {
    vBuffer[i] = spi_cmd(0xFF);
  }
  // read CRC (lost results in blue sky)
  spi_cmd(0xFF); // LSB
  spi_cmd(0xFF); // MSB
}

// write block on SD card 
// addr is the address in bytes (multiples of block size)
void sdc_writeBlock(long blockIndex) 
{  
  while(sdc_cmd(WRITE_BLOCK, blockIndex * blockSize) != 0x00) { }
  spi_cmd(0xFF); // dummy byte (at least one)
  // send data packet (includes data token, data block and CRC)
  // data token
  spi_cmd(0xFE);
  // copy block data
  for (int i=0; i<blockSize; i++) 
  {
    spi_cmd(vBlock[i]); 
  }
  // write CRC (lost results in blue sky)
  spi_cmd(0xFF); // LSB
  spi_cmd(0xFF); // MSB
  // wait until write is finished
  while (spi_cmd(0xFF) != 0xFF); // kind of NOP
}

// read block on SD card and copy data in block vector
// retuns 1 if successful
void sdc_readBlock(long blockIndex) 
{
  byte res = sdc_cmd(READ_SINGLE_BLOCK,  (blockIndex * blockSize));
  while(res != 0x00) res=spi_cmd(0xFF); // retry
  // read data packet (includes data token, data block and CRC)
  // read data token
  while (spi_cmd(0xFF) != 0xFE); 
  // read data block
  for (int i=0; i<blockSize; i++) 
  {
    vBlock[i] = spi_cmd(0xFF); // read data
  }
  // read CRC (lost results in blue sky)
  spi_cmd(0xFF); // LSB
  spi_cmd(0xFF); // MSB
}

/********************** MAIN ROUTINES SECTION  BELOW **********************/

void setup() 
{
  // Set ports
  // Data in
  DDRB &= ~(1<<PIN_MISO);
  // Data out
  DDRB |=  (1<<PIN_CLOCK);
  DDRB |=  (1<<PIN_CS);
  DDRB |=  (1<<PIN_MOSI);  
  // Initialize serial communication 
  Serial.begin(115200);
  // Initialize SPI and SDC 
  spi_err=0;        // reset SPI error
  spi_initialize(); // initialize SPI port
  sdc_initialize(); // Initialize SD Card
  Serial.print(sdc_totalNbrBlocks(), DEC);
  Serial.println(" blocks");
  Serial.print("Block Size: ");
  Serial.println(blockSize);
}  

void loop() 
{
  unsigned long tx1 = 0;
  unsigned long tx2 = 0; 
  
  if (true) // WRITE //
  {
    vBlock[0] = blockPosition;
    vBlock[1] = blockPosition+1;
    vBlock[2] = blockPosition+2;
    tx1 = micros();
    sdc_writeBlock(blockPosition);
    tx2 = micros();
  
    Serial.print("Time to write: ");
    Serial.println(tx2-tx1);
    
    delay(25);
  }
  
  vBlock[0] = 0;
  vBlock[1] = 0;
  vBlock[2] = 0;
  
  tx1 = micros();
  sdc_readBlock(blockPosition);
  tx2 = micros();

  Serial.print("Time to read: ");
  Serial.println(tx2-tx1);
  Serial.print("Values: ");
  Serial.print(vBlock[0]);
  Serial.print(" ");
  Serial.print(vBlock[1]);
  Serial.print(" ");
  Serial.println(vBlock[2]);
  
  delay(400);  
  
  blockPosition++;
}

Sorry newbie hear with a dump question again, can I use this file system with my 328 that's within my Arduino for midi file playing.

I not interested in recording any files just playing files that's on a fat 16 or fat 32 SD card.

Sorry, you will need uFat or something that reads Fat16 files in a SD Card.

Take a look at this:
http://blushingboy.net/p/SDuFAT/

Wk

Yes I understand that i will need more hardware, I've done all that now thanks: I was referring to software handling if it would handle it. So your post means it will thanks for that.
Got the software sorted now found what I needed on the net for my MIDI project, it's up and running but I need help with some parts of the programming still, editing mainly. I will be posting my project on this forum soon just need to find a suitable forum/place/thread.