本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

PT1000探头的温度数据记录器

发布时间:2021-05-07
分享到:

PT1000探头的温度数据记录器

发布时间:2021-05-07
分享到:

该设备通过Atmega32U4测量温度,并将数据记录在SD卡上,且它具有用于​​无线操作的电池。该设备的应用程序很简单,您可以为记录器操作进行各种设置。如记录器具有两个活动通道和每5秒自动测量一次的设置。

项目概括:

  • -25°C至120°C之间的4通道温度测量
  • 由三节AAA电池供电
  • Atmega 32U4作为µC
  • GUI(OLED / LED)
  • 3个用户按钮
  • 低功耗〜5mA,OLED开启时<1mA,而测量OLED关闭<1µA时关闭,仅RTC
  • USB接口
  • 开关;电源开关
  • 实时时钟

原理图:

硬件设计:

步骤一:PCB组装的第一步是使用带有焊接模板的焊膏。这样,在焊垫上到处都应有一层薄薄的糊剂,而不会流到其他焊垫上。

步骤二:PCB零件组装和回流工艺

  • 粘贴后,必须将所有SMD组件放置在正确的位置。
  • 将所有组件都放置在焊盘上后,并将PCB放入回流焊炉中,根据焊膏的焊接特性对其进行加热。或者使用热风枪将糊状物融化。
  • 焊接THT组件,即一体式显示器和电池座。

步骤三:电缆组装

使用PT1000压接和焊接电缆。实现了3线制测量。将两根电缆焊接到一个引脚上,PT1000的另一个引脚连接到Dupont连接器的中间引脚。

步骤四:测试

组装完所有零件后,检查整个电路是否短路,如有必要,请用烙铁对其进行维修。

程序设计:

第1步:引导加载程序

  • 加载-Sparkfun Pro micro,通过Arduino Uno和Arduino作为ISP对Bootloader进行编程,(需要从5V到3V3的电平转换器)。
  • Arduino在PCB的背面,可以使用标准线MOSI,MISO,SCK,RST,GND,+ 3V3访问编程板,为此板构建了一个带有集成电平转换器的适配器。

第2步:编程

通过USB接口对软件进行编程,该代码需要其他库。

程序的不同功能都可以在OLED上显示的菜单中选择。

  • 正常的记录器操作,该操作在每个5s-100min之间的特定时间间隔内测量并存储一次温度。在这种模式下,OLED也可以关闭以实现小于1mA的功耗。
  • 手动模式,不断测量温度并输出。

在另一个设置菜单中,可以设置所有内容,例如时间,两次测量之间的延迟时间,活动的通道以及显示器是否应处于活动状态。

用户通过按钮或看门狗计时器将其唤醒以更新时间。可以主动关闭所有硬件,包括差分放大器,OLED和SD卡。

测量:

测量值以csv文件的形式存储在SD卡上,可以在合适的程序中进行分析。

电脑辅助设计:

附件1:(BOM项目温度数据记录器)

附件2:(代码)

/ * Autor:悍马L。
   基准:21.4.2021
   Temperatur Datalogger软件
   修订:
    V0.16:调整了温度曲线,并与热电偶进行了比较
    
   安美宫:
* /

// ----------图书馆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 定义 ----------
#define oled_address 0x3C
#define rtc_address 0x52

