Go Down

Topic: Reading DS1307 RTC after sleeping (Read 1 time) previous topic - next topic

Eheran

I want to interrupt sleep, log something once per second, get a timestamp onto it and sleep again.
After going to a sleep mode like "LowPower.powerDown" and waking up again the I2C communication is dead.
I tryed a few things to get it working but nothing helps so far, only if there is no sleepmode at all it keeps working.
Google does give this but that didnt work for me or I didnt implement it correctly.

How could I get the I2C up and running again? Preferably as quickly as possible, good would be below 50ms and perfect below 1ms.
For the SD-card (SPI) I had to use a different library and had no more problems when switching it off and on via MOSFET just once every 10 sec.

This is the example out of the library im using, so there is no sleep. But if I would simply make it sleep and interrupt via the 1Hz output of the RTC it would be almost identical to my situation.
Code: [Select]
#include <Wire.h>
#include <Time.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

void setup() {
  Serial.begin(9600);
  while (!Serial) ; // wait for serial
  delay(200);
  Serial.println("DS1307RTC Read Test");
  Serial.println("-------------------");
}

void loop() {
  tmElements_t tm;

  if (RTC.read(tm)) {
    Serial.print("Ok, Time = ");
    print2digits(tm.Hour);
    Serial.write(':');
    print2digits(tm.Minute);
    Serial.write(':');
    print2digits(tm.Second);
    Serial.print(", Date (D/M/Y) = ");
    Serial.print(tm.Day);
    Serial.write('/');
    Serial.print(tm.Month);
    Serial.write('/');
    Serial.print(tmYearToCalendar(tm.Year));
    Serial.println();
    String datestring = "";                             // make a string for assembling the data to log
    datestring = String(tmYearToCalendar(tm.Year)) + "." + String(tm.Month) + "." + String(tm.Day) + " " + String(tm.Hour) + ":" + String(tm.Minute) + ":" + String(tm.Second);
    Serial.println(datestring);
  } else {
    if (RTC.chipPresent()) {
      Serial.println("The DS1307 is stopped.  Please run the SetTime");
      Serial.println("example to initialize the time and begin running.");
      Serial.println();
    } else {
      Serial.println("DS1307 read error!  Please check the circuitry.");
      Serial.println();
    }
    delay(9000);
  }
  delay(1000);
}

void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

pylon

#1
Jan 09, 2017, 06:00 pm Last Edit: Jan 09, 2017, 06:01 pm by pylon
This is not the code you're using for the sleep. Post that code because your error is probably in the wakeup code.

Also post a wiring diagram.

Eheran

The wake up is simply a interrupt. So there is no wakeup code.
It all works except for the RTC.
Its hooked up via I2C, no pullups, and workes like that in this sketch when the sleep is commented out.

Code: [Select]
#include <Wire.h>
#include <Time.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

#include <SdFat.h>
#include <SPI.h>
#include <LowPower.h>
const int chipSelect = 10;
SdFat SD;
int val = 0;

const int dPin = 2; // interrupt 0


String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
bool fertig = 1;
int warten = 0;
bool linefeed = 0;
bool an = 1;
int num;

char inChar;
boolean newData = false;
String datestring = "";

void setup()  {
  pinMode(5, OUTPUT);
  Serial.begin(9600);                                   // open serial port, set the baud rate as 9600 bps

  inputString.reserve(512);         // reserve 512 bytes for the inputString:
  delay(250);

  pinMode(dPin, INPUT);
  pinMode(8, OUTPUT);         //power for RTC, was on Vcc befor too, its I2C is supposed to recover when its powercycled and digital Pin has enough current to power it
  digitalWrite(8, HIGH);
  Serial.println("an");
  Serial.flush();
}

void loop() {
  recvoneChar();


  if (an) {         //on first start up sleep and wait for signal
    attachInterrupt(0, recvoneChar, CHANGE);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    detachInterrupt(0);
    an = 0;
  }


  if ((millis() - warten) > 50) {    //dont sleep while the data comes in
    val++;
    if (val == 10) {    //count 10x data for lower SD-card access -> lower power consumption
      stringComplete = true;
      val = 0;
    }
    if (stringComplete) {   //print the 10 records to the SD-card
      digitalWrite(5, HIGH);
      if (!SD.begin(chipSelect)) {                            // see if the card is present and can be initialized:
        Serial.println("Card failed or not present");
        delay(50);
        return;
      }
      File dataFile = SD.open("log.txt", FILE_WRITE);
      dataFile.print(inputString);
      dataFile.close();
      Serial.print(inputString);
      Serial.flush();
      inputString = "";                  // clear the string:
      stringComplete = false;
      digitalWrite(5, LOW);
    }


    attachInterrupt(0, recvoneChar, LOW);  //sleep after printing it and/or the buffer of 10 is not full yet
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    detachInterrupt(0);
    warten = millis();
    linefeed = 0;
    num = 0;

  }


}


