Hummer L.
Published © GPL3+

Temperature Datalogger for PT1000 Probes

A four-channel temperature datalogger for PT1000 probes measured by Atmega32U4 and logged on SD-card.

AdvancedFull instructions providedOver 2 days1,036
Temperature Datalogger for PT1000 Probes

Things used in this project

Hardware components

JLCPCB Customized PCB
JLCPCB Customized PCB
A specially developed PCB is needed, which is either assembled by the user or purchased assembled.
×1
PCB parts
For the complete list of components please see the attachment
×1

Software apps and online services

Arduino IDE
Arduino IDE
Autodesk EAGLE
Autodesk EAGLE
Fusion 360
Autodesk Fusion 360

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Reflow oven
For printed circuit board assembly
Solder Paste, Silver Bearing
Solder Paste, Silver Bearing

Story

Read more

Custom parts and enclosures

PCB CAD File Fusion 360

PCB CAD File Step

Case solid

Case soft

Top solid

Top soft

Schematics

Schematic Files Temp Datalogger Eagle

The schematic V2 for the PCB.
Small improvements compared to V1.

Board Files Temp Datalogger Eagle

BOM project temperature datalogger

All parts to rebuild the project

Code

V0.16_Temp_Datalogger.ino

C/C++
/* Autor: Hummer L.
   Datum: 21.4.2021
   Software fuer den Temperatur Datalogger
   Revision:
    V0.16: Temperatur curve adjusted and compared with thermoelement
    
   Anmerkung:
*/

//---------- Libraries einbinden ----------
#include <Wire.h>
#include <EEPROM.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include <Fat16.h>

//---------- I2C Adressen definieren ----------
#define oled_address 0x3C
#define rtc_address 0x52

//---------- REGISTERS RTC RV3028 ----------
//Clock registers
#define RV3028_SECONDS   0x00
#define RV3028_MINUTES   0x01
#define RV3028_HOURS     0x02
//Calendar registers
#define RV3028_WEEKDAY   0x03
#define RV3028_DATE      0x04
#define RV3028_MONTHS    0x05
#define RV3028_YEARS     0x06
#define RV3028_STATUS    0x0E

//---------- Objekte fr SD und Oled erstellen ----------
SSD1306AsciiWire oled;
SdCard sd;
Fat16 file;

//---------- Pinnummer einem Namen zuweisen ----------
#define led_g  30 // TX LED
#define led_b  17 // RX LED 
#define button1 1
#define button2 0
#define button3 7
#define temp1_enb 5
#define temp2_enb A5
#define temp3_enb 6
#define temp4_enb 8
#define temp1_adc A1
#define temp2_adc A2
#define temp3_adc A0
#define temp4_adc A3
#define bat_adc A4
#define sd_io 9
#define sd_cs 10
#define sd_enb 12
#define oled_enb 11
#define opv_enb 13



//---------- Struktur ber wichtige Infos des Datalogger ----------
struct fileinfo {
  float temp_value[4];
  bool temp_active[4];
  word _time[7]; //0..sek 1..min 2..hour 3..day 4..date 5..month 6..year
  word pasttime[7];
  volatile bool buttonstate[3];
  unsigned long starttime;
  byte delay_time; //Zeit zwischen zwei Messungen in sek / Gibt den Index der delay_choice funktion an
  bool oled_state;
  int id; //Gibt die Anzahl an Messungen an
};
fileinfo datalogger = {0};

//---------- Zusatzliche globale Variablen deklarieren ----------
#define bounce_time  50
#define eep_add_temp  0
#define eep_add_oled  1
#define eep_add_time  2

byte rows;      // Rows per line.

volatile word choice = 0;
char data[30];
byte bounce_count[3] = {0};
volatile bool wdt_status = 0;
const  word delay_choice[10] = {5, 15, 30, 60, 180, 300, 600, 900, 1800, 6000 };
byte channel_sel = 0;
char filename[] = "XX_XXLog.csv";
bool sleep_status = 0;
byte daycount = 0;
byte day_old = 0;
const byte samples = 5; //Anzahl der Messungen, aus der Anzahl wird dann das endgltige Ergebniss gemittelt, hhere Genausigkeit