//---------- 寄存器 RTC RV3028 ----------
//时钟寄存器
#define RV3028_SECONDS   0x00
#define RV3028_MINUTES   0x01
#define RV3028_HOURS     0x02
//日历寄存器
#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;      //每行行数

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);

  //---------- 开始配置 ----------
  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

  //---------- 计时器 Konfiguration ----------
  //中断频率1000 Hz的TIMER 1:
  cli ();  //停止中断
  TCCR1A = 0; //将整个TCCR1A寄存器设置为0
  TCCR1B = 0; //与TCCR1B相同
  TCNT1  = 0; //将计数器值初始化为0
  //将比较匹配寄存器设置为1000 Hz增量
  OCR1A = 7999;// = 8000000 /(1 * 1000)-1(必须小于<65536)
  //开启CTC模式
  TCCR1B |= (1 << WGM12);
  //设置1个预分频器的CS12,CS11和CS10位
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
  //启用定时器比较中断
  TIMSK1 |= (1 << OCIE1A);
  sei(); // 允许中断

  //----------中断配置 ----------
  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() {
  //对于每个WDT调用,都会执行此函数,因为WDT状态是在WDT例程1中设置的
  if (wdt_status == 1) {
    rtc_gettime();
    oled_displaytime();
    wdt_status = 0;
  }

  //从不同的情况下,如手动,记录器或设置
  switch (choice) {
    //---------- 开始设置第一个问题是hndic、自动操作还是设置 ------------------------------------------------------------
    case 0:
      //---------- 看门狗定时器配置 ----------
      wdt_setE5(1);
      //---------- 您的配置----------
      if (digitalRead(oled_enb) == 0) {
        digitalWrite(oled_enb, 1);
        oled.begin(&Adafruit128x64, oled_address);
        oled.setFont(Callibri11); // 字体的选择
        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_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: //案例fr设置将在200中处理
      oled_print(3, -1, 0, "Setting");
      choice = 9;
      break;

    case 7: //来自记录器操作的查询
      //enter_sleep(); //首先,当所有的事情都完成后,把问题放到uart上
      but_do(10, 3, 4);
      break;
    case 8://手工操作查询
      enter_sleep();
      but_do(100, 4, 2);
      break;
    case 9://招聘案例查询
      enter_sleep();
      but_do(200, 2, 3);
      break;

    //----------测量作业 ------------------------------------------------------------
    case 10: //测量作业
      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: //测量作业
      enter_sleep();
      if (but_do(12, -1, 0) == 1 && digitalRead(sd_io) == 1) {
        choice = 14;
        //为记录器操作分配开始变量
        datalogger.starttime = rtc_gettime(); //测量开始时间
        datalogger.id = 1; //ID为1
        sd_config(); //初始化SD
      }
      break;
    case 12: //案例错误无SD卡
      oled_print(2, -1, 0, "SD-Card missing");
      choice = 13;
      break;
    case 13: //案件等待,直到开始再次按下,并再次测试SD可用
      enter_sleep();
      if (but_do(11, -1, 0) == 1) {
        datalogger.buttonstate[0] = 1;
      }
      break;
    case 14: //好了,可以开始了
      //取决于其他wdt设置的延迟时间
      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开/关,取决于配置
      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();
      //记录第一个测量值
      oled_displayvalue(1, 0);
      oled_displaybat();
      sd_update();
      choice = 16;
      break;
    case 15: //箱记录器Messausfhrung
      enter_sleep();
      //数据
      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;
      }
      //操作中的OLED开关和配置
      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); // 字体的选择
            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: //在OLED上发布新ID
      sprintf(data, "%d", datalogger.id);
      oled_print(3, 65, 65, data);
      choice = 15;
      break;

    //---------- 手动操作 ------------------------------------------------------------
    case 100: //手动操作
      oled.clear(0, 128, rows, 2 * rows + rows - 1);
      oled_displayvalue(0, 1);
      oled_print(3, 0, 0, "<-Back");
      choice = 101;
      break;
    case 101: //数据采集+输出
      opv_getvalue();
      oled_displayvalue(1, 0);
      enter_sleep();
      but_do(-1, -1, 0);
      break;

    //---------- 设置月时间 ------------------------------------------------------------
    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: //选择案例设置时间
      enter_sleep();
      but_do(300, 203, 206);
      break;
    case 208://Case设置测量间隔时间
      enter_sleep();
      but_do(230, 204, 202);
      break;
    case 209: //案例Einstellung welcher Channel Aktiv
      enter_sleep();
      but_do(240, 205, 203);
      break;
    case 210://案例Oled和aus
      enter_sleep();
      but_do(250, 206, 204);
      break;
    case 211://案件重新开始
      //enter_sleep();
      but_do(0, 202, 205);
      break;

    //---------- 延迟设置 ------------------------------------------------------------
    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;

    //---------- 频道活动设置 ------------------------------------------------------------
    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;

    //---------- 活动设置 ------------------------------------------------------------
    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;

    //---------- 时刻表设置 ------------------------------------------------------------
    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: //变更年份
      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: //月末
      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;
    }
  }
}

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论