void recvoneChar() {


  if (Serial.available() > 0) {
    inChar = Serial.read();
    num++;

    tmElements_t tm;

    if (RTC.read(tm)) {
      datestring = String(tmYearToCalendar(tm.Year)) + "." + String(tm.Month) + "." + String(tm.Day) + " " + String(tm.Hour) + ":" + String(tm.Minute) + ":" + String(tm.Second);
      //      Serial.print(datestring);
    }    else {
      if (RTC.chipPresent()) {  //this never shows up, so the I2C itself failed
        Serial.println("The DS1307 is stopped.  Please run the SetTime");
        Serial.println("example to initialize the time and begin running.");
        Serial.println();
      } else {
        Serial.println("DS1307 read error!  Please check the circuitry.");
        Serial.println();
      }
      delay(2000);
    }
String(tm.Day) + " " + String(tm.Hour) + ":" + String(tm.Minute) + ":" + String(tm.Second);
    Serial.println(datestring);
    if (num > 2) {
      if (!linefeed) {
        linefeed = 1;
        inputString += '\r';
        inputString += '\n';
        inputString += datestring;                   // add timestamp
        inputString += ",";
      }
      inputString += inChar;                   // add it to the inputString:
    }
  }
  digitalWrite(5, LOW);
}

pylon

I guess the problem is your interrupt handler. Set an empty routine as the interrupt handler but NEVER send anything to the serial interface in an interrupt service routine (the serial interface uses interrupts to work and inside ISRs interrupts are disabled).

Eheran

Holy moly!
That did it!
I simply have a empty loop now:
Code: [Select]
   
attachInterrupt(0, interrupt, LOW);
.
.
.
void interrupt() {
}



Great. Finally the powerconsumption is low and not far too high.
Its about 4,5mA idle, mostly from the arduino and I might optimize that in hardware. If I get down low enough I could leave the 9V battery in there all the time instead of switching it off or taking it out.

7,5mA average while logging (including the thermometer itself with about 1mA). About half of the value I had befor (arduino in idle-mode with UART on). Could be halfed again in hardware.

Eheran

After working for about 7min (~300 measurements, ~3000 loops) it totally fucks up.
I found somthing about the heap getting messed up with strings, got every bit of RAM static and also measured the free memory. Its rock solid untill it suddenly crashes, so thats not it.

What could be the cause of these crashes?
I cant c&p that, you have to check the screenshot.
Code: [Select]
#include <MemoryFree.h>

#include <Wire.h>
#include <Time.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

#include <SdFat.h>
#include <SPI.h>
#include <LowPower.h>
const int chipSelect = 10;
SdFat SD;
int val = 0;

const int dPin = 2; // interrupt 0


String inputString = "";         // a string to hold incoming data
String datestring = "";          // strong for date assembly
boolean stringComplete = false;  // whether the string is complete
bool fertig = 1;
int warten = 0;
bool linefeed = 0;
bool an = 1;
int num;

char inChar;
boolean newData = false;


void setup()  {
  Serial.begin(9600);     

  inputString.reserve(312);         // reserve 312 bytes for the inputString
  datestring.reserve(32);
  delay(250);

  pinMode(5, OUTPUT);         //power to SD
  digitalWrite(5, LOW);
  pinMode(dPin, INPUT);       //interruptpin
  pinMode(8, OUTPUT);         //power for RTC
  digitalWrite(8, LOW);
  Serial.println("an");
  Serial.flush();
}

void loop() {
  recvoneChar();


  if (an) {         //on first start up sleep and wait for signal
    attachInterrupt(0, interrupt, CHANGE);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    detachInterrupt(0);
    an = 0;
  }
  digitalWrite(8, HIGH);

  if ((millis() - warten) > 70) {    //dont sleep while the data comes in
    val++;
    if (val == 10) {    //count 10x data for lower SD-card access -> lower power consumption
      stringComplete = true;
      val = 0;
    }
    if (stringComplete) {   //print the 10 records to the SD-card
      digitalWrite(5, HIGH);
      if (!SD.begin(chipSelect)) {                            // see if the card is present and can be initialized:
        Serial.println("Card failed or not present");
        delay(50);
        return;
      }
      File dataFile = SD.open("log.txt", FILE_WRITE);
      dataFile.print(inputString);
      dataFile.close();
      //      Serial.println();
      Serial.print(inputString);
      //      Serial.println();
      Serial.flush();
      inputString = "";                  // clear the string:
      stringComplete = false;
      digitalWrite(5, LOW);
    }


    //    Serial.println(",  sl");  //debug to see what happens when
    //    Serial.flush();
    attachInterrupt(0, interrupt, LOW);  //sleep after printing it and/or the buffer of 10 is not full yet
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    detachInterrupt(0);

    warten = millis();
    //    Serial.print("wk  ,");
    linefeed = 0;
    num = 0;

  }


}


