/*
SD card datalogger "logger_mon.cpp"
Hardware: Duemilaove withAT238, Adafruit logger shield
and Arduino proto-shield with LCD shift register
Four analog inputs on DA9S pins 1 - 4 to A0-A3
RTC with I2C on A4 and A5
Serial 20x4 LCD using D2,D3,D4
Two switches on D5 & D6, which also has a LED
External digital I/O on DA9S pins 7,8,9, with LEDs using D7,D8 & D9
SD SPI interface using D10,D11,D12 & D13
The SD card has year and month directiries pre-made
There is one CSV file created per day
John Saunders 7/25/2012
The analog reference is 3.072 V to ger 3mv/count from the Duemilanove 3.3V
*/
#define DS1307_I2C_ADDRESS 0x68
#define leftSW 5
#define rightSW 6
#include <avr/pgmspace.h>
#include <SerialLCD.h>
#include <Wire.h>
#include <SD.h>
const int chipSelect = 10;
SerialLCD lcd(2);
// The order of DS1397 values is: secs,mins,hours,weekday(not used),date,month,year(2 digit)
const byte aPin[] = {0,1,2,3}; // the analog pin to be measured
const byte D9pin[] = {1,2,3,4}; // the D9 pin to which it is connected
const byte dPin[] = {7,8,9};
const char LCDColAddr[] = {0,10,0,10}; // for analog LCD placing
const char LCDRowAddr[] = {2,2,3,3};
// for each range 3V - 24 V by 3 there are specific calculations, per the range,Index for the input 0- 7
const int mult[] = {9,6,9,6,6,9,10,12};
const int divide[] = {3,1,1,5,4,5,5,5};
PROGMEM const char header[] = {"Date,Time,Pin 1,Pin2,Pin 3,Pin4,Pin7,Pin8,Pin9"};
PROGMEM const byte recFileIndex[] = {99,99,99,98,8,3,0}; //These are DS1307 addresses or 99=skip
PROGMEM const byte recDateIndex[] = {99,99,99,98,3,0,8};
PROGMEM const byte recTimeIndex[] = {6,3,0,98,99,99,99}; //98 is a flag for midnight
const char intTimeIndex[] = {6,4,3}; //RTC addresses for options for recording interval
const char* settingStrings[] = \
{"minutes","hours","Pin 1 range","Pin 2 range","Pin 3 range","Pin 4 range","Finished"};
const char* intervalStrings[] = {"10 sec","1 min","10 min"};
PROGMEM const byte adjustAddr[] = {0x01,0x02,0x08,0x09,0x0A,0x0B}; //For adjusting & saving setup parametyers
PROGMEM const byte adjustLimit[] = {59,23,7,7,7,7};
PROGMEM const char msg0[] = {"Initializing SD card"};
PROGMEM const char msg1[] = {"Press left switch"};
PROGMEM const char msg2[] = {"for setup options"};
PROGMEM const char msg3[] = {"Entering setting mode"};
PROGMEM const char msg4[] = {"to select this"};
PROGMEM const char msg5[] = {"Stopped to remove SD"};
PROGMEM const char msg6[] = {"Re-boot after"};
PROGMEM const char msg7[] = {"replacing the SD"};
char oldDay = 8; // To find midnight to write the header
char newDay;
char interval = '0'; // the previous value of the selected element of recTime
byte intIndex; // Index into intIndex, stored in DS1307 at address 0x0C
byte rangeIndex[4]; // Index into mult and divide, stored in DS1307 at addresses 0x08-0x0x0B
char D8prev = 1; // State of left button
boolean cardOK = true;
boolean initCount = true;
// These are templates to be populated from the RTC. Put here to help stability
char recDate[] = {"mm/dd/20yy, "};
char recTime[] = {"hh:mm:ss "};
char analogString[4][7]; // Index in AnalogString to put string, the number to be displayed, decimals
char dateFilename[] = {"yy/mm/ARdd.CSV"};
boolean adjustments(byte);
boolean recordHeaders(char *);
boolean recordData(char *);
void makeTimings();
int makeAnalog(int,int,byte dploc = 3);
// int availableMemory();
//void displaySRAM();
int get_free_memory();
void setup()
{
lcd.begin();
Wire.begin();
byte countdown;
analogReference(EXTERNAL); // set by potentiometer to 3.072 volts (3 mv/count)
pinMode(leftSW,INPUT_PULLUP);
pinMode(rightSW,INPUT_PULLUP);
pinMode(7,INPUT_PULLUP);
pinMode(8,INPUT_PULLUP);
pinMode(9,INPUT_PULLUP);
while(digitalRead(leftSW) == 0 || digitalRead(rightSW) == 0) {
lcd.clear();
lcd.print("Release switches!");
delay(100);
}
lcd.printPgm(msg0,1); //msg0
lcd.setCursor(0,2);
// see if the card is present and can be initialized:
if( !SD.begin(chipSelect)) {
lcd.print("No valid card");
// don't do anything more
cardOK=false; // Inhibits ecording, permits monitoring visually
}
else {
lcd.print("Card initialized");
cardOK=true;
}
delay(1000); //Setting mode
for (countdown=0; countdown < 10; countdown++) {
lcd.printPgm(msg1,0,0); //msg1
lcd.printPgm(msg2,2); //msg2
lcd.setCursor(0,3);
lcd.print(10-countdown);
if(digitalRead(leftSW) == 0) {
break;
}
delay(300);
}
if (countdown < 10) {
lcd.printPgm(msg3,0,0); //msg3
byte selection = 0;
while(digitalRead(leftSW) == 0) {
delay(100);
}
while(true) {
lcd.clear();
lcd.print("Adjust ");
lcd.printStr(settingStrings[selection]);
lcd.printPgm(msg1,2);
lcd.printPgm(msg4,3); //msg4
delay(1500);
if(digitalRead(leftSW) == 0) {
if(selection == 6) {
break;
}
else {
D8prev = 0;
adjustments(selection);
}
}
selection++;
if (selection > 6) {
selection = 0;
}
}
} //End of setting mode
// retrieve and display the range and interval settings from the RTC RAM
lcd.clear();
lcd.print("Range settings:");
lcd.setCursor(0,2);
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x08);
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS,5);
for(char i=0;i<4;i++) {
rangeIndex[i] = Wire.read();
if(rangeIndex[i]>7) {
rangeIndex[i] = 0;
}
if(i==2) {
lcd.setCursor(0,3);
}
lcd.print("Pin");
lcd.print(D9pin[i]);
lcd.print('=');
lcd.print(3*(rangeIndex[i]+1));
lcd.print("V ");
}
intIndex=Wire.read();
Wire.endTransmission();
lcd.setCursor(0,4);
lcd.print("Press left switch ");
do {
delay(300);
}
while(digitalRead(leftSW)==1);
D8prev=0;
// displaySRAM();
}
void loop()
{
int analogReading[4];
byte analogCount[4];
if(digitalRead(rightSW)==0) { //This permits safe SD removal
lcd.printPgm(msg5,0,0); //msg5
lcd.printPgm(msg6,2); //msg6
lcd.printPgm(msg7,3); //msg7
delay(300);
cardOK = false;
return;
}
makeTimings();// Generate the filename and date and time strings
if ( initCount == true) {
for(byte i=0;i< 4;i++) {
analogReading[i]=0;
analogCount[i]=0;
analogIn[i]=0;
interval=recTime[intTimeIndex[intIndex]];
}
initCount=false;
}
// Display th date,time,analog values and selected interval
lcd.clear();
lcd.print(recDate);
lcd.print(recTime);
// read four analog pIns and display their values
for (int i = 0; i < 4; i++) {
byte range = rangeIndex[i];
byte dpLoc = 2;
if(range < 3) {
dpLoc++;
}
analogReading[i]+=analogRead(aPin[i]);
analogCount[i]++;
lcd.setCursor(LCDColAddr[i],LCDRowAddr[i]);
lcd.print('P');
lcd.print(D9pin[i]);
lcd.print('=');
if(analogCount[i] >= mult[range]) {
analogIn[i]=analogReading[i]/divide[range];
analogCount[i]=0;
analogReading[i]=0;
}
makeAnalog(i,analogIn[i],dpLoc);
lcd.printStr(analogString[i]);
}
lcd.setCursor(0,4); // bottom line
lcd.print("Rec. Int. = ");
lcd.printStr(intervalStrings[intIndex]);
char D8now = digitalRead(leftSW);
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
// Write header s at midnight anf when booted
if (newDay != oldDay && cardOK == true) {
if(!recordHeaders(dateFilename)) {
lcd.clear();
lcd.print("Header record fail");
delay(1000);
}
else {
lcd.clear();
lcd.print("Header recorded");
delay(1000);
}
}
oldDay=newDay;
if (interval != recTime[intTimeIndex[intIndex]] && cardOK==true \
&&analogIn[0]>=0&&analogIn[1]>=0&&analogIn[2]>=0&&analogIn[3]>=0){
// Write the date and time to the file at selected interval
if(!recordData(dateFilename)) {
lcd.clear();
lcd.print("Data record fail");
delay(1000);
}
}//End of recording
// Run-time change of recording interval
if((D8now == 0) && (D8prev == 1)) {
intIndex++;
if(intIndex >= 4) {
intIndex=0;
}
// store this value into the DS1307 at address 0x0C
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x0C);
Wire.write(intIndex);
Wire.endTransmission();
}
interval=recTime[intTimeIndex[intIndex]];
D8prev=D8now;
delay(70);
}
boolean adjustments(byte adrInx) {
byte adjustValue;
byte adjustBCD;
char D8now;
lcd.clear();
lcd.print("Adj. ");
lcd.printStr(settingStrings[adrInx]);
lcd.print('=');
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(pgm_read_byte(&adjustAddr[adrInx]));
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS,1);
adjustBCD = Wire.read();
Wire.endTransmission();
adjustValue = (adjustBCD & 0x0F) + (10*(adjustBCD>>4));
lcd.printStr("Left SW: adjust",2);
lcd.printStr("Right SW: done",3);
while(digitalRead(rightSW) == 1) {
lcd.setCursor(17,0);
if(adrInx>1) {
lcd.print(3*(adjustValue + 1));
lcd.print(' ');
}
else {
lcd.print(adjustValue);
}
lcd.print(' ');
D8now = digitalRead(leftSW);
if((D8now == 0) && (D8prev == 1)) {
adjustValue++;
if(adjustValue > pgm_read_byte(&adjustLimit[adrInx])) {
adjustValue = 0;
}
}
D8prev = D8now;
}
adjustBCD=(adjustValue % 10) + (adjustValue/10*16);
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(pgm_read_byte(&adjustAddr[adrInx]));
Wire.write(adjustBCD);
Wire.endTransmission();
return true;
}
void makeTimings() {
char timeValue;
char LSDigit;
char MSDigit;
// Read the time values and assign to the template strings
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS,7);
for(char i=0;i<7;i++) {
timeValue = Wire.read();
LSDigit = (timeValue & 0x0F) + '0';
MSDigit = ((timeValue >> 4) & 0x07) +'0';
if(recFileIndex[i] < 98) {
dateFilename[pgm_read_byte(&recFileIndex[i])] = MSDigit;
dateFilename[pgm_read_byte(&recFileIndex[i]) + 1] = LSDigit;
}
if(recDateIndex[i] < 98) {
recDate[pgm_read_byte(&recDateIndex[i])] = MSDigit;
recDate[pgm_read_byte(&recDateIndex[i]) + 1] = LSDigit;
}
if(recTimeIndex[i] < 98) {
recTime[pgm_read_byte(&recTimeIndex[i])] = MSDigit;
recTime[pgm_read_byte(&recTimeIndex[i]) + 1] = LSDigit;
}
if(i == 3) {
newDay = timeValue;
}
}
Wire.endTransmission();
return;
}
boolean recordHeaders(char *recFilename) {
File recFile = SD.open(recFilename, FILE_WRITE); // Open the file
if(recFile) {
int i=0;
char c;
do {
c = pgm_read_byte(header+i++);
recFile.print(c);
}
while(c != 0);
recFile.println();
recFile.print(",,");
for(int i = 0;i<4;i++) {
recFile.print(3*(rangeIndex[i]+1));
recFile.print('V');
if(i<3) {
recFile.print(',');
}
}
recFile.println();
recFile.close();
return true;
}
return false;
}
boolean recordData(char *recFilename) {
File recFile = SD.open(recFilename, FILE_WRITE); // Open the file
if(recFile) {
// Write the analogIn values to the file
recFile.print(recDate);
recFile.print(recTime);
for (byte i = 0; i < 4; i++) {
if (i < 4) {
recFile.print(',');
}
recFile.print(analogString[i]);
}
for(byte i = 7;i < 10;i++) {
recFile.print(',');
recFile.print(digitalRead(i));
}
recFile.println();
recFile.close();
return true;
}
return false;
}
int makeAnalog(int j,int k,byte dploc) {
int div;
int rem;
int mod = 10000;
byte leading = 0;
byte i = 4;
byte pos = 0;
dploc=constrain(dploc,0,3);
if(k < 0) {
analogString[j][pos++] = '-';
k = -k;
}
while(mod >= 1) {
div = k/mod;
rem = k % mod;
if(leading == 1 || div > 0) {
analogString[j][pos++] = div + '0';
leading = 1;
}
if(leading == 0 && div == 0 && i <= dploc) {
analogString[j][pos++] = div + '0';
}
if(dploc > 0 && i == dploc) {
analogString[j][pos++] = '.';
}
k = rem;
mod = mod/10;
i--;
}
analogString[j][pos] = NULL;
return pos;
}
/*
int availableMemory() {
int size = 2048;
byte *buf;
while((buf= (byte *)malloc(--size)) == NULL);
free(buf);
return size;
}
void displaySRAM() {
while(digitalRead(5) == 0) { delay(50);}
lcd.clear();
lcd.print("free memory=");
lcd.print(availableMemory());
lcd.printStr("memory used=",2);
lcd.print(2048-availableMemory());
while(digitalRead(5) == 1) { delay(50);}
return;
}
*/
int get_free_memory()
{
int free_memory;
extern int __bss_end;
extern int* __brkval;
if((int)__brkval == 0)
free_memory = ((int)&free_memory) - ((int)&__bss_end);
else
free_memory = ((int)&free_memory) - ((int)__brkval);
return free_memory;
}