void setup() {
  Wire.begin();
  Wire.setClock(400000L);


  //---------- Pin Deklaration, ob In oder Output ----------
  pinMode(led_g, OUTPUT);
  pinMode(led_g, OUTPUT);
  pinMode(temp1_enb, OUTPUT);
  pinMode(temp2_enb, OUTPUT);
  pinMode(temp3_enb, OUTPUT);
  pinMode(temp4_enb, OUTPUT);
  pinMode(sd_enb, OUTPUT); //Vorerst nicht mglich
  pinMode(sd_cs, OUTPUT);
  pinMode(oled_enb, OUTPUT);
  pinMode(opv_enb, OUTPUT);

  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  pinMode(button3, INPUT);
  pinMode(sd_io, INPUT);

  digitalWrite(sd_cs, 1);
  digitalWrite(led_g, 1);
  digitalWrite(led_b, 1);

  //---------- Start Konfiguration ----------
  if (EEPROM.read(eep_add_oled) > 1)EEPROM.write(eep_add_oled, 1);
  datalogger.oled_state = EEPROM.read(eep_add_oled);
  if (EEPROM.read(eep_add_time) > 9)EEPROM.write(eep_add_time, 0);
  datalogger.delay_time = EEPROM.read(eep_add_time);
  channel_sel = EEPROM.read(eep_add_temp);
  if (channel_sel > 9)EEPROM.write(eep_add_temp, 0b0011);
  datalogger.temp_active[0] = 0b0001 & channel_sel;
  datalogger.temp_active[1] = 0b0010 & channel_sel;
  datalogger.temp_active[2] = 0b0100 & channel_sel;
  datalogger.temp_active[3] = 0b1000 & channel_sel;
  channel_sel = 0;

  //---------- RTC Konfiguration ----------
  rtc_initalize(); //Alle anderen Werte sind default, wie 24h Format

  //---------- Timer Konfiguration ----------
  // TIMER 1 for interrupt frequency 1000 Hz:
  cli(); // stop interrupts
  TCCR1A = 0; // set entire TCCR1A register to 0
  TCCR1B = 0; // same for TCCR1B
  TCNT1  = 0; // initialize counter value to 0
  // set compare match register for 1000 Hz increments
  OCR1A = 7999; // = 8000000 / (1 * 1000) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12, CS11 and CS10 bits for 1 prescaler
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei(); // allow interrupts

  //---------- Interrupts Konfiguration ----------
  attachInterrupt(digitalPinToInterrupt(button1), int1_event, LOW);
  attachInterrupt(digitalPinToInterrupt(button2), int2_event, LOW);
  attachInterrupt(digitalPinToInterrupt(button3), int3_event, LOW);
  //gab sonst probleme, beim aktivieren der Tasterinterrupts erst alle Tastervariablen reseten
  datalogger.buttonstate[0] = 0;
  datalogger.buttonstate[1] = 0;
  datalogger.buttonstate[2] = 0;
}
void loop() {
  //bei jedem WDT aufruf, wird diese Funktion ausgefhrt, weil wdt_status in der wdt Routine 1 gesetzt wird
  if (wdt_status == 1) {
    rtc_gettime();
    oled_displaytime();
    wdt_status = 0;
  }

  //Menstruktur fr die unterschiedlichen Flle, wie Manuell, Logger oder Einstellungen
  switch (choice) {
    //---------- Startsetting erste Frage, ob hndisch, auomatik betrieb oder einstellungen ------------------------------------------------------------
    case 0:
      //---------- Watch Dog Timer Konfiguration ----------
      wdt_setE5(1);
      //---------- OLED Konfiguration ----------
      if (digitalRead(oled_enb) == 0) {
        digitalWrite(oled_enb, 1);
        oled.begin(&Adafruit128x64, oled_address);
        oled.setFont(Callibri11); // Auswahl der Schriftart
        rows = oled.fontRows();
        oled.clear();
        oled.setContrast(10);
        datalogger.pasttime[4] = 0;
        datalogger.pasttime[2] = 111;
        datalogger.pasttime[1] = 111;
        oled_displaytime();
        rtc_gettime();

      }
      //Text auf das Display ausgeben
      oled_displaybat();
      oled.setLetterSpacing(2);
      oled_print(1, -1, 0, "-----Betriebsart-----");
      oled.setLetterSpacing(1);
      choice = 1;
      break;
    case 1:
      oled_print(2, -1, 0, "Logger | Manuell | Setting");
      choice = 2;
      break;
    case 2:
      oled_print(3, -1, 0, "Logger");
      choice = 7;
      break;
    case 3:
      oled_print(3, -1, 0, "Manuell");
      choice = 8;
      break;
    case 4: //Case fr Einsellung, welche in 200 abgearbeitet werden
      oled_print(3, -1, 0, "Setting");
      choice = 9;
      break;

    case 7: //Abfrage fr den logger Betrieb
      //enter_sleep(); //Ers setzten, wenn alles fertig sonst probleme mit uart
      but_do(10, 3, 4);
      break;
    case 8://Abfrage fr den Manuellen Betrieb
      enter_sleep();
      but_do(100, 4, 2);
      break;
    case 9:// Abfrage fr den Einstellungscase
      enter_sleep();
      but_do(200, 2, 3);
      break;

    //---------- Logger Betrieb ------------------------------------------------------------
    case 10: //Logger Betrieb
      oled.clear(0, 128 , rows, 3 * rows + rows - 1);
      oled_print(1, -1, 0, "Datalogger");
      oled.setCursor(0, 2 * rows);
      if (datalogger.temp_active[0] == 1)oled.print("1 ");
      if (datalogger.temp_active[1] == 1)oled.print("2 ");
      if (datalogger.temp_active[2] == 1)oled.print("3 ");
      if (datalogger.temp_active[3] == 1)oled.print("4 ");
      sprintf(data, "On | Time: %dsek", delay_choice[datalogger.delay_time]);
      oled.print(data);
      oled_displaybat();
      oled_print(3, -1, 0, "<-Back | v Start v | -----");
      choice = 11;
      break;
    case 11: //Logger Betrieb
      enter_sleep();
      if (but_do(12, -1, 0) == 1 && digitalRead(sd_io) == 1) {
        choice = 14;
        //Startvariablen fr Loggerbetrieb vergeben
        datalogger.starttime = rtc_gettime(); //Startzeit der Messung
        datalogger.id = 1; //ID ist 1
        sd_config(); //SD initalisieren
      }
      break;
    case 12: //Case Fehler keine SD-Karte
      oled_print(2, -1, 0, "SD-Card missing");
      choice = 13;
      break;
    case 13: //Case warten, bis nochmal start gedrckt  und SD vorhanden nochmals berprfen
      enter_sleep();
      if (but_do(11, -1, 0) == 1) {
        datalogger.buttonstate[0] = 1;
      }
      break;
    case 14: //Alles ok, es kann gestartet werden
      //Je nach delayzeit anderen wdt setzen
      if (datalogger.delay_time > 2 && datalogger.delay_time <= 4) {
        wdt_set2E(1);
      }
      else if (datalogger.delay_time > 4) {
        wdt_set8E(1);
      }
      digitalWrite(oled_enb, datalogger.oled_state); //OLED an/aus, je nach konfiguraion
      oled.clear(0, 128, rows, 2 * rows + rows - 1);
      oled_displayvalue(0, 1);
      oled_print(3, 0, 0, "<-Back");
      oled_print(3, 50, 50, "ID: ");
      opv_getvalue();
      //Ersten Messwert aufnehmen
      oled_displayvalue(1, 0);
      oled_displaybat();
      sd_update();
      choice = 16;
      break;
    case 15: //Case Logger Messausfhrung
      enter_sleep();
      //Datenerfassung
      if (rtc_gettime() >= datalogger.starttime + delay_choice[datalogger.delay_time]*datalogger.id) {
        datalogger.id++;
        opv_getvalue();
        oled_displayvalue(1, 0);
        sd_update();
        oled_displaybat();
        choice = 16;
      }
      //Im Betrieb OLED an-Aus schalten und Konfigurieren
      if (but_do(-1, 15, 0) == 2) {
        datalogger.oled_state = !datalogger.oled_state;
        if (datalogger.oled_state == 1) {
          if (digitalRead(oled_enb) == 0) {
            digitalWrite(oled_enb, 1);
            oled.begin(&Adafruit128x64, oled_address);
            oled.setFont(Callibri11); // Auswahl der Schriftart
            rows = oled.fontRows();
            oled.clear();
            oled.setContrast(10);
            datalogger.pasttime[4] = 0;
            datalogger.pasttime[2] = 111;
            datalogger.pasttime[1] = 111;
            oled_displaytime();
            rtc_gettime();
            oled_displaytime();
            oled_displaybat();
            oled.clear(0, 128, rows, 3 * rows + rows - 1);
            oled_displayvalue(0, 1);

            oled_print(3, 0, 0, "<-Back");
            oled_print(3, 50, 150, "ID: ");
            sprintf(data, "%d", datalogger.id);
            oled_print(3, 65, 65, data);
          }
        }
        else {
          digitalWrite(oled_enb, 0);
        }

      }
      break;
    case 16: //Neue ID am OLED ausgeben
      sprintf(data, "%d", datalogger.id);
      oled_print(3, 65, 65, data);
      choice = 15;
      break;

    //---------- Manueller Betrieb ------------------------------------------------------------
    case 100: //Manueller Betrieb Einmalsetting
      oled.clear(0, 128, rows, 2 * rows + rows - 1);
      oled_displayvalue(0, 1);
      oled_print(3, 0, 0, "<-Back");
      choice = 101;
      break;
    case 101: //Datenerfassung + Ausgabe
      opv_getvalue();
      oled_displayvalue(1, 0);
      enter_sleep();
      but_do(-1, -1, 0);
      break;

    //---------- Settings Menstruktur ------------------------------------------------------------
    case 200:
      oled_displaybat();
      oled_print(1, -1, 0, "Settings");
      choice = 201;
      break;
    case 201:
      oled_print(2, -1, 0, "Tim | Del | Act | Dis | Back");
      choice = 206;
      break;
    case 202:
      oled_print(3, -1, 0, "Time");
      choice = 207;
      break;
    case 203:
      oled_print(3, -1, 0, "Delay");
      choice = 208;
      break;
    case 204:
      oled_print(3, -1, 0, "ADC active");
      choice = 209;
      break;
    case 205:
      oled_print(3, -1, 0, "Display");
      choice = 210;
      break;
    case 206:
      oled_print(3, -1, 0, "Back");
      choice = 211;
      break;
    case 207: //Case zur Zeiteinstellung auswahl
      enter_sleep();
      but_do(300, 203, 206);
      break;
    case 208://Case zur Einstellung der Zeit zwischen Messung
      enter_sleep();
      but_do(230, 204, 202);
      break;
    case 209: //Case Einstellung welcher Channel Aktiv
      enter_sleep();
      but_do(240, 205, 203);
      break;
    case 210://Case Oled an aus
      enter_sleep();
      but_do(250, 206, 204);
      break;
    case 211://Case zurck zum Start
      //enter_sleep();
      but_do(0, 202, 205);
      break;

    //---------- Delay Settings ------------------------------------------------------------
    case 230:
      oled_print(1, -1, 0, "Delay Settings");
      oled_print(3, -1, 0, "<- -- | v Set v | ++ ->");
      choice = 231;
      break;
    case 231:
      sprintf(data, "Delaytime: %dsek", delay_choice[datalogger.delay_time]);
      oled_print(2, 0, 0, data);
      choice = 232;
      break;
    case 232:
      enter_sleep();
      switch (but_do(200, 231, 231)) {
        case 1:
          EEPROM.update(eep_add_time, datalogger.delay_time);
          break;
        case 2:
          datalogger.delay_time++;
          if (datalogger.delay_time > 9) {
            datalogger.delay_time = 0;
          }
          break;
        case 3:
          datalogger.delay_time--;
          if (datalogger.delay_time < 0 || datalogger.delay_time > 9) {
            datalogger.delay_time = 9;
          }
          break;
      }
      break;

    //---------- Channel Aktiv Settings ------------------------------------------------------------
    case 240:
      oled_print(1, -1, 0, "ADC actice Settings");
      oled_print(3, -1, 0, "<-Back | vOn-Offv | Next->");
      choice = 241;
      break;
    case 241:
      sprintf(data, "Channel T%d: ", channel_sel + 1);
      oled_print(2, 0, 0, data);
      oled_print(2, 60, 60, datalogger.temp_active[channel_sel] ? "On" : "Off");
      choice = 242;
      break;
    case 242:
      enter_sleep();
      switch (but_do(241, 241, 200)) {
        case 1:
          datalogger.temp_active[channel_sel] = !datalogger.temp_active[channel_sel];
          break;
        case 2:
          channel_sel++;
          if (channel_sel > 3) {
            channel_sel = 0;
          }
          break;
        case 3:
          EEPROM.update(eep_add_temp, datalogger.temp_active[0] | datalogger.temp_active[1] << 1 | datalogger.temp_active[2] << 2 | datalogger.temp_active[3] << 3);
          channel_sel = 0;
          break;
      }
      break;

    //---------- Display aktiv Settings ------------------------------------------------------------
    case 250:
      oled_print(1, -1, 0, "Display Settings");
      oled_print(2, 0, 0, "Display: ");
      oled_print(3, 0, 0, "<-Back | vOn-Offv | ");

      choice = 251;
      break;
    case 251:
      oled_print(2, 46, 46, datalogger.oled_state ? "On" : "Off");
      choice = 252;
      break;
    case 252:
      enter_sleep();
      switch (but_do(251, -1, 200)) {
        case 1:
          datalogger.oled_state = !datalogger.oled_state;
          break;
        case 3:
          EEPROM.update(eep_add_oled, datalogger.oled_state);
          break;
      }
      break;

    //---------- Zeiteinstellung Settings ------------------------------------------------------------
    case 300:
      oled_print(1, -1, 0, "Time Settings");
      oled_print(3, -1, 0, "<- -- | v Next v | ++ ->");
      wdt_setE5(0);
      choice = 301;
      break;
    case 301: //Jahr ndern
      sprintf(data, "Year: %d", datalogger._time[6] + 2000);
      oled_print(2, 0, 0, data);
      choice = 302;
      break;
    case 302:
      switch (but_do(303, 301, 301)) {
        case 2:
          datalogger._time[6] ++;
          break;
        case 3:
          datalogger._time[6] --;
          break;
      }
      break;
    case 303: //Monat ndern
      sprintf(data, "Month: %d", datalogger._time[5]);
      oled_print(2, 0, 0, data);
      choice = 304;
      break;
    case 304:
      switch (but_do(305, 303, 303)) {
        case 2:
          datalogger._time[5] ++;
          break;
        case 3:
          datalogger._time[5] --;
          break;
      }
      if (datalogger._time[5] < 1 || datalogger._time[5] > 12)datalogger._time[5] = 1;
      break;
    case 305: //Tag ndern
      sprintf(data, "Day: %d", datalogger._time[4]);
      oled_print(2, 0, 0, data);
      choice = 306;
      break;
    case 306:
      switch (but_do(307, 305, 305)) {
        case 2:
          datalogger._time[4] ++;
          break;
        case 3:
          datalogger._time[4] --;
          break;
      }
      if (datalogger._time[4] < 1 || datalogger._time[4] > 31)datalogger._time[4] = 1;
      break;
    case 307: //Stunde ndern
      sprintf(data, "Hour: %d", datalogger._time[2]);
      oled_print(2, 0, 0, data);
      choice = 308;
      break;
    case 308:
      switch (but_do(309, 307, 307)) {
        case 2:
          datalogger._time[2] ++;
          break;
        case 3:
          datalogger._time[2] --;
          break;
      }
      if (datalogger._time[2] < 0 || datalogger._time[2] > 24)datalogger._time[2] = 1;
      break;
    case 309: //Minute ndern
      sprintf(data, "Minute: %d", datalogger._time[1]);
      oled_print(2, 0, 0, data);
      choice = 310;
      break;
    case 310:
      switch (but_do(311, 309, 309)) {
        case 2:
          datalogger._time[1] ++;
          break;
        case 3:
          datalogger._time[1] --;
          break;
      }
      if (datalogger._time[1] < 0 || datalogger._time[1] > 60)datalogger._time[1] = 1;
      break;
    case 311: //Setzten der zuvor eingestellten Zeit
      datalogger._time[0] = 0;
      rtc_settime();
      datalogger.pasttime[4] = 0;
      datalogger.pasttime[2] = 111;
      datalogger.pasttime[1] = 111;
      wdt_setE5(1);
      choice = 200;
      break;

    //---------- Default Fehler ------------------------------------------------------------
    default:
      choice = 0;
      oled_print(2, -1, 0, "Fehler");
      oled_print(3, -1, 0, "Restart");
      delay(2000);
      break;
  }
}
byte but_do(int16_t choice_0, int16_t choice_1, int16_t choice_2) {//---------- Tastervariablenabfrage ----------
  //Abfrage der Tastervariablen, welche in der ISR gesetzt werden und je nach Stellungen, wird die choice gendert, also die cases
  byte x = 0; //Hilfsvariable
  //Wenn Tastervariable gesetzt, die choice je nach bergabewert ndern bzw. , wenn negativ dann ignorieren und Tastervariable NULL setzen
  if (datalogger.buttonstate[0] == 1) {
    if (choice_0 >= 0) {
      choice = choice_0;
    }
    x = 1;
    datalogger.buttonstate[0] = 0;
  }
  //Wenn Tastervariable gesetzt, die choice je nach bergabewert ndern bzw. , wenn negativ dann ignorieren und Tastervariable NULL setzen
  else if (datalogger.buttonstate[1] == 1) {
    if (choice_1 >= 0) {
      choice = choice_1;
    }
    x = 2;
    datalogger.buttonstate[1] = 0;
  }
  //Wenn Tastervariable gesetzt, die choice je nach bergabewert ndern bzw. , wenn negativ dann ignorieren und Tastervariable NULL setzen
  else if (datalogger.buttonstate[2] == 1) {
    if (choice_2 >= 0) {
      choice = choice_2;
    }
    x = 3;
    datalogger.buttonstate[2] = 0;
  }
  return x; //Rckgabewert, je nach ausgefhrten case 0..keine Variabele gesetzt 1.. taster1 2..taster2 3..taster3
}
void sd_update() {//---------- Sd Karte mit Messwerte beschreiben ----------
  //String mit allen Daten, wie timestamp, ID und Messwerte bilden
  String dataString = "";
  dataString += String(datalogger.id);
  sprintf(data, ";%02d.%02d.%04d ", datalogger._time[4], datalogger._time[5], datalogger._time[6] + 2000);
  dataString += String(data);
  sprintf(data, "%02d:%02d:%02d;", datalogger._time[2], datalogger._time[1], datalogger._time[0]);
  dataString += String(data);
  //Die 4 Messwerte in String einfgen und je nach aktiv oder nicht, den Wert oder "inactice" schreiben
  for (int i = 0; i < 4; i++) {
    if (datalogger.temp_active[i] == 1) {
      sprintf(data, "%d,%d;", (int)datalogger.temp_value[i], abs((int)(datalogger.temp_value[i] * 10) % 10));
    }
    else {
      sprintf(data, "inactive;");
    }
    dataString += String(data);
  }
  digitalWrite(sd_enb, 1);
  //File append ffnen und den zuvor kreiierten String hineinschreiben
  file.open(filename, O_CREAT | O_APPEND | O_WRITE);
  if (file.isOpen()) {
    file.println(dataString);
    file.close();
  }
  digitalWrite(sd_enb, 0);
}
void sd_config() { //---------- SD Karte Konfiguration ----------
  digitalWrite(sd_enb, 1);
  if (sd.begin(sd_cs)) { //SD initalisieren
    Fat16::init(&sd);
    //Name fr die csv Datei ndern
    filename[0] = (int)(datalogger._time[2] / 10) + '0';
    filename[1] = datalogger._time[2] % 10 + '0';
    filename[3] = datalogger._time[1] / 10 + '0';
    filename[4] = datalogger._time[1] % 10 + '0';
  }
  file.open(filename, O_CREAT | O_APPEND | O_WRITE); //File ffnen mit dem zuvor genderten Namen
  if (file.isOpen()) { //Wenns File offen, dann...
    file.println("ID;Timestamp;Temp1 [*C];Temp2 [*C];Temp3 [*C];Temp4 [*C]"); //...Starttext in die erste Zeile der csv Datei schreiben
    file.close(); //File schlieen
  }
  digitalWrite(sd_enb, 0);
}
void oled_print(uint8_t row_, int8_t cur, uint8_t start_del, char text[30]) {//---------- OLED Test ausgeben und lschen ----------
  oled.clear(start_del, 128 , row_ * rows, row_ * rows + rows - 1); //bestimmte Zeile lschen
  //Auswhlen, ob Text an best. Stelle oder in der Mitte vom Display
  if (cur < 0) { //Case in der Mitte
    oled.setCursor(64 - oled.strWidth(text) / 2, row_ * rows);
  }
  else { //Case best. Stelle
    oled.setCursor(cur, row_ * rows);
  }
  oled.print(text);//Text ausgeben an der zuvorig gesetzten Stelle
}
void oled_displaytime() {//---------- OLED Zeit ausgeben ----------

  //Die aktuelle Zeit ausgeben, aber nur wenn sie sich zur vorherigen unterscheidet
  // case fr Datum
  if (datalogger._time[4] != datalogger.pasttime[4]) {
    oled.clear(0, 45,  0 * rows, rows - 1);
    sprintf(data, "%02d.%02d.%02d", datalogger._time[4], datalogger._time[5], datalogger._time[6]);
    oled.print(data);
  }
  // case fr Stunden
  if (datalogger._time[2] != datalogger.pasttime[2]) {
    oled.clear(46, 60,  0 * rows, rows - 1);
    sprintf(data, "%02d:", datalogger._time[2]);
    oled.print(data);
  }
  // case fr Minuten
  if (datalogger._time[1] != datalogger.pasttime[1]) {
    oled.clear(61, 75,  0 * rows, rows - 1);
    sprintf(data, "%02d:", datalogger._time[1]);
    oled.print(data);
  }
  // case fr Sekunden
  if (datalogger._time[0] != datalogger.pasttime[0]) {
    oled.clear(76, 90,  0 * rows, rows - 1);
    sprintf(data, "%02d", datalogger._time[0]);
    oled.print(data);
  }
  //Die aktuelle Zeit der alten Zeit zuweisen, damit er dann beim nchsten Mal wieder alt und neu berprfen kann
  memcpy(datalogger.pasttime, datalogger._time, sizeof(datalogger.pasttime));
}
void oled_displaybat() {//---------- OLED BatterieSpannung asugeben ----------
  float x = analogRead(bat_adc) * 0.026316 - 8.0263; //Spannung messen und in V umwandeln
  sprintf(data, "%d.%02dV", (int)x, (int)(x * 100) % 100); //Umwandeln in char[], weil mit float inkompatibel, zweimal x und Modulo Funktion(Trick)
  oled_print(0, 128 - oled.strWidth(data), 128 - oled.strWidth(data), data);//ausgeben am OLED
}
void oled_displayvalue(uint8_t aktual, bool preset) {//---------- OLED Messwerte ausgeben ----------
  //Hilfsvariablen erstellen
  uint8_t col_number = 0;
  uint8_t row_number = 0;
  char buf1 [6];
  char buf2 [5];
  char buf3 [4];
  //For schleife fr die 4 Messwerte
  for (uint8_t i = 0; i < 4; i++) {

    switch (i) {
      case 0:
        col_number = 0;
        row_number = 1;
        break;
      case 1:
        col_number = 1;
        row_number = 1;
        break;
      case 2:
        col_number = 0;
        row_number = 2;
        break;
      case 3:
        col_number = 1;
        row_number = 2;
        break;
    }

    sprintf(buf1, "T%d: ", (int)(i + 1));

    //Nur aktuellen Wert beschreiben, wenn berhaupt aktiv
    if (datalogger.temp_active[i] == 1) {
      if (aktual == 1) { //Aktualisierungscase nur Zahl wird ausgegeben
        //oled.clear(col_number * 65 + oled.strWidth(buf1), col_number * 65 + oled.strWidth(buf1) + oled.strWidth(buf2) , row_number * rows, row_number * rows + rows - 1);
        sprintf(buf2, "%04d.%01d", (int)datalogger.temp_value[i], abs((int)(datalogger.temp_value[i] * 10) % 10));
        oled.clear(col_number * 65 + oled.strWidth(buf1), col_number * 65 + oled.strWidth(buf1)+oled.strWidth(buf2) , row_number * rows, row_number * rows + rows - 1);
        
        oled.print(buf2);
      }
      else { //Einmalcase am Anfang, Zahl und Text wird ausgeegeben
        oled.clear(col_number * 65, col_number * 65 + 66 , row_number * rows, row_number * rows + rows - 1);
        sprintf(buf2, "%04d.%01d", (int)datalogger.temp_value[i], abs((int)(datalogger.temp_value[i] * 10) % 10));
        oled.print(buf1);
        oled.print(buf2);
        //oled.print("*C"); //Wieso Probleme????????????????????
        sprintf(buf3, "*C");
        oled.print(buf3);
      }
    }
    //Falls man die Voreinstellungen macht, wird "inactive" geschrieben, wenn der Channel inaktiv ist
    else if (preset == 1) {
      oled.clear(col_number * 65, col_number * 65 + 65 , row_number * rows, row_number * rows + rows - 1);
      oled.print(buf1);
      oled.print("inactive");
    }
  }
}
void rtc_initalize() {//---------- RTC Initalisierung ----------
  Wire.beginTransmission(rtc_address); //Kommunikation an RTC adress starten
  Wire.write(RV3028_STATUS); //Ins Status register schreiben
  Wire.write((0x00)); //0 schreiben, weil das alle bedrfnisse abdeckt
  Wire.endTransmission();
}
void rtc_settime() {//---------- RTC Zeit ndern ----------
  Wire.beginTransmission(rtc_address); //Kommunikation an RTC adress starten
  Wire.write(RV3028_SECONDS); //als erstes in sekunden register schreiben, wird auto. inkrementiert fr die anderen register
  for (byte i = 0; i < 7; i++) { //Write multiple Registers
    Wire.write(DECtoBCD(datalogger._time[i])); //Die eingestellte Zeit der RTC bergeben
  }
  Wire.endTransmission();
  oled_displaytime(); //Zeit ausgeben
}
unsigned long rtc_gettime() {//---------- RTC Zeit auslesen ----------
  Wire.beginTransmission(rtc_address); //Kommunikation an RTC adresse starten
  Wire.write(RV3028_SECONDS);// das Sekunden register auswhlen

  Wire.requestFrom(rtc_address, 7); //Dann 7Byte auslesen, also Sekunden bis Jahr
  while (Wire.available()) {
    for (byte i = 0; i < 7; i++) { //Read multiple Registers
      datalogger._time[i] = BCDtoDEC(Wire.read());
    }
  }
  Wire.endTransmission();

  // Die Zeit in Sekunden zurckgeben, damit man dann die Messzeitpunkte zwischen alt und neu abgleichen kann
  if (datalogger._time[4] != day_old) { //Die vergangenen Tag zhlen, damit mein kein Problem bei einem Monatssprung hat
    daycount ++;;
    day_old = datalogger._time[4];
  }
  return datalogger._time[0] + 60 * datalogger._time[1] + 3600 * datalogger._time[2] + 24 * 3600 * daycount;
}
byte BCDtoDEC(uint8_t val) {//---------- BCD zu DEC formatieren ----------
  return ((val / 0x10) * 10) + (val % 0x10);
}
byte DECtoBCD(uint8_t val) {//---------- DEC zu BCD formatieren ----------
  return ((val / 10) * 0x10) + (val % 10);
}
void opv_getvalue() {//---------- Temperaturspannung messen ----------
  digitalWrite(led_b, 0); //led anschalten
  digitalWrite(opv_enb, 1); //Opv aktivieren
  //Die Channels aktivieren, wie in Settings
  digitalWrite(temp1_enb, datalogger.temp_active[0]);
  digitalWrite(temp2_enb, datalogger.temp_active[1]);
  digitalWrite(temp3_enb, datalogger.temp_active[2]);
  digitalWrite(temp4_enb, datalogger.temp_active[3]);
  delay(15);

  //Den aktuellen und aktiven Channel den Hilfsvariablen zuweisen, damit mit for-schleife mglich
  for (byte i = 0; i < 4; i++) {
    datalogger.temp_value[i] = 0;
    int pin = 0;
    int pin_read = 0;
    if (datalogger.temp_active[i] == 1) {

      switch (i) {
        case 0:
          pin = temp1_enb;
          pin_read = temp1_adc;
          break;
        case 1:
          pin = temp2_enb;
          pin_read = temp2_adc;
          break;
        case 2:
          pin = temp3_enb;
          pin_read = temp3_adc;
          break;
        case 3:
          pin = temp4_enb;
          pin_read = temp4_adc;
          break;
      }
      for(byte j = 0; j < samples; j++){
      datalogger.temp_value[i] += analogRead(pin_read); //Spannung an best. Channel messen und in Temp umwandeln
      delayMicroseconds(200);
      }
      datalogger.temp_value[i] =  ((datalogger.temp_value[i]/samples)*0.153479)-27.23; //10bit ADC Wert in Temp umwandel
      digitalWrite(pin, 0); //Channel ausschalten
    }
  }

  digitalWrite(opv_enb, 0); //OPV deaktivieren
  digitalWrite(led_b, 1); //Led ausschalten
}
void int1_event() {//---------- Interrupt Event Taster 1 ----------
  if (bounce_count[0] == 0) {     //Nur wenn Entprell Routine wieder zurckgesetzt wurde, interrupt erlauben
    if (sleep_status == 1) {      //Nur wenn im Sleep mode
      sleep_disable();            //...dann Sleep mode aussschalten
      sleep_status = 0;           //...und variable zurcksetzen
    }
    datalogger.buttonstate[0] = 1;//Tastervariable setzen
    bounce_count[0] = 1;
  }
}
void int2_event() {//---------- Interrupt Event Taster 2 ----------
  if (bounce_count[1] == 0) {     //Nur wenn Entprell Routine wieder zurckgesetzt wurde, interrupt erlauben
    if (sleep_status == 1) {          //Nur wenn im Sleep mode
      sleep_disable();            //...dann Sleep mode aussschalten
      sleep_status = 0;           //...und variable zurcksetzen
    }
    datalogger.buttonstate[1] = 1;//Tastervariable setzen
    bounce_count[1] = 1;
  }
}
void int3_event() {//---------- Interrupt Event Taster 3 ----------
  if (bounce_count[2] == 0) {     //Nur wenn Entprell Routine wieder zurckgesetzt wurde, interrupt erlauben
    if (sleep_status == 1) {      //Nur wenn im Sleep mode
      sleep_disable();            //...dann Sleep mode aussschalten
      sleep_status = 0;           //...und variable zurcksetzen
    }
    datalogger.buttonstate[2] = 1;//Tastervariable setzen
    bounce_count[2] = 1;
  }
}
void wdt_setE5(bool enable) {//---------- WDT konfiguration 0,5s ----------
  wdt_reset(); // Reset Watchdog Timer
  cli();//Interrupts verhindern
  MCUSR &= ~(1 << WDRF);           /* WDT reset flag loeschen */
  WDTCSR |= (1 << WDCE) | (1 << WDE); /* WDCE setzen, Zugriff auf Presclaler etc. */
  WDTCSR = 1 << WDP0 | 1 << WDP2;  /* Prescaler auf 0.5 s */
  WDTCSR |= enable << WDIE;
  sei();//Interrupts wieder erlauben
}
void wdt_set2E(bool enable) {//---------- WDT konfiguration 2s ----------
  wdt_reset(); // Reset Watchdog Timer
  cli();//Interrupts verhindern
  MCUSR &= ~(1 << WDRF);           /* WDT reset flag loeschen */
  WDTCSR |= (1 << WDCE) | (1 << WDE); /* WDCE setzen, Zugriff auf Presclaler etc. */
  WDTCSR = 1 << WDP0 | 1 << WDP1 | 1 << WDP2; /* Prescaler auf 2.0 s */
  WDTCSR |= enable << WDIE;
  sei();//Interrupts wieder erlauben
}
void wdt_set8E(bool enable) {//---------- WDT konfiguration 8s ----------
  wdt_reset(); // Reset Watchdog Timer
  cli();  //Interrupts verhindern
  MCUSR &= ~(1 << WDRF);           /* WDT reset flag loeschen */
  WDTCSR |= (1 << WDCE) | (1 << WDE); /* WDCE setzen, Zugriff auf Presclaler etc. */
  WDTCSR = 1 << WDP0 | 1 << WDP3;  /* Prescaler auf 8.0 s */
  WDTCSR |= enable << WDIE;
  sei();  //Interrupts wieder erlauben
}
ISR(WDT_vect) {//---------- Watchdog Timer Interrupt Service Routine ----------
  wdt_status = 1;
}
void enter_sleep(void) {//---------- Sleep Funktion ----------
  sleep_status = 1;  //Status setzen, wenn Sleep Mode aktiv, wird in der ISR fr Taster verwendet
  //digitalWrite(led_g, 1);
  bounce_count[0] = 0;
  bounce_count[2] = 0;
  bounce_count[1] = 0;
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   /* Es geht auch SLEEP_MODE_PWR_DOWN */
  sleep_enable();
  power_adc_disable();    /* Analog-Eingaenge abschalten */
  power_spi_disable();    /* SPI abschalten */
  power_timer0_disable(); /* Timer0 abschalten */
  power_timer1_disable(); /* Timer1 abschalten */
  power_timer2_disable(); /* Timer2 abschalten */
  power_twi_disable();    /* TWI abschalten */
  sleep_mode();
  sleep_disable();        //Sleep mode disable
  power_all_enable();     /* Komponenten wieder aktivieren */
  //digitalWrite(led_g, 0);
  sleep_status = 0;
}
ISR(TIMER1_COMPA_vect) {//---------- Timerinterrupt  1000Hz ----------
  // Entprellen des Tasters 1, wenn gedrckt wird 1, dann bis bounce_time hochzhlen, dann taster erst wieder freigegeben
  if (bounce_count[0] >= 1) {
    bounce_count[0]++;
    if (bounce_count[0] >= bounce_time) {
      bounce_count[0] = 0;
    }
  }
  // Entprellen des Tasters 2, wenn gedrckt wird 1, dann bis bounce_time hochzhlen, dann taster erst wieder freigegeben
  if (bounce_count[1] >= 1) {
    bounce_count[1]++;
    if (bounce_count[1] >= bounce_time) {
      bounce_count[1] = 0;
      //Serial.println("BounceCount Zeit erreicht");
    }
  }
  // Entprellen des Tasters 3, wenn gedrckt wird 1, dann bis bounce_time hochzhlen, dann taster erst wieder freigegeben
  if (bounce_count[2] >= 1) {
    bounce_count[2]++;
    if (bounce_count[2] >= bounce_time) {
      bounce_count[2] = 0;
    }
  }
}

Credits

Hummer L.

Hummer L.

2 projects • 1 follower
Mechatronics technician apprenticeship / Systems engineering student

Comments