PROGRAMMING

I designed this program (project leader: Jacob Martin, BSc (Hons), MSc Chemistry, Photon Factory: photonfactory.auckland.ac.nz, Blog: nznano.blogspot.co.nz) for an Arduino Seeeduino Stalker v2.3 to test for conductivity every hour and interrupts and put to sleep in between, to save battery. All data is written onto an SD card. Battery status is also logged in a separate file. Note: has to be in Arduino 1.0.6.

http://pastie.org/private/fcwyhatrdftk5cnuyavsgg

#include <MCP3221.h>
#include <Wire.h>
#include <DS3231.h>
#include <avr/sleep.h>
#include <avr/power.h>
DS3231 RTC; //Create the DS3231 object


//The following code is taken from sleep.h as Arduino Software v22 (avrgcc), in w32 does not have the latest sleep.h file
#define sleep_bod_disable() \
{ \
 uint8_t tempreg; \
 __asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
 "ori %[tempreg], %[bods_bodse]" "\n\t" \
 "out %[mcucr], %[tempreg]" "\n\t" \
 "andi %[tempreg], %[not_bodse]" "\n\t" \
 "out %[mcucr], %[tempreg]" \
 : [tempreg] "=&d" (tempreg) \
 : [mcucr] "I" _SFR_IO_ADDR(MCUCR), \
 [bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
 [not_bodse] "i" (~_BV(BODSE))); \
}

/////////////////////////////////////////////////////////////////////////////
/*

Author: Tristan Pang

This program was designed for an Arduino stalker. Has to be in Arduino 1.0.6
It tests for conductivity (using: 
https://www.sparkyswidgets.com/portfolio-item/miniec-i2c-ec-interface/ )
every hour and interrupts and put to sleep in between, to save battery.
All data is written onto an SD card. Battery status is also logged in a 
separate file.

Visit my website: http://quest-is-fun.org.nz/

Libraries used:
*DS3231-master
*Onewire
*Pstring
*SDFat-Master
*SeedStalkerBattery
*MCP3221-Library-Master
*/
/////////////////////////////////////////////////////////////////////////////

#include <Battery.h>

int LEDPin=13;
int flashesforfull=10; // 1 blink =10%
int chcnt=0;

Battery battery;

//I2C Library
#include <Wire.h>
//We'll want to save calibration and configration information in EEPROM 
#include <avr/eeprom.h>
#include <MCP3221.h>
//EEPROM trigger check
#define Write_Check 0x1234

byte i2cAddress = 0x4F; // MCP3221 A5 in Dec 77 A0 = 72 A7 = 79)
 // A0 = x48, A1 = x49, A2 = x4A, A3 = x4B, 
 // A4 = x4C, A5 = x4D, A6 = x4E, A7 = x4F

//Our parameter, for ease of use and eeprom access lets use a struct
struct parameters_T
{
 unsigned int WriteCheck;
 int eCLowCal, eCHighCal;
 float eCStep;
}
 



params;

float eC, temperatute;
const int I2CadcVRef = 4948;
const int oscV = 185; //voltage of oscillator output after voltage divider in millivolts i.e 120mV (measured AC RMS) ideal output is about 180-230mV range 
const float kCell = 1.0; //set our Kcell constant basically our microsiemesn conversion 10-6 for 1 10-7 for 10 and 10-5 for .1
const float Rgain = 3000.0; //this is the measured value of the R9 resistor in ohms 

static uint8_t prevSecond=0; 


unsigned long time;


MCP3221 i2cADC(i2cAddress, I2CadcVRef);


// include the SD library:
#include <SPI.h>
#include <SD.h>
const int chipSelect = 10;
 File file;

