/* * Copyright (c) 2006-2009 by Roland Riegel * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /* 12-3-09 Copyright SparkFun Electronics© 2009 Nathan Seidle spark at sparkfun.com version 1.11 mods by S. James Remington (sjames_remington (at) yahoo.com OpenLog is a simple serial logger based on the ATmega328 running at 16MHz. The ATmega328 should be able to talk to high capacity (larger than 2GB) SD cards. The whole purpose of this logger was to create a logger that just powered up and worked. OpenLog ships with standard 57600bps serial bootloader running at 16MHz so you can load new firmware with a simple serial connection. This makes it compatible with Arduino if needed. OpenLog runs at 9600bps by default. This is configurable to 2400, 9600, 57600, and 115200bps. We recommend you attach a serial connection to reconfigure the unit to work at a different serial speed, but you should be able to do it in software. Type '?' to get a list of supported commands. During power up, you will see '12>'. '1' indicates the serial connection is established. '2' indicates the SD card has been successfully initialized. '>' indicates OpenLog is ready to receive commands. Recording constant 115200bps datastreams are supported. Throw it everything you've got! 'cd ..' is a bit weird. Normally it's 'cd..' but to change to a lower dir, use 'cd ..' Currently, the firmware supports creating a new file or directory up to 16 characters including the '.' and extension. "123456789012.txt" is the longest name. Any longer and the module will re-initialize as if there was a variable sizeof error. Capital letters, white space, and other characters are supported ("Hi there#$_.txt"). Type 'set' to enter baud rate configuration menu. Select the baud rate and press enter. You will then see a message 'Going to 9600bps...' or some such message. You will need to power down OpenLog, change your system UART settings to match the new OpenLog baud rate and then power OpenLog back up. If you get OpenLog stuck into an unknown baudrate, there is a safety mechanism built-in. Tie the RX pin to ground and power up OpenLog. You should see the LEDs blink back and forth for 2 seconds, then blink in unison. Now power down OpenLog and remove the RX/GND jumper. OpenLog is now reset to 9600bps. Please note: The preloaded STK500 serial bootloader is 2k, and begins at 0x7800 (30,720). If the code is l arger than 30,719 bytes, you will get verification errors during serial bootloading. SD vs HCSD configuration is found in sd_raw_config.h - Currently only 512MB, 1GB, 2GB, and some 4GB cards work (not yet compatible with HCSD cards). STAT1 LED is sitting on PD5 (Arduino D5) STAT2 LED is sitting on PB5 (Arduino D13) LED Flashing errors @ 2Hz: No SD card - 3 blinks Baud rate change (requires power cycle) - 4 blinks During an append, OpenLog will buffer 512 characters at a time. That means that if the system loses power while reading in characters, you may loose up to, but no more than, 511 characters. This is important for low power systems where you may not know when the battery or power will die. OpenLog should record each buffer as it receives each 512 byte chunk. The only way to exit an append is with Ctrl+z (ASCII 26). 8mA idle 18mA actively writing Input voltage on VCC can be 3.3 to 12V. Input voltage on RX-I pin must not exceed 6V. Output voltage on TX-O pin will not be greater than 3.3V. This may cause problems with some systems - for example if your attached microcontroller requires 4V minimum for serial communication (this is rare). v1.1 Adding better defines for EEPROM settings Adding new log and sequential log functions. Code is acting very weird with what looks to be stack crashes. I can get around this by turning the optimizer off ('0'). Found an error : EEPROM functions fail when optimizer is set to '0' or '1'. sd-reader_config.h contains flag for USE_DYNAMIC_MEMORY Looks like tweaking the optimization higher causes the asm("nop"); to fail inside the append_file routine. Changing this to delay_us(1); works. I have a sneaking suspicion that I was having buffer overrun problems when defining the input_buffer at 1024 bytes. The ATmega328 should have enough RAM (2K) but weird reset errors were occuring. With the buffer at 512bytes, append_file runs just fine and is able to log at 115200 at a constant data rate. Added file called 'lots-o-text.txt' to version control. This text contains easy to scan text to be used for full data rate checking and testing. */ #include #include #include #include #include "fat.h" #include "fat_config.h" #include "partition.h" #include "sd_raw.h" #include "sd_raw_config.h" #include "uart.h" //Setting DEBUG to 1 will cause extra serial messages to appear //such as "Media Init Complete!" indicating the SD card was initalized #define DEBUG 0 /** * \mainpage MMC/SD/SDHC card library * * This project provides a general purpose library which implements read and write * support for MMC, SD and SDHC memory cards. * * It includes * - low-level \link sd_raw MMC, SD and SDHC read/write routines \endlink * - \link partition partition table support \endlink * - a simple \link fat FAT16/FAT32 read/write implementation \endlink * * \section circuit The circuit * The circuit which was mainly used during development consists of an Atmel AVR * microcontroller with some passive components. It is quite simple and provides * an easy test environment. The circuit which can be downloaded on the * project homepage has been * improved with regard to operation stability. * * I used different microcontrollers during development, the ATmega8 with 8kBytes * of flash, and its pin-compatible alternative, the ATmega168 with 16kBytes flash. * The first one is the one I started with, but when I implemented FAT16 write * support, I ran out of flash space and switched to the ATmega168. For FAT32, an * ATmega328 is required. * * The circuit board is a self-made and self-soldered board consisting of a single * copper layer and standard DIL components, except of the MMC/SD card connector. * * The connector is soldered to the bottom side of the board. It has a simple * eject button which, when a card is inserted, needs some space beyond the connector * itself. As an additional feature the connector has two electrical switches * to detect wether a card is inserted and wether this card is write-protected. * * \section pictures Pictures * \image html pic01.jpg "The circuit board used to implement and test this application." * \image html pic02.jpg "The MMC/SD card connector on the soldering side of the circuit board." * * \section software The software * The software is written in pure standard ANSI-C. It might not be the smallest or * the fastest one, but I think it is quite flexible. See the project's * benchmark page to get an * idea of the possible data rates. * * I implemented an example application providing a simple command prompt which is accessible * via the UART at 9600 Baud. With commands similiar to the Unix shell you can browse different * directories, read and write files, create new ones and delete them again. Not all commands are * available in all software configurations. * - cat \\n * Writes a hexdump of \ to the terminal. * - cd \\n * Changes current working directory to \. * - disk\n * Shows card manufacturer, status, filesystem capacity and free storage space. * - init\n * Reinitializes and reopens the memory card. * - ls\n * Shows the content of the current directory. * - mkdir \\n * Creates a directory called \. * - rm \\n * Deletes \. * - sync\n * Ensures all buffered data is written to the card. * - touch \\n * Creates \. * - write \ \\n * Writes text to \, starting from \. The text is read * from the UART, line by line. Finish with an empty line. * * \htmlonly *