void recvoneChar() {


  if (Serial.available() > 0) {
    inChar = Serial.read();
    num++;

    tmElements_t tm;
    if (RTC.read(tm)) {
      datestring = String(tmYearToCalendar(tm.Year)) + "." + String(tm.Month) + "." + String(tm.Day) + " " + String(tm.Hour) + ":" + String(tm.Minute) + ":" + String(tm.Second);
      //      Serial.print(datestring);
    }    else {
      if (RTC.chipPresent()) {  //this never shows up, so the I2C itself failed
        Serial.println("The DS1307 is stopped.  Please run the SetTime");
        Serial.println("example to initialize the time and begin running.");
        Serial.println();
      } else {
        Serial.println("DS1307 read error!  Please check the circuitry.");
        Serial.println();
      }
      delay(2000);
    }

    if (num > 2) {
      if (!linefeed) {
        linefeed = 1;
        inputString += '\r';
        inputString += '\n';
        inputString += datestring;                   // add timestamp
        inputString += ",";
        Serial.print("freeMemory()=");
        Serial.println(freeMemory());
      }
      inputString += inChar;                   // add it to the inputString:
    }
    Serial.println(inChar);
  }
  digitalWrite(5, LOW);
}

void interrupt() {
}

PaulS

Quote
got every bit of RAM static
That is IMPOSSIBLE since you still use the String class. If you do not believe that, LOOK at WString.cpp, and see what the += operator is defined to do.
The art of getting good answers lies in asking good questions.

Eheran

The two strings have a fix size of 312 and 32 bytes.
The free memory is not moving one byte.

I dont know if my statement is really correct like that, but I dont see RAM issues here. Reducing the size of the large string (down from 512 bytes) didnt change a thing.

PaulS

Quote
The two strings have a fix size of 312 and 32 bytes.
So, you don't need Strings. You are just being lazy using them. I see.
The art of getting good answers lies in asking good questions.

Eheran

#9
Jan 11, 2017, 08:12 pm Last Edit: Jan 12, 2017, 12:54 am by Eheran
Could u give me some constructive feedback?
Its not like I have experience or anything, I program since November 2016 and its just a hobby to realise things I couldnt do befor. I dont know other ways to get accumulated data on the SD-card.

Edit: Is the/a string really the problem? I used the 512 Byte one for days worth of data (not just 7min), same size, without the RTC timestamp but millis().

Edit2: Added pictures of the glitch happening on the scope. Ch1 is the signal, Ch2 is pin9 going low whenever the arduino is wake and high right befor it sleeps. In normal operation its interrupted, wake for the set 50ms and back to sleep. In faulty operation it doesnt wake up, incoming data however is perfectly fine.

pylon

Quote
Could u give me some constructive feedback?
What Paul means: the String class is a piece of shit for microcontroller without a memory management unit and only 2kB of RAM. It fragments your memory in no time, you really shouldn't use it for a sketch that is expected to run more than a few seconds. Instead of building your output string you could simply call Serial.print() several times and finish the line with a Serial.println().
For the input string you could use a character array and I hope you don't need 512 bytes for it, that a quarter of the whole RAM this little processor has.

To save more memory, replace all calls to a print() or println() method with constant strings as

Code: [Select]
Serial.print("This is a constant string");

by one with the F() macro:

Code: [Select]
Serial.print(F("This is a constant string"));

If you use the F() macro the string is submitted to the serial interface directly out of the flash memory. If you don't use it, the constant string is copied to RAM at sketch start and that memory is never released.

Quote
Edit: Is the/a string really the problem? I used the 512 Byte one for days worth of data (not just 7min), same size, without the RTC timestamp but millis().
There's a high probability that it is.

Quote
Edit2: Added pictures of the glitch happening on the scope. Ch1 is the signal, Ch2 is pin9 going low whenever the arduino is wake and high right befor it sleeps. In normal operation its interrupted, wake for the set 50ms and back to sleep. In faulty operation it doesnt wake up, incoming data however is perfectly fine.
Please describe in more detail what these pictures show. Ch1 is what signal? Ch2 is going low but where is low in that picture? I don't know your scope's output.