void setup()
{
 Wire.begin(0x4F); //connects I2C 
 // Serial.begin(9600); //took out all Seriel commands to save battery.
 //Lets read our Info from the eeprom and setup our params,
 //if we loose power or reset we'll still remember our settings!
 eeprom_read_block(&params, (void *)0, sizeof(params));
 //if its a first time setup or our magic number in eeprom is wro ng reset to default 
 if (params.WriteCheck != Write_Check){ 
 reset_Params();
 }
 analogReference(INTERNAL);
 //analogRead(6);
 pinMode(12,OUTPUT);
 digitalWrite(12,HIGH);
 pinMode(13,OUTPUT);
 RTC.begin();
 
 
;
////////SD CHECK///////////
 //Serial.print("Initializing SD card...");

 // see if the card is present and can be initialized:
 if (!SD.begin(chipSelect)) {
 // Serial.println("Card failed, or not present");
 // don't do anything more:
 return;
 
 }

 
 ///////////RTC//////////
 
 String time = "";
 
 DateTime now = RTC.now(); //get the current date-time 
 time += (now.year(), DEC);
 time +=('/');
 time +=(now.month(), DEC);
 time +=('/');
 time +=(now.date(), DEC);
 time +=(' ');
 time +=(now.hour(), DEC);
 time +=(':');
 time +=(now.minute(), DEC);
 time +=(':');
 time +=(now.second(), DEC);
 time +=(" ");
 
 
 
 
 
 file = SD.open("Battery.txt", FILE_WRITE);

 // if the file is available, write to it:
 if (file) { 
 file.println("-----------");
 file.println(time);
 file.close();
 }
 
 file = SD.open("EC.txt", FILE_WRITE);

 // if the file is available, write to it:
 if (file) { 
 file.println("-----------");
 file.println(time);
 file.close();
 
 
}

/////////////////////Setup Interupt////////////////////
PORTD |= 0x04; 
 DDRD &=~ 0x04;
Wire.begin();
 
 RTC.begin();
 
 attachInterrupt(0, INT0_ISR, LOW); //Only LOW level interrupt can wake up from PWR_DOWN
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
 //Enable Interrupt 
 RTC.enableInterrupts(EveryHour); //interrupt at EverySecond, EveryMinute, EveryHour
 // or this
 //RTC.enableInterrupts(18,4,0); // interrupt at (h,m,s)


}


void loop()
{
 
 // make a string for assembling the data to log:
 String dataString = "";
 
 int adcRaw = i2cADC.calcRollingAVG();
 calcEc(adcRaw);
//////////////Battery status//////////////////

char CH_status_print[][4]=
 {
 "off","on ","ok ","err"
 };
 unsigned char CHstatus = read_charge_status();//read the charge status
 dataString += " ,charge status -->";
 dataString += CH_status_print[CHstatus];
 
 battery.update();
 battery.ledflashStatus(LEDPin,flashesforfull);
 float voltage = battery.getVoltage();
 int percentage = battery.getPercentage();
 char* CS = battery.getChStatus();
 bool ch = battery.isCharging();
 if(ch) chcnt++;
 
 dataString += " ,battery: ";
 dataString += voltage;
 dataString += "V -> ";
 dataString += percentage;
 dataString += "% Charge Status: ";
 dataString += CS;
 dataString += " , ";
 
 RTC.convertTemperature(); //convert current temperature into registers
 dataString += RTC.getTemperature(); //read registers and display the temperature 
 dataString += "deg C";



//print to file
 file = SD.open("Battery.txt", FILE_WRITE);

 // if the file is available, write to it:
 if (file) { 
 file.println(dataString);
 file.close();
 
 }
 // if the file isn't open, pop up an error:
 else {
 delay(10000);
 
}

 //|||||||||||||||||||Write to Disk||||||||||||||||||||||||||||||||||
 RTC.clearINTStatus(); //This function call is a must to bring /INT pin HIGH after an interrupt.
 attachInterrupt(0, INT0_ISR, LOW); //Enable INT0 interrupt (as ISR disables interrupt). This strategy is required to handle LEVEL triggered interrupt
 
 
 
 
 //\/\/\/\/\/\/\/\/\/\/\/\/Sleep Mode and Power Down routines\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
 
 //Power Down routines
 cli(); 
 sleep_enable(); // Set sleep enable bit
 sleep_bod_disable(); // Disable brown out detection during sleep. Saves more power
 sei();
 
 // Serial.println("\nSleeping");
 delay(500); //This delay is required to allow print to complete
 //Shut down all peripherals like ADC before sleep. Refer Atmega328 manual
 power_all_disable(); //This shuts down ADC, TWI, SPI, Timers and USART
 sleep_cpu(); // Sleep the CPU as per the mode set earlier(power down) 
 sleep_disable(); // Wakes up sleep and clears enable bit. Before this ISR would have executed
 power_all_enable(); //This shuts enables ADC, TWI, SPI, Timers and USART
 delay(10); //This delay is required to allow CPU to stabilize
 // Serial.println("Awake from sleep"); 
 
 //\/\/\/\/\/\/\/\/\/\/\/\/Sleep Mode and Power Saver routines\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\



}

