Newer
Older
#include "ultralcd.h"
#include "stepper.h"
#include "temperature.h"
#include "language.h"
CardReader::CardReader() {
filesize = 0;
sdpos = 0;
sdprinting = false;
cardOK = false;
saving = false;
logging = false;
workDirDepth = 0;
file_subcall_ctr = 0;
memset(workDirParents, 0, sizeof(workDirParents));
autostart_stilltocheck = true; //the SD start is delayed, because otherwise the serial cannot answer fast enough to make contact with the host software.
autostart_index = 0;
//power to SD reader
#if SDPOWER > -1
char *createFilename(char *buffer, const dir_t &p) { //buffer > 12characters
char *pos = buffer;
for (uint8_t i = 0; i < 11; i++) {
if (p.name[i] == ' ') continue;
if (i == 8) *pos++ = '.';
*pos++ = p.name[i];
return buffer;
}
/**
* Dive into a folder and recurse depth-first to perform a pre-set operation lsAction:
* LS_Count - Add +1 to nrFiles for every file within the parent
* LS_GetFilename - Get the filename of the file indexed by nrFiles
* LS_SerialPrint - Print the full path of each file to serial output
*/
void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) {
// Read the next entry from a directory
// If the entry is a directory and the action is LS_SerialPrint
if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) {
// Get the short name for the item, which we know is a folder
char lfilename[FILENAME_LENGTH];
// Allocate enough stack space for the full path to a folder, trailing slash, and nul
boolean prepend_is_empty = (prepend[0] == '\0');
int len = (prepend_is_empty ? 1 : strlen(prepend)) + strlen(lfilename) + 1 + 1;
// Append the FOLDERNAME12/ to the passed string.
// It contains the full path to the "parent" argument.
// We now have the full path to the item in this folder.
strcpy(path, prepend_is_empty ? "/" : prepend); // root slash if prepend is empty
strcat(path, lfilename); // FILENAME_LENGTH-1 characters maximum
strcat(path, "/"); // 1 character
// Get a new directory object using the full path
// and dive recursively into it.
SdFile dir;
if (!dir.open(parent, lfilename, O_READ)) {
if (lsAction == LS_SerialPrint) {
SERIAL_ECHO_START;
SERIAL_ECHOLN(MSG_SD_CANT_OPEN_SUBDIR);
SERIAL_ECHOLN(lfilename);
}
}
// close() is done automatically by destructor of SdFile
char pn0 = p.name[0];
if (pn0 == DIR_NAME_FREE) break;
if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
if (longFilename[0] == '.') continue;
if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
filenameIsDir = DIR_IS_SUBDIR(&p);
if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue;
switch (lsAction) {
case LS_Count:
nrFiles++;
break;
case LS_SerialPrint:
createFilename(filename, p);
SERIAL_PROTOCOL(prepend);
SERIAL_PROTOCOLLN(filename);
break;
case LS_GetFilename:
createFilename(filename, p);
if (match != NULL) {
if (strcasecmp(match, filename) == 0) return;
}
else if (cnt == nrFiles) return;
cnt++;
break;
void CardReader::ls() {
lsAction = LS_SerialPrint;
root.rewind();
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
* Get a long pretty path based on a DOS 8.3 path
*/
void CardReader::printLongPath(char *path) {
lsAction = LS_GetFilename;
int i, pathLen = strlen(path);
// SERIAL_ECHOPGM("Full Path: "); SERIAL_ECHOLN(path);
// Zero out slashes to make segments
for (i = 0; i < pathLen; i++) if (path[i] == '/') path[i] = '\0';
SdFile diveDir = root; // start from the root for segment 1
for (i = 0; i < pathLen;) {
if (path[i] == '\0') i++; // move past a single nul
char *segment = &path[i]; // The segment after most slashes
// If a segment is empty (extra-slash) then exit
if (!*segment) break;
// Go to the next segment
while (path[++i]) { }
// SERIAL_ECHOPGM("Looking for segment: "); SERIAL_ECHOLN(segment);
// Find the item, setting the long filename
diveDir.rewind();
lsDive("", diveDir, segment);
// Print /LongNamePart to serial output
SERIAL_PROTOCOLCHAR('/');
SERIAL_PROTOCOL(longFilename[0] ? longFilename : "???");
// If the filename was printed then that's it
if (!filenameIsDir) break;
// SERIAL_ECHOPGM("Opening dir: "); SERIAL_ECHOLN(segment);
// Open the sub-item as the new dive parent
SdFile dir;
if (!dir.open(diveDir, segment, O_READ)) {
SERIAL_EOL;
SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_SD_CANT_OPEN_SUBDIR);
SERIAL_ECHO(segment);
break;
}
diveDir.close();
diveDir = dir;
} // while i<pathLen
SERIAL_EOL;
}
#endif // LONG_FILENAME_HOST_SUPPORT
#define SPI_SPEED SPI_HALF_SPEED
#else
#define SPI_SPEED SPI_FULL_SPEED
if (!card.init(SPI_SPEED,SDSS)
#if defined(LCD_SDSS) && (LCD_SDSS != SDSS)
&& !card.init(SPI_SPEED, LCD_SDSS)
#endif
) {
//if (!card.init(SPI_HALF_SPEED,SDSS))
SERIAL_ECHO_START;
SERIAL_ECHOLNPGM(MSG_SD_INIT_FAIL);
SERIAL_ERRORLNPGM(MSG_SD_VOL_INIT_FAIL);
SERIAL_ERRORLNPGM(MSG_SD_OPENROOT_FAIL);
cardOK = true;
SERIAL_ECHO_START;
SERIAL_ECHOLNPGM(MSG_SD_CARD_OK);
SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
void CardReader::setroot() {
/*if (!workDir.openRoot(&volume)) {
SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
sdprinting = false;
cardOK = false;
}
void CardReader::startFileprint() {
if (cardOK) {
sdprinting = true;
}
}
void CardReader::pauseSDPrint() {
if (sdprinting) sdprinting = false;
logging = true;
openFile(name, false);
}
void CardReader::getAbsFilename(char *t) {
uint8_t cnt = 0;
*t = '/'; t++; cnt++;
for (uint8_t i = 0; i < workDirDepth; i++) {
while(*t && cnt < MAXPATHNAMELENGTH) { t++; cnt++; } //crawl counter forward.
void CardReader::openFile(char* name, bool read, bool replace_current/*=true*/) {
if (!cardOK) return;
if (file.isOpen()) { //replacing current file by new file, or subfile call
if (!replace_current) {
if (file_subcall_ctr > SD_PROCEDURE_DEPTH - 1) {
SERIAL_ERROR_START;
SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:");
SERIAL_ERRORLN(SD_PROCEDURE_DEPTH);
SERIAL_ECHO_START;
SERIAL_ECHOPGM("SUBROUTINE CALL target:\"");
SERIAL_ECHO(name);
SERIAL_ECHOPGM("\" parent:\"");
//store current filename and position
getAbsFilename(filenames[file_subcall_ctr]);
SERIAL_ECHO(filenames[file_subcall_ctr]);
SERIAL_ECHOPGM("\" pos");
SERIAL_ECHOLN(sdpos);
SERIAL_ECHO_START;
SERIAL_ECHOPGM("Now doing file: ");
SERIAL_ECHOLN(name);
}
file.close();
}
else { //opening fresh file
file_subcall_ctr = 0; //resetting procedure depth in case user cancels print while in procedure
SERIAL_ECHO_START;
SERIAL_ECHOPGM("Now fresh file: ");
SERIAL_ECHOLN(name);
}
sdprinting = false;
SdFile myDir;
curDir = &root;
char *fname = name;
char *dirname_start, *dirname_end;
if (name[0] == '/') {
dirname_start = &name[1];
dirname_end = strchr(dirname_start, '/');
//SERIAL_ECHO("start:");SERIAL_ECHOLN((int)(dirname_start - name));
//SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end - name));
if (dirname_end > 0 && dirname_end > dirname_start) {
char subdirname[FILENAME_LENGTH];
strncpy(subdirname, dirname_start, dirname_end - dirname_start);
subdirname[dirname_end - dirname_start] = 0;
SERIAL_ECHOLN(subdirname);
SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL);
SERIAL_PROTOCOL(subdirname);
//SERIAL_ECHOLN("dive ok");
curDir = &myDir;
dirname_start = dirname_end + 1;
else { // the remainder after all /fsa/fdsa/ is the filename
fname = dirname_start;
//SERIAL_ECHOLN("remainder");
//SERIAL_ECHOLN(fname);
break;
}
if (read) {
if (file.open(curDir, fname, O_READ)) {
SERIAL_PROTOCOLPGM(MSG_SD_FILE_OPENED);
SERIAL_PROTOCOL(fname);
SERIAL_PROTOCOLPGM(MSG_SD_SIZE);
Bernhard Kubicek
committed
SERIAL_PROTOCOLLN(filesize);
SERIAL_PROTOCOLLNPGM(MSG_SD_FILE_SELECTED);
getfilename(0, fname);
lcd_setstatus(longFilename[0] ? longFilename : fname);
SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL);
else { //write
if (!file.open(curDir, fname, O_CREAT | O_APPEND | O_WRITE | O_TRUNC)) {
SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL);
SERIAL_PROTOCOL(fname);
SERIAL_PROTOCOLPGM(MSG_SD_WRITE_TO_FILE);
Bernhard Kubicek
committed
SERIAL_PROTOCOLLN(name);
lcd_setstatus(fname);
void CardReader::removeFile(char* name) {
if (!cardOK) return;
file.close();
sdprinting = false;
SdFile myDir;
curDir = &root;
char *fname = name;
char *dirname_start, *dirname_end;
if (name[0] == '/') {
dirname_start = strchr(name, '/') + 1;
while (dirname_start > 0) {
dirname_end = strchr(dirname_start, '/');
//SERIAL_ECHO("start:");SERIAL_ECHOLN((int)(dirname_start - name));
//SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end - name));
if (dirname_end > 0 && dirname_end > dirname_start) {
char subdirname[FILENAME_LENGTH];
strncpy(subdirname, dirname_start, dirname_end - dirname_start);
subdirname[dirname_end - dirname_start] = 0;
SERIAL_ECHOLN(subdirname);
SERIAL_PROTOCOLPGM("open failed, File: ");
SERIAL_PROTOCOL(subdirname);
return;
}
//SERIAL_ECHOLN("dive ok");
curDir = &myDir;
dirname_start = dirname_end + 1;
else { // the remainder after all /fsa/fdsa/ is the filename
fname = dirname_start;
//SERIAL_ECHOLN("remainder");
//SERIAL_ECHOLN(fname);
break;
}
}
}
else { // relative path
curDir = &workDir;
}
if (file.remove(curDir, fname)) {
SERIAL_PROTOCOLPGM("File deleted:");
SERIAL_PROTOCOLLN(fname);
sdpos = 0;
}
else {
SERIAL_PROTOCOLPGM("Deletion failed, File: ");
SERIAL_PROTOCOL(fname);
}
}
void CardReader::getStatus() {
if (cardOK) {
SERIAL_PROTOCOLPGM(MSG_SD_PRINTING_BYTE);
Bernhard Kubicek
committed
SERIAL_PROTOCOL(sdpos);
Bernhard Kubicek
committed
SERIAL_PROTOCOLLN(filesize);
SERIAL_PROTOCOLLNPGM(MSG_SD_NOT_PRINTING);
void CardReader::write_command(char *buf) {
char* begin = buf;
char* npos = 0;
char* end = buf + strlen(buf) - 1;
file.writeError = false;
begin = strchr(npos, ' ') + 1;
end = strchr(npos, '*') - 1;
}
end[1] = '\r';
end[2] = '\n';
end[3] = '\0';
file.write(begin);
Bernhard Kubicek
committed
SERIAL_ERROR_START;
SERIAL_ERRORLNPGM(MSG_SD_ERR_WRITE_TO_FILE);
if (!force && (!autostart_stilltocheck || next_autostart_ms < millis()))
return;
autostart_stilltocheck = false;
if (!cardOK) {
sprintf_P(autoname, PSTR("auto%i.g"), autostart_index);
for (int8_t i = 0; i < (int8_t)strlen(autoname); i++) autoname[i] = tolower(autoname[i]);
dir_t p;
root.rewind();
bool found = false;
while (root.readDir(p, NULL) > 0) {
for (int8_t i = 0; i < (int8_t)strlen((char*)p.name); i++) p.name[i] = tolower(p.name[i]);
if (p.name[9] != '~' && strncmp((char*)p.name, autoname, 5) == 0) {
char cmd[4 + (FILENAME_LENGTH + 1) * MAX_DIR_DEPTH + 2];
sprintf_P(cmd, PSTR("M23 %s"), autoname);
enqueuecommand(cmd);
enqueuecommands_P(PSTR("M24"));
void CardReader::closefile(bool store_location) {
saving = logging = false;
if (store_location) {
//future: store printer state, filename and position for continuing a stopped print
// so one can unplug the printer and continue printing the next day.
}
/**
* Get the name of a file in the current directory by index
*/
void CardReader::getfilename(uint16_t nr, const char * const match/*=NULL*/) {
curDir = &workDir;
lsAction = LS_GetFilename;
nrFiles = nr;
curDir->rewind();
uint16_t CardReader::getnrfilenames() {
curDir = &workDir;
lsAction = LS_Count;
nrFiles = 0;
curDir->rewind();
return nrFiles;
SdFile *parent = &root;
if (workDir.isOpen()) parent = &workDir;
if (!newfile.open(*parent, relpath, O_READ)) {
SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_SD_CANT_ENTER_SUBDIR);
SERIAL_ECHOLN(relpath);
if (workDirDepth < MAX_DIR_DEPTH) {
++workDirDepth;
for (int d = workDirDepth; d--;) workDirParents[d + 1] = workDirParents[d];
workDirParents[0] = *parent;
void CardReader::updir() {
if (workDirDepth > 0) {
--workDirDepth;
workDir = workDirParents[0];
for (uint16_t d = 0; d < workDirDepth; d++)
workDirParents[d] = workDirParents[d+1];
void CardReader::printingHasFinished() {
st_synchronize();
if (file_subcall_ctr > 0) { // Heading up to a parent file that called current as a procedure.
file.close();
file_subcall_ctr--;
openFile(filenames[file_subcall_ctr], true, true);
setIndex(filespos[file_subcall_ctr]);
startFileprint();
}
else {
file.close();
sdprinting = false;
if (SD_FINISHED_STEPPERRELEASE) {
//finishAndDisableSteppers();
enqueuecommands_P(PSTR(SD_FINISHED_RELEASECOMMAND));
}