Eheran

#11
Jan 14, 2017, 01:02 am Last Edit: Jan 14, 2017, 01:12 am by Eheran
In the currently working version its the exact same string. All that is different is the device is not in the powerdown but idle mode to get the UART data -> no more external interrupt as that is interrupt driven.
Just finished testing that for 31h or 71000 datapoints with no problems.

I will still try and find a way to make a char array somehow. Could I do it like this to add each char to the array and print it later?
Code: [Select]
char charBuf[350];  //creat array "charBuf"
char.toCharArray(charBuf, 350)      //add "char" to my array via toCharArray()
dataFile.print(charBuf); //print after eg. 10 samples are collected, hence 350 for ~300 chars

Quote
To save more memory, replace all calls to a print() or println() method with constant strings as
There are no constant strings.
Edit: There are! From the RTC debugging, they never get called. Ill fix that.

Ch.1 is the dataline, aka Rx on the arduino, getting TTL data at 9600 baud form the device, about once every 1,6s.
Ch.2 is arduinos pin9 going low right after the interrupt and high befor sleeping.
On pic 150 that works, its up for the set 50ms and back to sleep.
Pic 146a (stitched from 3 pics) shows how it fails to wake up. Its up for a a few µs somehow, even with that small programm I have it couldnt be that fast to get threw the loop - not to mention the fact that it isnt allowed to go back to sleep untill 50ms pas...

pylon

Quote
I will still try and find a way to make a char array somehow. Could I do it like this to add each char to the array and print it later?
Your code will not work. A character array is not really a C++ object but the way strings are handled in C. It's just a bunch of bytes in consecutive order in memory with a pointer to it.

Examples of how to handle it:

Code: [Select]
char buffer[200];
buffer[0] = 0; // terminating null byte

// add a single byte
int l = strlen(buffer);
buffer[l++] = c;
buffer[l] = 0; // terminating null byte

// add a C string
strncat(buffer, "This is the string to add", 200); // 200 is the size of the buffer array


Quote
Ch.2 is arduinos pin9 going low right after the interrupt and high befor sleeping.
I cannot find the code for the handling of pin 9 in your sketch.  Please post a complete sketch.

Eheran

This is what you are looking for, it was added for debugging:
Code: [Select]
digitalWrite(9, HIGH);
attachInterrupt(0, interrupt, LOW);
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
detachInterrupt(0);
digitalWrite(9, LOW);




Can you explain this example a little? Is my understanding correct:
Code: [Select]
    //both these lines go to the top of the programm
    char buffer[200];      //creat a buffer(?) for 200 chars (incl. terminating null byte) with the name "buffer"
    buffer[0] = 0;         //terminating null byte like a initialisation?


    // add a single byte
    int 1 = strlen(buffer);   //defines the lenght of the string(?!) to be one, eg. add one char
    buffer[1++] = c;          //Counts up 1 position in "buffer" and puts "c" into this position, I would put my "inChar" here
    buffer[1] = 0;             // terminating null byte, but why fixed at position 1?


    // add a C string  -- what is a "C string"?
    strncat(buffer, "This is the string to add", 200); //Why is "buffer" suddenly a string?
    //Output would be "buffer" combined with "This is the string to add"
    //Why did u use 200 when "This is the string to add" is only 25 chars?
    //I would put my datestamp in there instead of the 2nd argument
    //My understanding: strncat(string1, string2, Maximum number of characters to be appended) --> string1 now contains string2 or, if too long, only the max. numbers of chars


What I dont see is how I could get the buffer printed to SD-card and possibly to serial for easy debugging too. Can i simply use eg. "Serial.print(buffer)"?

pylon

Quote
What I dont see is how I could get the buffer printed to SD-card and possibly to serial for easy debugging too. Can i simply use eg. "Serial.print(buffer)"?
Yes, you can do that.

Quote
//defines the lenght of the string(?!) to be one, eg. add one char
That's not a one but a lowercase *L* and it stands for length. On my system a 1 and lowercase l has different font outlines to make them distinguishable, you should choose a font in your editor that makes them distinguishable too.

The strlen() function counts the number of bytes in the string up till the null byte at the end.

Quote
Pic 146a (stitched from 3 pics) shows how it fails to wake up. Its up for a a few µs somehow, even with that small programm I have it couldnt be that fast to get threw the loop - not to mention the fact that it isnt allowed to go back to sleep untill 50ms pas...
How many micros is it up? Can you analyze the that? Do you have the Pin9 code in both locations where you call the sleep?

Go Up