Hi folks,
I have an ECG project, and my first Idea was just to have a simple datalogger with SD card. Done, its working.
Further, I wanted to add an OLED display (SSD1306 128x64), but when I included the library, and after the display init, the program has stucked in loop because after the memory allocation, there was not enough memory space left.
Currently I'm at 55% of program space, and 44% of dynamic memory space.
Can somebody check my code for available reducible dynamic memory space?
/*
* ToDo: Update bootloader of Nano
* Portable ECG
* Author: yoman91
* RTC setting command on UART: "$yymmdd_hhmmss\n"
* RTC time checking command on UART: "#/n"
*
* LEDs description:
* Green ON, Red OFF: Normal operation
* Green ON, Red ON: SD Card problem
* Green OFF, Red ON: Leads Off detection
* Green OFF, Red OFF: Sleep mode/No power
*
* Output:
* GreenLed
* RedLed
* Power switching output
* Naming of SD file: 8.3 conversion restricted: hhmmss.csv
*/
extern "C"{
#include "DS1337.h"
}
#include <SPI.h>
#include <SD.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
File myFile;
uint8_t sensorPin = A0, triggerPin = 2, buttonPin = 3, greenLEDPin = 6, redLEDPin = 7;
uint8_t leadOff_neg = 8, leadOff_pos = 9, powerOutPin = 10, sensorValue = 0;
uint8_t second = 0, minute = 0, hour = 12, day = 1, month = 1, year = 21;
uint8_t lcdcursor = 0;
String bufferString = "";
char buf[10];
char fileName[12]; //, fileName = "";
struct FLAG
{
uint8_t serialInputFinished :1;
uint8_t setsTime :1;
uint8_t sendRTCTime :1;
volatile uint8_t writeFile :1;
volatile uint8_t trigger :1;
volatile uint8_t sleep :1;
}flag = {false, false, false, false, false, false};
ISR(TIMER1_COMPA_vect)
{
/* 10ms timer interrupt */
TCNT1 = 0;
flag.writeFile = true;
}
ISR(INT1_vect)
{
/* Button handling interrupt for wake up */
/* Add a switch between D3 and GND. */
/* If the switch is not in ON state */
if(digitalRead(buttonPin))
{
// Wake up
flag.sleep = false;
}
else /* If the switch is not in OFF state */
{
flag.sleep = true;
}
}
ISR(INT0_vect)
{
/* Button handling interrupt for trigger */
/* Add a push button between D2 and GND. If it pressed, then this interrupt fired */
flag.trigger = true;
}
void setup()
{
/* Data direction definition */
pinMode(sensorPin, INPUT); pinMode(leadOff_pos, INPUT); pinMode(leadOff_neg, INPUT); pinMode(buttonPin, INPUT_PULLUP);
pinMode(greenLEDPin, OUTPUT); pinMode(redLEDPin, OUTPUT); pinMode(powerOutPin, OUTPUT); pinMode(triggerPin, INPUT_PULLUP);
/* Set Timer to 10 ms */
TCCR1B |= (1 << CS11); //Set CS11 to 1 so we get prescalar 8
TIMSK1 |= (1 << OCIE1A); //Set OCIE1A to 1 so we enable compare match A
OCR1A = 20000; //10ms - 20000; 1ms = 2000; 0.5ms = 1000
/* Set up interrupt INT1 */
EICRA = (1 << ISC10) | (1 << ISC01); //Any logical change INT1, falling INT0
EIMSK = (1 << INT1) | (1 << INT0); //Enable external interrupts
/* Init UART */
Serial.begin(9600);
while (!Serial) { ; } // wait for serial port to connect. Needed for native USB port only
bufferString.reserve(20); // reserve 20 bytes for the bufferString:
/* Read out current time from RTC and save to eeprom */
year = read_year(); // eeprom_update_byte(&year, read_year_byte());
month = read_month(); // eeprom_update_byte(&month, read_month());
day = read_date(); // eeprom_update_byte(&day, read_date());
hour = read_hour(); // eeprom_update_byte(&hour, read_hour());
minute = read_minute(); // eeprom_update_byte(&minute, read_minute());
second = read_second(); // eeprom_update_byte(&second, read_second());
/* Create filename */
// if(hour < 10) {fileName = "0" + String(hour); }
// else {fileName = String(hour); }
// if(minute < 10) {fileName += "0" + String(minute);}
// else {fileName += String(minute); }
// if(second < 10) {fileName += "0" + String(second);}
// else {fileName += String(second); }
// fileName += ".csv";
sprintf(fileName, "%u%u%u%u%u%u.csv",
((hour < 10) ? 0 : hour / 10), // ((eeprom_read_byte(&hour) < 10) ? 0 : eeprom_read_byte(&hour) / 10),
( hour % 10), // ( eeprom_read_byte(&hour) % 10),
((minute < 10) ? 0 : minute / 10), // ((eeprom_read_byte(&minute) < 10) ? 0 : eeprom_read_byte(&minute) / 10),
( minute % 10), // ( eeprom_read_byte(&minute) % 10),
((second < 10) ? 0 : second / 10), // ((eeprom_read_byte(&second) < 10) ? 0 : eeprom_read_byte(&second) / 10),
( second % 10)); // (eeprom_read_byte(&second) % 10));
/* Define watchdog */
wdt_enable(WDTO_2S);
/* Init SD card */
SdFile::dateTimeCallback(dateTime);
if (!SD.begin(4)) // chip select = D4
{
//initialization failed
digitalWrite(redLEDPin, HIGH);
digitalWrite(greenLEDPin, HIGH);
wdt_disable();
while (1);
}
/* Write header to SDcard */
myFile = SD.open(fileName, FILE_WRITE);
if (myFile)
{
myFile.println(F("SensorValue\tTrigger"));
myFile.close();
}
digitalWrite(powerOutPin, HIGH);
sei();
}
void loop()
{
wdt_reset();
digitalWrite(redLEDPin, LOW);
digitalWrite(greenLEDPin, LOW);
if(flag.sleep)
{
Sleep();
}
/* Set RTC time from UART */
if(flag.serialInputFinished && flag.setsTime)
{
/* parse bufferString, which is like "$210314_205612\n" */
year = bufferString.substring(1, 3).toInt(); // eeprom_update_byte(&year, bufferString.substring(1, 3).toInt());
month = bufferString.substring(3, 5).toInt(); // eeprom_update_byte(&month, bufferString.substring(3, 5).toInt());
day = bufferString.substring(5, 7).toInt(); // eeprom_update_byte(&day, bufferString.substring(5, 7).toInt());
hour = bufferString.substring(8, 10).toInt(); // eeprom_update_byte(&hour, bufferString.substring(8, 10).toInt());
minute = bufferString.substring(10, 12).toInt(); // eeprom_update_byte(&minute, bufferString.substring(10, 12).toInt());
second = bufferString.substring(12, 14).toInt(); // eeprom_update_byte(&second, bufferString.substring(12, 14).toInt());
//set_time_and_date(eeprom_read_byte(&year), eeprom_read_byte(&month), eeprom_read_byte(&day), eeprom_read_byte(&hour), eeprom_read_byte(&minute), eeprom_read_byte(&second));
set_time_and_date(year, month, day, hour, minute, second);
bufferString = "";
flag.serialInputFinished = false;
flag.setsTime = false;
}
/* Send RTC time to UART */
if(flag.sendRTCTime && flag.serialInputFinished)
{
sendRTCTime();
bufferString = "";
flag.sendRTCTime = false;
flag.serialInputFinished = false;
}
/* Leads OFF detection */
// if( (digitalRead(leadOff_neg) == 1) || (digitalRead(leadOff_pos) == 1) )
// {
// digitalWrite(redLEDPin, HIGH);
// wdt_disable();
// while(1);
// }
/* Write data to SD card */
if(flag.writeFile)
{
digitalWrite(greenLEDPin, HIGH);
sensorValue = analogRead(sensorPin);
/* Write data to SDcard */
myFile = SD.open(fileName, FILE_WRITE);
if (myFile)
{
sprintf(buf, "%u\t%u", sensorValue, flag.trigger);
myFile.println(buf);
myFile.close();
flag.trigger = false;
}
else
{
// failed to open
digitalWrite(redLEDPin, HIGH);
digitalWrite(greenLEDPin, HIGH);
}
flag.writeFile = false;
}
}
void serialEvent()
{
while (Serial.available())
{
char inChar = (char)Serial.read(); // get the new byte:
bufferString += inChar; // add it to the bufferString
if(inChar == '$')
{
// request to set the rtc time
flag.setsTime = true;
}
else if(inChar == '#')
{
// send out the rtc time on uart
flag.sendRTCTime = true;
}
else if (inChar == '\n')
{
flag.serialInputFinished = true;
}
else
{
// Do nothing
}
}
}
void sendRTCTime(void)
{
Serial.println(String(read_year()) + '.' + String(read_month()) + '.'+ String(read_date()) + '_' + String(read_hour()) + ':' + String(read_minute()) + ':' + String(read_second()));
}
void Sleep(void)
{
digitalWrite(greenLEDPin, LOW);
digitalWrite(redLEDPin, LOW);
digitalWrite(powerOutPin, LOW);
/* Define the sleep mode - max power consumption */
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
cli();
sleep_enable();
wdt_disable();
sei();
sleep_cpu();
sleep_disable();
wdt_enable(WDTO_1S);
delay(1500); //Force watchdog reset
/* When exiting sleep mode, first the modules has to be powered on */
digitalWrite(powerOutPin, HIGH);
}
void dateTime(uint16_t* date, uint16_t* time)
{
/* 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);
}