//Lets read our raw reading while in our lower eC cal solution
void calibrateeCLow(int calnum)
{
 params.eCLowCal = calnum;
 calceCSlope();
 //write these settings back to eeprom 
 eeprom_write_block(&params, (void *)0, sizeof(params)); 
}

//Lets read our raw reading while in our higher eC cal solution
void calibrateeCHigh(int calnum)
{
 params.eCHighCal = calnum;
 calceCSlope();
 //write these settings back to eeprom
 eeprom_write_block(&params, (void *)0, sizeof(params));
}

//This is really the heart of the calibration process
void calceCSlope ()
{
 //RefVoltage * our deltaRawpH / 12bit steps *mV in V / OP-Amp gain /pH step difference 7-4
 params.eCStep = ((((I2CadcVRef*(float)(params.eCLowCal - params.eCHighCal)) / 4095) * 1000));
}

//////////Reads conductivity meter///////////
void calcEc(int raw)
{
 float temp, tempmv, tempgain, Rprobe;
 tempmv = (float)i2cADC.calcMillivolts(raw);
 tempgain = (tempmv / (float)oscV) - 1.0; // what is our overall gain again so we can cal our probe leg portion
 Rprobe = (Rgain / tempgain); // this is our actually Resistivity
 temp = ((1000000) * kCell) / Rprobe; // this is where we convert to uS inversing so removed neg exponant
 eC = temp / 1000.0; //convert to EC from uS
 String dataString ="" ;
 dataString +="";
 dataString += ",";
 
 
 dataString +="," ;
 dataString += "conductivity = ";
 dataString += eC;
 dataString += " EC (mS/m)";
 
 
 
 
 
 
 //write to SD
 file = SD.open("EC.txt", FILE_WRITE);

 // if the file is available, write to it:
 if (file) {
 file.println(dataString);
 file.close();
 // print to the serial port too:
 // Serial.println(dataString);
 }
 // if the file isn't open, pop up an error:
 else {
 // Serial.println("error opening EC.txt");
 // delay(10000);
}

}

 
 
 
//Interrupt service routine for external interrupt on INT0 pin conntected to /INT
void INT0_ISR()
{
 //Keep this as short as possible. Possibly avoid using function calls
 
 // Serial.println(" -----External Interrupt detected--------");
 detachInterrupt(0); 
}


/////////////Battery Settings///////////////
 
 unsigned char read_charge_status(void)
{
 unsigned char CH_Status=0;
 unsigned int ADC6=analogRead(6);
 if(ADC6>900)
 {
 CH_Status = 0;// sleeping
 }
 else if(ADC6>550)
 {
 CH_Status = 1;//charging
 }
 else if(ADC6>350)
 {
 CH_Status = 2;//done
 }
 else
 {
 CH_Status = 3;//error
 }
 return CH_Status;
}

//This just simply applies defaults to the params incase the need to be reset or
//they have never been set before (!magicnum) 
void reset_Params(void)
{
 //Restore to default set of parameters!z
 params.WriteCheck = Write_Check;
 params.eCLowCal = 200; //assume ideal probe and amp conditions 1/2 of 4096
 params.eCHighCal = 1380; //using ideal probe slope we end up this many 12bit units away on the 4 scale
 eeprom_write_block(&params, (void *)0, sizeof(params)); //write these settings back to eeprom
}