* The following table shows some typical code sizes in bytes, using the 20090330 release with a * buffered read-write MMC/SD configuration, FAT16 and static memory allocation: *

* * * * * * * * * * * * * * * * * * * * * * *
layercode sizestatic RAM usage
MMC/SD2410518
Partition45617
FAT167928188
* *

* The static RAM is mostly used for buffering memory card access, which * improves performance and reduces implementation complexity. *

* *

* Please note that the numbers above do not include the C library functions * used, e.g. some string functions. These will raise the numbers somewhat * if they are not already used in other program parts. *

* *

* When opening a partition, filesystem, file or directory, a little amount * of RAM is used, as listed in the following table. Depending on the library * configuration, the memory is either allocated statically or dynamically. *

* * * * * * * * * * * * * * * * * * * * * * *
descriptordynamic/static RAM
partition17
filesystem26
file53
directory49
* * \endhtmlonly * * \section adaptation Adapting the software to your needs * The only hardware dependent part is the communication layer talking to the * memory card. The other parts like partition table and FAT support are * completely independent, you could use them even for managing Compact Flash * cards or standard ATAPI hard disks. * * By changing the MCU* variables in the Makefile, you can use other Atmel * microcontrollers or different clock speeds. You might also want to change * the configuration defines in the files fat_config.h, partition_config.h, * sd_raw_config.h and sd-reader_config.h. For example, you could disable * write support completely if you only need read support. * * For further information, visit the project's * FAQ page. * * \section bugs Bugs or comments? * If you have comments or found a bug in the software - there might be some * of them - you may contact me per mail at feedback@roland-riegel.de. * * \section acknowledgements Acknowledgements * Thanks go to Ulrich Radig, who explained on his homepage how to interface * MMC cards to the Atmel microcontroller (http://www.ulrichradig.de/). * I adapted his work for my circuit. Although this is a very simple * solution, I had no problems using it. * * \section copyright Copyright 2006-2009 by Roland Riegel * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by * the Free Software Foundation (http://www.gnu.org/copyleft/gpl.html). * At your option, you can alternatively redistribute and/or modify the following * files under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation (http://www.gnu.org/copyleft/lgpl.html): * - byteordering.c * - byteordering.h * - fat.c * - fat.h * - fat_config.h * - partition.c * - partition.h * - partition_config.h * - sd_raw.c * - sd_raw.h * - sd_raw_config.h * - sd-reader_config.h */ //Function declarations static uint8_t read_line(char* buffer, uint8_t buffer_length); static uint32_t strtolong(const char* str); static uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry); static struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name); static uint8_t print_disk_info(const struct fat_fs_struct* fs); void ioinit(void); void print_menu(void); void init_media(void); void baud_menu(void); void system_menu(void); uint8_t read_buffer(char* buffer, uint8_t buffer_length); uint8_t append_file(char* file_name); void newlog(void); void seqlog(void); void command_shell(void); void blink_error(uint8_t ERROR_TYPE); void EEPROM_write(uint16_t uiAddress, unsigned char ucData); unsigned char EEPROM_read(uint16_t uiAddress); void delay_us(uint16_t x); void delay_ms(uint16_t x); #define sbi(port, port_pin) ((port) |= (uint8_t)(1 << port_pin)) #define cbi(port, port_pin) ((port) &= (uint8_t)~(1 << port_pin)) //STAT1 is a general LED and indicates serial traffic #define STAT1 5 #define STAT1_PORT PORTD //STAT2 is tied to the SCL line and indicates volume SPI traffic #define STAT2 5 #define STAT2_PORT PORTB #define ERROR_SD_INIT 3 #define ERROR_NEW_BAUD 5 #define LOCATION_BAUD_SETTING 0x01 #define LOCATION_SYSTEM_SETTING 0x02 #define LOCATION_FILE_NUMBER 0x03 #define BAUD_2400 0 #define BAUD_9600 1 #define BAUD_57600 2 #define BAUD_115200 3 #define MODE_NEWLOG 0 #define MODE_SEQLOG 1 #define MODE_COMMAND 2 //Global variables struct fat_fs_struct* fs; struct partition_struct* partition; struct fat_dir_struct* dd; #define BUFF_LEN 512 char input_buffer[BUFF_LEN]; uint16_t read_spot, checked_spot; //Circular buffer UART RX interrupt //Is only used during append //make treatment of \r consistent with uart_getc, that is, replace with \n (sjr) ISR(USART_RX_vect) { char c; c=UDR0; if(c=='\r') c='\n'; input_buffer[read_spot] = c; read_spot++; STAT1_PORT ^= (1< 5) { system_mode = MODE_NEWLOG; //By default, unit will turn on and go to new file logging EEPROM_write(LOCATION_SYSTEM_SETTING, MODE_NEWLOG); } //If we are in new log mode, find a new file name to write to if(system_mode == MODE_NEWLOG) newlog(); //If we are in sequential log mode, determine if seqlog.txt has been created or not, and then open it for logging if(system_mode == MODE_SEQLOG) seqlog(); //Once either one of these modes exits, go to normal command mode, which is called by returning to main() command_shell(); return 0; } void ioinit(void) { //Init Timer0 for delay_us //TCCR0B = (1< 5) { uart_speed = BAUD_9600; //Reset UART to 9600 if there is no speed stored EEPROM_write(LOCATION_BAUD_SETTING, BAUD_9600); } //Setup uart uart_init(uart_speed); #if DEBUG uart_puts_p(PSTR("UART Init\n")); #else uart_puts_p(PSTR("1")); #endif //Setup SPI, init SD card, etc init_media(); uart_puts_p(PSTR("2")); } //Log to the same file every time the system boots, sequentially //Checks to see if the file SEQLOG.txt is available //If not, create it //If yes, append to it //Return 0 on error //Return anything else on sucess void seqlog(void) { char seq_file_name[13]; sprintf(seq_file_name, "SEQLOG.txt"); struct fat_file_struct* fd = open_file_in_dir(fs, dd, seq_file_name); if(!fd) { uart_puts_p(PSTR("Creating SEQLOG\n")); struct fat_dir_entry_struct file_entry; if(!fat_create_file(dd, seq_file_name, &file_entry)) { uart_puts_p(PSTR("Error creating SEQLOG\n")); return; } } fat_close_file(fd); //Close the file so we can re-open it in append_file append_file(seq_file_name); } //Log to a new file everytime the system boots //Checks the spots in EEPROM for the next available LOG# file name //Updates EEPROM and then appends to the new log file. //Currently limited to 255 files but this should not always be the case. void newlog(void) { uint8_t new_file_number; new_file_number = EEPROM_read(LOCATION_FILE_NUMBER); if(new_file_number == 255) { new_file_number = 0; //By default, unit will start at file number zero EEPROM_write(LOCATION_FILE_NUMBER, new_file_number); } char new_file_name[13]; sprintf(new_file_name, "LOG%03d.txt", new_file_number); struct fat_dir_entry_struct file_entry; while(!fat_create_file(dd, new_file_name, &file_entry)) { //Increment the file number because this file name is already taken new_file_number++; sprintf(new_file_name, "LOG%03d.txt", new_file_number); } //Record new_file number to EEPROM EEPROM_write(LOCATION_FILE_NUMBER, new_file_number++); //Add one the new_file_number for the next power-up #if DEBUG uart_puts_p(PSTR("\nCreated new file: ")); uart_puts(new_file_name); uart_puts_p(PSTR("\n")); #endif //Begin writing to file append_file(new_file_name); } void command_shell(void) { //provide a simple shell char buffer[24]; while(1) { //print prompt uart_putc('>'); //read command char* command = buffer; if(read_line(command, sizeof(buffer)) < 1) continue; //execute command if(strcmp_P(command, PSTR("init")) == 0) { uart_puts_p(PSTR("Closing down file system\n")); /* close file system */ fat_close(fs); /* close partition */ partition_close(partition); //Setup SPI, init SD card, etc init_media(); uart_puts_p(PSTR("File system initialized\n")); } else if(strcmp_P(command, PSTR("?")) == 0) { //Print available commands print_menu(); } else if(strcmp_P(command, PSTR("help")) == 0) { //Print available commands print_menu(); } else if(strcmp_P(command, PSTR("baud")) == 0) { //Go into baud select menu baud_menu(); } else if(strcmp_P(command, PSTR("set")) == 0) { //Go into system setting menu system_menu(); } else if(strncmp_P(command, PSTR("cd "), 3) == 0) { command += 3; if(command[0] == '\0') continue; /* change directory */ struct fat_dir_entry_struct subdir_entry; if(find_file_in_dir(fs, dd, command, &subdir_entry)) { struct fat_dir_struct* dd_new = fat_open_dir(fs, &subdir_entry); if(dd_new) { fat_close_dir(dd); dd = dd_new; continue; } } uart_puts_p(PSTR("directory not found: ")); uart_puts(command); uart_putc('\n'); } else if(strcmp_P(command, PSTR("ls")) == 0) { /* print directory listing */ struct fat_dir_entry_struct dir_entry; while(fat_read_dir(dd, &dir_entry)) { uint8_t spaces = sizeof(dir_entry.long_name) - strlen(dir_entry.long_name) + 4; uart_puts(dir_entry.long_name); uart_putc(dir_entry.attributes & FAT_ATTRIB_DIR ? '/' : ' '); while(spaces--) uart_putc(' '); uart_putdw_dec(dir_entry.file_size); uart_putc('\n'); } } else if(strncmp_P(command, PSTR("cat "), 4) == 0) { command += 4; if(command[0] == '\0') continue; /* search file in current directory and open it */ struct fat_file_struct* fd = open_file_in_dir(fs, dd, command); if(!fd) { uart_puts_p(PSTR("error opening ")); uart_puts(command); uart_putc('\n'); continue; } /* hex dump of file contents */ uint8_t buffer[8]; uint32_t offset = 0; while(fat_read_file(fd, buffer, sizeof(buffer)) > 0) { uart_putdw_hex(offset); uart_putc(':'); for(uint8_t i = 0; i < 8; ++i) { uart_putc(' '); uart_putc_hex(buffer[i]); } uart_putc('\n'); offset += 8; } fat_close_file(fd); } else if(strncmp_P(command, PSTR("type "), 5) == 0) //type out contents of input file with no translation (other than \n -> add \r), sjr) { command += 5; if(command[0] == '\0') continue; /* search file in current directory and open it */ struct fat_file_struct* fd = open_file_in_dir(fs, dd, command); if(!fd) { uart_puts_p(PSTR("error opening ")); uart_puts(command); uart_putc('\n'); continue; } /*print file contents*/ uint8_t buffer[8]; uint32_t offset = 0; uint8_t i; while(fat_read_file(fd, buffer, sizeof(buffer)) > 0) { for(i = 0; i < 8; ++i) { if(buffer[i]==26) break; uart_putc(buffer[i]); } offset += 8; } // uart_putc('\n'); //uart_putc will add \r fat_close_file(fd); } else if(strcmp_P(command, PSTR("disk")) == 0) { if(!print_disk_info(fs)) uart_puts_p(PSTR("error reading disk info\n")); } #if FAT_WRITE_SUPPORT else if(strncmp_P(command, PSTR("rm "), 3) == 0) { command += 3; if(command[0] == '\0') continue; struct fat_dir_entry_struct file_entry; if(find_file_in_dir(fs, dd, command, &file_entry)) { if(fat_delete_file(fs, &file_entry)) continue; } uart_puts_p(PSTR("error deleting file: ")); uart_puts(command); uart_putc('\n'); } else if(strncmp_P(command, PSTR("new "), 4) == 0) { command += 4; if(command[0] == '\0') continue; struct fat_dir_entry_struct file_entry; if(!fat_create_file(dd, command, &file_entry)) { uart_puts_p(PSTR("error creating file: ")); uart_puts(command); uart_putc('\n'); } } else if(strncmp_P(command, PSTR("write "), 6) == 0) { command += 6; if(command[0] == '\0') continue; char* offset_value = command; while(*offset_value != ' ' && *offset_value != '\0') ++offset_value; if(*offset_value == ' ') *offset_value++ = '\0'; else continue; /* search file in current directory and open it */ struct fat_file_struct* fd = open_file_in_dir(fs, dd, command); if(!fd) { uart_puts_p(PSTR("error opening ")); uart_puts(command); uart_putc('\n'); continue; } int32_t offset = strtolong(offset_value); if(!fat_seek_file(fd, &offset, FAT_SEEK_SET)) { uart_puts_p(PSTR("error seeking on ")); uart_puts(command); uart_putc('\n'); fat_close_file(fd); continue; } /* read text from the shell and write it to the file */ uint8_t data_len; while(1) { /* give a different prompt */ uart_putc('<'); //uart_putc(' '); /* read one line of text */ data_len = read_line(buffer, sizeof(buffer)); if(!data_len) break; /* write text to file */ if(fat_write_file(fd, (uint8_t*) buffer, data_len) != data_len) { uart_puts_p(PSTR("error writing to file\n")); break; } } fat_close_file(fd); } else if(strncmp_P(command, PSTR("append "), 7) == 0) { //Find the end of a current file and begins writing to it //Ends only when the user inputs Ctrl+z (ASCII 26) command += 7; if(command[0] == '\0') continue; append_file(command); //Uses circular buffer to capture full stream of text and append to file } else if(strncmp_P(command, PSTR("md "), 3) == 0) { command += 3; if(command[0] == '\0') continue; struct fat_dir_entry_struct dir_entry; if(!fat_create_dir(dd, command, &dir_entry)) { uart_puts_p(PSTR("error creating directory: ")); uart_puts(command); uart_putc('\n'); } } #endif #if SD_RAW_WRITE_BUFFERING else if(strcmp_P(command, PSTR("sync")) == 0) { if(!sd_raw_sync()) uart_puts_p(PSTR("error syncing disk\n")); } #endif else { uart_puts_p(PSTR("unknown command: ")); uart_puts(command); uart_putc('\n'); } } //Do we ever get this far? uart_puts_p(PSTR("Exiting: closing down\n")); } //Appends a stream of serial data to a given file //We use the RX interrupt and a circular buffer of 1024 bytes so that we can capture a full stream of //data even at 115200bps //Does not exit until Ctrl+z (ASCII 26) is received //Returns 0 on error //Returns 1 on success uint8_t append_file(char* file_name) { //search file in current directory and open it struct fat_file_struct* fd = open_file_in_dir(fs, dd, file_name); if(!fd) { uart_puts_p(PSTR("error opening ")); uart_puts(file_name); uart_putc('\n'); return(0); } #if DEBUG uart_puts_p(PSTR("File open\n")); #endif int32_t offset = 0; //Seeks the end of the file : offset = EOF location if(!fat_seek_file(fd, &offset, FAT_SEEK_END)) { uart_puts_p(PSTR("error seeking on ")); uart_puts(file_name); uart_putc('\n'); fat_close_file(fd); return(0); } #if DEBUG uart_puts_p(PSTR("Recording\n")); #endif //give a different prompt to indicate no echoing uart_putc('<'); sbi(STAT1_PORT, STAT1); //Turn on indicator LED read_spot = 0; checked_spot = 0; //Clear circular buffer for(uint16_t i = 0 ; i < BUFF_LEN ; i++) input_buffer[i] = 0; //Start UART buffered interrupts UCSR0B |= (1< (BUFF_LEN/2) { //Record second half the buffer if(fat_write_file(fd, (uint8_t*) input_buffer + (BUFF_LEN/2), (checked_spot - (BUFF_LEN/2)) ) != (checked_spot - (BUFF_LEN/2)) ) uart_puts_p(PSTR("error writing to file\n")); } fat_close_file(fd); cbi(STAT1_PORT, STAT1); //Turn off indicator LED #if DEBUG uart_puts_p(PSTR("Done!\n")); #endif uart_puts_p(PSTR("~")); //Indicate a successful record return(1); //Success! } //Inits the SD interface, opens file system, opens root dir, and checks card info if wanted void init_media(void) { /* setup sd card slot */ if(!sd_raw_init()) { #if DEBUG uart_puts_p(PSTR("MMC/SD initialization failed\n")); #endif blink_error(ERROR_SD_INIT); //continue; } /* open first partition */ partition = partition_open(sd_raw_read, sd_raw_read_interval, #if SD_RAW_WRITE_SUPPORT sd_raw_write, sd_raw_write_interval, #else 0, 0, #endif 0 ); if(!partition) { /* If the partition did not open, assume the storage device * is a "superfloppy", i.e. has no MBR. */ partition = partition_open(sd_raw_read, sd_raw_read_interval, #if SD_RAW_WRITE_SUPPORT sd_raw_write, sd_raw_write_interval, #else 0, 0, #endif -1 ); if(!partition) { #if DEBUG uart_puts_p(PSTR("Opening partition failed\n")); #endif //continue; } } /* open file system */ fs = fat_open(partition); if(!fs) { #if DEBUG uart_puts_p(PSTR("Opening file system failed\n")); #endif //continue; } /* open root directory */ struct fat_dir_entry_struct directory; fat_get_dir_entry_of_path(fs, "/", &directory); dd = fat_open_dir(fs, &directory); if(!dd) { #if DEBUG uart_puts_p(PSTR("Opening root directory failed\n")); #endif //continue; } /* print some card information as a boot message */ //print_disk_info(fs); #if DEBUG uart_puts_p(PSTR("Media Init Complete!\n")); #endif } //Reads a line until the \n enter character is found // modified to store \n to be consistent with append (sjr). uint8_t read_line(char* buffer, uint8_t buffer_length) { memset(buffer, 0, buffer_length); uint8_t read_length = 0; while(read_length < buffer_length - 2) //enough room for \n\0 as last "char" { PORTD ^= (1<> 8; //Set up address and data registers EEARL = uiAddress; //Set up address and data registers EEDR = ucData; EECR |= (1<> 8; //Set up address and data registers EEARL = uiAddress; //Set up address and data registers EECR |= (1< 256) { TIFR0 = (1< 0 ; x--) delay_us(1000); } //Reads a larger buffer of characters until the ctrl+z (ascii 26) character is received uint8_t read_buffer(char* buffer, uint8_t buffer_length) { memset(buffer, 0, buffer_length); uint8_t read_length = 0; while(read_length < buffer_length - 1) { uint8_t c = uart_getc(); if(c == 0x08 || c == 0x7f) { if(read_length < 1) continue; --read_length; buffer[read_length] = '\0'; uart_putc(0x08); uart_putc(' '); uart_putc(0x08); continue; } uart_putc(c); //if(c == '\n') if(c == 26) { buffer[read_length] = '\0'; break; } else { buffer[read_length] = c; ++read_length; } } return read_length; } uint32_t strtolong(const char* str) { uint32_t l = 0; while(*str >= '0' && *str <= '9') l = l * 10 + (*str++ - '0'); return l; } uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry) { while(fat_read_dir(dd, dir_entry)) { if(strcmp(dir_entry->long_name, name) == 0) { fat_reset_dir(dd); return 1; } } return 0; } struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name) { struct fat_dir_entry_struct file_entry; if(!find_file_in_dir(fs, dd, name, &file_entry)) return 0; return fat_open_file(fs, &file_entry); } uint8_t print_disk_info(const struct fat_fs_struct* fs) { if(!fs) return 0; struct sd_raw_info disk_info; if(!sd_raw_get_info(&disk_info)) return 0; uart_puts_p(PSTR("manuf: 0x")); uart_putc_hex(disk_info.manufacturer); uart_putc('\n'); uart_puts_p(PSTR("oem: ")); uart_puts((char*) disk_info.oem); uart_putc('\n'); uart_puts_p(PSTR("prod: ")); uart_puts((char*) disk_info.product); uart_putc('\n'); uart_puts_p(PSTR("rev: ")); uart_putc_hex(disk_info.revision); uart_putc('\n'); uart_puts_p(PSTR("serial: 0x")); uart_putdw_hex(disk_info.serial); uart_putc('\n'); uart_puts_p(PSTR("date: ")); uart_putw_dec(disk_info.manufacturing_month); uart_putc('/'); uart_putw_dec(disk_info.manufacturing_year); uart_putc('\n'); uart_puts_p(PSTR("size: ")); uart_putdw_dec(disk_info.capacity / 1024 / 1024); uart_puts_p(PSTR("MB\n")); uart_puts_p(PSTR("copy: ")); uart_putw_dec(disk_info.flag_copy); uart_putc('\n'); uart_puts_p(PSTR("wr.pr.: ")); uart_putw_dec(disk_info.flag_write_protect_temp); uart_putc('/'); uart_putw_dec(disk_info.flag_write_protect); uart_putc('\n'); uart_puts_p(PSTR("format: ")); uart_putw_dec(disk_info.format); uart_putc('\n'); uart_puts_p(PSTR("free: ")); uart_putdw_dec(fat_get_fs_free(fs)); uart_putc('/'); uart_putdw_dec(fat_get_fs_size(fs)); uart_putc('\n'); return 1; } void print_menu(void) { uart_puts_p(PSTR("\nOpenLog v1.11\n")); uart_puts_p(PSTR("Available commands:\n")); uart_puts_p(PSTR("new \t\t: Creates \n")); uart_puts_p(PSTR("append \t\t: Appends text to end of . The text is read from the UART in a stream and is not echoed. Finish by sending Ctrl+z (ASCII 26)\n")); uart_puts_p(PSTR("write \t: Writes text to , starting from . The text is read from the UART, line by line. Finish with an empty line\n")); uart_puts_p(PSTR("rm \t\t: Deletes \n")); uart_puts_p(PSTR("md \t: Creates a directory called \n")); uart_puts_p(PSTR("cd \t\t: Changes current working directory to \n")); uart_puts_p(PSTR("cd ..\t\t: Changes to lower directory in tree\n")); uart_puts_p(PSTR("ls\t\t\t: Shows the content of the current directory\n")); uart_puts_p(PSTR("cat \t\t: Writes a hexdump of to the terminal\n")); uart_puts_p(PSTR("type \t\t: Prints contents of to the terminal\n")); uart_puts_p(PSTR("disk\t\t\t: Shows card manufacturer, status, filesystem capacity and free storage space\n")); uart_puts_p(PSTR("init\t\t\t: Reinitializes and reopens the memory card\n")); uart_puts_p(PSTR("sync\t\t\t: Ensures all buffered data is written to the card\n")); uart_puts_p(PSTR("\nMenus:\n")); uart_puts_p(PSTR("set\t\t\t: Menu to configure system boot mode\n")); uart_puts_p(PSTR("baud\t\t\t: Menu to configure baud rate\n")); } //Configure what baud rate to communicate at void baud_menu(void) { char buffer[5]; while(1) { uart_puts_p(PSTR("\nBaud Configuration:\n")); uart_puts_p(PSTR("1) Set Baud rate 2400\n")); uart_puts_p(PSTR("2) Set Baud rate 9600\n")); uart_puts_p(PSTR("3) Set Baud rate 57600\n")); uart_puts_p(PSTR("4) Set Baud rate 115200\n")); //print prompt uart_putc('>'); //read command char* command = buffer; if(read_line(command, sizeof(buffer)) < 1) continue; //execute command if(strcmp_P(command, PSTR("1")) == 0) { uart_puts_p(PSTR("\nGoing to 2400bps...\n")); //Set baud rate to 2400 EEPROM_write(LOCATION_BAUD_SETTING, BAUD_2400); blink_error(ERROR_NEW_BAUD); return; } if(strcmp_P(command, PSTR("2")) == 0) { uart_puts_p(PSTR("\nGoing to 9600bps...\n")); //Set baud rate to 9600 EEPROM_write(LOCATION_BAUD_SETTING, BAUD_9600); blink_error(ERROR_NEW_BAUD); return; } if(strcmp_P(command, PSTR("3")) == 0) { uart_puts_p(PSTR("\nGoing to 57600bps...\n")); //Set baud rate to 57600 EEPROM_write(LOCATION_BAUD_SETTING, BAUD_57600); blink_error(ERROR_NEW_BAUD); return; } if(strcmp_P(command, PSTR("4")) == 0) { uart_puts_p(PSTR("\nGoing to 115200bps...\n")); //Set baud rate to 115200 EEPROM_write(LOCATION_BAUD_SETTING, BAUD_115200); blink_error(ERROR_NEW_BAUD); return; } } } //Change how OpenLog works //1) Turn on unit, unit will create new file, and just start logging //2) Turn on, append to known file, and just start logging //3) Turn on, sit at command prompt void system_menu(void) { char buffer[5]; while(1) { uart_puts_p(PSTR("\nSystem Configuration\n")); uart_puts_p(PSTR("\nBoot to:\n")); uart_puts_p(PSTR("1) New file logging\n")); uart_puts_p(PSTR("2) Append file logging\n")); uart_puts_p(PSTR("3) Command prompt\n")); uart_puts_p(PSTR("4) Reset new file number\n")); //print prompt uart_putc('>'); //read command char* command = buffer; if(read_line(command, sizeof(buffer)) < 1) continue; //execute command if(strcmp_P(command, PSTR("1")) == 0) { uart_puts_p(PSTR("New file logging\n")); EEPROM_write(LOCATION_SYSTEM_SETTING, MODE_NEWLOG); return; } if(strcmp_P(command, PSTR("2")) == 0) { uart_puts_p(PSTR("Append file logging\n")); EEPROM_write(LOCATION_SYSTEM_SETTING, MODE_SEQLOG); return; } if(strcmp_P(command, PSTR("3")) == 0) { uart_puts_p(PSTR("Command prompt\n")); EEPROM_write(LOCATION_SYSTEM_SETTING, MODE_COMMAND); return; } if(strcmp_P(command, PSTR("4")) == 0) { uart_puts_p(PSTR("New file number reset to zero\n")); EEPROM_write(LOCATION_FILE_NUMBER, 0); return; } } } #if FAT_DATETIME_SUPPORT void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec) { *year = 2007; *month = 1; *day = 1; *hour = 0; *min = 0; *sec = 0; } #endif