#include "ADC.h"
#include <SdFat.h>
#include <SdFatUtil.h>
#include <Bounce.h>
#include <Time.h>
//sdFat Setup
SdFat sd;
SdFile file;
#define SD_CHIP_SELECT 10 // SD chip select pin
#define error(s) sd.errorHalt_P(PSTR(s))
#define FILE_BASE_NAME "LOG" //Can be max size of 6 character
#define TMP_FILE_NAME "TMP_LOG.BIN"
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
const uint32_t FILE_BLOCK_COUNT = 10000UL; //500=250kb
const uint32_t ERASE_SIZE = 262144L;
uint32_t bgnBlock, endBlock;
uint8_t* pCache;
char binName[13] = FILE_BASE_NAME "00.BIN";
//Pins
#define LED 6 //recording signal
#define RECORDBUTTON 7
#define FULL 8 //used to signal a full file and turn off recording
const int battery = A3; // ADC1
const int ledPin = 13;
//Interval Timer
IntervalTimer timer0;
// Variables
const int BUFF = 500;
float average = 0.000000;
int i=0;
float sum = 0.000000;
float offset = 0.000000;
int buffer [BUFF];
float SDBuffer [128];
int count = 0;
uint32_t bn = 1; //block number
int batteryVoltage = 0;
//boolean setup
boolean recordValue = false;
int ledValue = LOW;
//Button Setup
Bounce bouncer = Bounce( RECORDBUTTON,5 ); //5ms delay
//ADC object
int value = ADC_ERROR_VALUE;
ADC *adc = new ADC();
//Flash LED error setup
#define LONG_INTERVAL 500 // number of mills to wait, for a longish time
#define SHORT_INTERVAL 100 // number of mills to wait, short time.
//------------------------------------------------------------------------------
/*
* User provided date time callback function.
* See SdFile::dateTimeCallback() for usage.
*/
void dateTime(uint16_t* date, uint16_t* time) {
// User gets date and time from GPS or real-time
// clock in real callback function
time_t now();
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(year(), month(), day());
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(hour(), minute(), second());
}
time_t getTeensy3Time()
{
return Teensy3Clock.get();
}
void createFile(){
bn=1; //reset block count
count=0;
// Find unused file name.
if (BASE_NAME_SIZE > 6) {
sendError(3,1);
}
while (sd.exists(binName)) {
if (binName[BASE_NAME_SIZE + 1] != '9') {
binName[BASE_NAME_SIZE + 1]++;
}
else {
binName[BASE_NAME_SIZE + 1] = '0';
if (binName[BASE_NAME_SIZE] == '9') sendError(3,1);
binName[BASE_NAME_SIZE]++;
}
}
// delete old log file
if (sd.exists(TMP_FILE_NAME)) {
// Serial.println(F("Deleting tmp file"));
if (!sd.remove(TMP_FILE_NAME)) sendError(3,1);
}
// create new file
file.close();
if (!file.createContiguous(sd.vwd(),
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) sendError(3,1);
// get address of file on SD
if (!file.contiguousRange(&bgnBlock, &endBlock)) sendError(3,1);
// use SdFat's internal buffer
pCache = (uint8_t*)sd.vol()->cacheClear();
if (pCache == 0) sendError(3,1);
memset(pCache, '0', 512);
// flash erase all data in file
uint32_t bgnErase = bgnBlock;
uint32_t endErase;
while (bgnErase < endBlock) {
endErase = bgnErase + ERASE_SIZE;
if (endErase > endBlock) endErase = endBlock;
if (!sd.card()->erase(bgnErase, endErase)) sendError(3,1);
bgnErase = endErase + 1;
}
}
void flashLED(byte count, int interval) {
// Just what it says: it flashes the LED 'count' times for
// the given on/off interval each time.
byte j;
for (j=0; j<count; j++) {
digitalWrite(LED, HIGH);
delay(interval);
digitalWrite(LED, LOW);
delay(interval);
}
}
void sendError(byte errorGroup, byte errorItem) {
// Flashes the LED count times, then pauses, then repeats.
// Used to tell the user what the problem is.
while (true) {
flashLED(errorGroup, SHORT_INTERVAL);
delay(LONG_INTERVAL/2);
flashLED(errorItem, SHORT_INTERVAL);
delay(LONG_INTERVAL);
}
}
void checkBattery(){
while (i<1000){
sum += adc->analogRead(battery, ADC_1);
i++;
delay(1);
}
batteryVoltage=sum/1000;
if (batteryVoltage < 1500) //1497=3.7V
{
sendError(3,1);
}
sum=0;
i=0;
}
void timerCallback0(){
for (int b=0; b<i; b++)
{
sum += buffer [b]; //Sum samples
}
average = sum/i;
//Serial.println(i);
i=0;
sum=0;
if (count < 128){ //since using float values, 128x4=512bytes
SDBuffer[count++] = average;
}
if (count==128){
noInterrupts();
memcpy(pCache, &SDBuffer, 512);
if (!sd.card()->writeData((uint8_t*)pCache)) ;
bn++;
count = 0;
memset(pCache, '0', 512);
interrupts();
}
Serial.print('V');
Serial.println(average,6);
//stop recording if file is full
if (bn == FILE_BLOCK_COUNT) digitalWrite(FULL,HIGH); //pulse digital pin to mimic buttonpush
}
void setup() {
Serial.begin(115200);
setSyncProvider(getTeensy3Time);
SdFile::dateTimeCallback(dateTime);
pinMode(A10, INPUT); //Diff Channel 0 Positive
pinMode(A11, INPUT); //Diff Channel 0 Negative
pinMode(battery, INPUT); //single channel to read battery voltage
pinMode(RECORDBUTTON, INPUT);
pinMode(ledPin, OUTPUT);
pinMode(LED, OUTPUT);
pinMode(FULL, OUTPUT);
digitalWrite(FULL, LOW);
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) {
sd.initErrorHalt();
}
//load cell voltage is ~2.3V...so maybe can't use internal
//maybe not affected using differential?
// adc->setReference(ADC_REF_INTERNAL, ADC_0); //change all 3.3 to 1.2 if you change the reference
//adc->setReference(ADC_REF_EXTERNAL, ADC_0); //change all 3.3 to 1.2 if you change the reference
adc->enablePGA(6, ADC_0);
adc->setAveraging(32, ADC_0); // set number of averages
adc->setResolution(13, ADC_0); // set bits of resolution
//Battery voltage reading on A3
adc->setAveraging(32, ADC_1); // set number of averages
adc->setResolution(12, ADC_1); // set bits of resolution
// always call the compare functions after changing the resolution!
//adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_1), 0, ADC_1); // measurement will be ready if value < 1.0V
//adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V
delay(500);
while (i<1000){
sum += adc->analogRead(battery, ADC_1);
i++;
delay(1);
}
batteryVoltage=sum/1000;
delay(2000);
Serial.println(batteryVoltage);
if (batteryVoltage < 1500) //1497=3.7V
{
sendError(3,1);
}
sum=0; //reset to calulate offset
i=0;
createFile(); //create first file, since after this only make file after collection stops
//adc->enableInterrupts(ADC_0); // enable interrupts BEFORE calling a reading function!
adc->startContinuousDifferential(A10, A11, ADC_0);
digitalWrite(LED,HIGH); //indicate calibrating of loadcell
//Calibration/Offset Routine
while (i<2000){
sum += adc->analogReadContinuous(ADC_0);
i++;
delay(1);
}
digitalWrite(LED,LOW);
offset = sum/2000;
delay(1000);
Serial.println(offset,6);
i=0;
sum=0;
}
void loop() {
if ( bouncer.update() ) {
if ( bouncer.read() == HIGH) {
if ( ledValue == LOW ) {
if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) sendError(3,1);
recordValue = true;
ledValue = HIGH;
timer0.begin(timerCallback0, 5000); //start timer 5000=200sps
}
else{
timer0.end(); //Turn off timer to stop SD card write
recordValue = false;
digitalWrite(FULL,LOW);
if (!sd.card()->writeStop())sendError(3,1);
//file.close();
ledValue = LOW;
// Truncate file if recording stopped early.
if (bn != FILE_BLOCK_COUNT) {
if (!file.truncate(512L * bn)) sendError(3,1);
}
if (!file.rename(sd.vwd(), binName)) sendError(3,1);
//checkBattery(); //dbl check to make sure working
createFile();
}
digitalWrite(LED,ledValue); //to indicate file recording
}
}
if (i<BUFF & recordValue == true){
buffer[i++] = adc->analogReadContinuous(ADC_0) - offset;
}
delayMicroseconds(100);
}
void adc0_isr(void) {//raised on each ADC conversion, need to be enabled?
// Low-level code
ADC0_RA;
//GPIOC_PTOR = 1<<5; //toggles LED on/off
// High-level code
//adc->analogReadContinuous(ADC_0); // read to clear the COCO flag
//digitalWrite(ledPin, !digitalRead(ledPin)); // toggle led
}
#if defined(__MK20DX256__)
void adc1_isr(void) {
// Low-level code
ADC1_RA;
GPIOC_PTOR = 1<<5;
// High-level code
//adc->analogReadContinuous(ADC_1); // read to clear the COCO flag
//digitalWrite(ledPin, !digitalRead(ledPin)); // toggle led
}
#endif