資料介紹
描述
介紹
我的目的是開發一種溫度測量和數據記錄設備,因為我沒有。
它應該在顯示器上顯示溫度測量數據,同時將其存儲在 SD 卡上。這是我給自己設定的主要要求。此外,它應該有電池用于無線操作,因此整個系統必須設計為低功耗。
該應用程序非常簡單,您打開記錄器并開始操作。您可以對記錄器操作進行各種設置。在視頻中,您可以看到具有兩個活動通道和每 5 秒自動測量一次的設置。
設計

我選擇的 μC 是 Atmega32U4,因為它具有內置 USB 接口,并且易于通過 Arduino IDE 進行編程。一切都由一個 3.3V 的線性穩壓器 (TPS782) 供電,這是該應用的最佳選擇,因為它在運行期間僅消耗 500nA。三節 AAA 電池提供電源。PT1000 的電壓由差分放大器放大一次,并由 Atmega 的模擬輸入測量。測量范圍定義為 -25°C 至 120°C,對應于 0V 至 3.3V 的電壓。測量數據在 128x64 Oled 上顯示一次,并可選擇存儲在 FAT16 SD 卡上。時間和日期由實時時鐘 (RV3028C7) 提供,在空閑模式下僅消耗 45nA。此外,菜單中有三個用于導航的用戶按鈕,背面有一個重置按鈕。以及兩個用戶指示燈和一個 USB 接口。幾乎所有可從外部訪問的部件仍然配備 ESD 鉗位二極管。
集會
第 1 步:PCB 和焊膏

PCB組裝的第一步是使用焊接模板應用焊膏。之后,焊盤上的任何地方都應該有一層薄薄的糊狀物,而不會溢出到其他焊盤上。
第 2 步:PCB 零件組裝和回流工藝
粘貼后,所有 SMD 元件必須放置在正確的位置。這對于具有許多引腳的小型組件尤其困難,例如 Atmega32U4。
當所有元件都放置在焊盤上后,需要將 PCB 放入回流爐中并根據焊膏的焊接曲線對其進行加熱。或者,您可以使用熱風槍熔化糊狀物,但不幸的是我沒有任何經驗。
最后,必須焊接 THT 組件,即 oled 顯示器和電池座。
第 3 步:電纜組裝

下一步是使用 PT1000 壓接和焊接電纜。實現了 3 線測量,因此必須將兩條電纜焊接到一個引腳。PT1000 的另一個引腳連接到杜邦連接器的中間引腳,因此連接器的插入方式無關緊要。
第 4 步:測試

一切都組裝好后,我會檢查整個電路是否短路,如有必要,用烙鐵重新加工。
編程
第 1 步:引導加載程序
作為編程的第一步,您需要加載一個額外的板,Sparkfun Pro micro。在 Arduino IDE → Board Manager → SparkFun AVR Boards 中可用我通過 Arduino Uno 和 Arduino 作為 ISP 對 Bootloader 進行了編程,但您必須小心,因為您需要一個從 5V 到 3V3 的電平轉換器。請參閱:作為 ISP 的 Arduino 和 Arduino Bootloaders | Arduino在 PCB 的背面,您可以使用標準線路 MOSI、MISO、SCK、RST、GND、+3V3 訪問編程焊盤。我為這個焊盤構建了一個帶有集成電平轉換器的適配器,也許我會做一個項目其中。
第 2 步:編程
最后一步是通過 USB 接口對軟件進行編程。代碼需要額外的庫。
- SSD1306 for OLED GitHub - greiman/SSD1306Ascii: 用于 SSD1306 OLED 顯示器的純文本 Arduino 庫
- FAT 16 SD 卡GitHub - greiman/Fat16: 用于 Arduino 的更小的 FAT16 庫
- 標準 Arduino 庫
程序的不同功能都可以在OLED顯示的菜單中選擇。其中有一個正常的記錄儀操作,它在每次5s-100min之間的某個時間間隔內測量并存儲一次溫度。在這種模式下,OLED 也可以被關閉以實現小于 1mA 的功耗。
手動模式,持續測量和輸出溫度。
還有另一個設置菜單,可以設置所有內容,例如時間、測量之間的延遲時間、活動通道以及顯示是否應該活動。
在每種模式下,我都嘗試使用盡可能少的電量。這就是為什么我每次都讓 μC 進入睡眠模式。它由用戶按鈕或看門狗定時器喚醒以更新時間。所有硬件都可以主動關閉,包括差分放大器、OLED和SD卡。
/* 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
#include
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include
#include
#include
#include
//---------- 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 fuer 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;
}
}
}
測量值以 csv 文件的形式存儲在 SD 卡上,以后可以在合適的程序中進行分析。

加元
2 使用雙擠壓打印機打印的組件外殼。
材料 1:PLA(外殼堅固/頂部堅固)
材料 2:TPU(外殼柔軟/頂部柔軟)
?

?
概括
- -25°C 至 120°C 之間的 4 通道溫度測量
- 由三節 AAA 電池供電
- Atmega 32U4 作為 μC
- 圖形用戶界面(OLED / LED)
- 3 個用戶按鈕
- 低功耗 ~5mA On with OLED<1mA while measure OLED off<1μA off off, only RTC
- USB接口
- 電源開關
- 實時時鐘
- 創建帶有SD屏蔽的溫度記錄器
- 簡單的數據記錄器開源設計
- 基于Arduino MKR ZERO的天氣數據記錄器 0次下載
- 基于Particle Photon的數據記錄器 0次下載
- 溫度和濕度數據記錄器開源案例
- 溫度和濕度數據記錄器開源分享
- 基于Arduino的GPS數據記錄器
- 虹科SWitrace溫度數據記錄儀-低溫、干冰、超低溫、藍牙
- Nodemcu數據記錄器
- 基于單片機30路PT100溫度數據自動采集系統設計資料 18次下載
- 愛華微膠囊記錄器TP-M725/TP-M525數據手冊 1次下載
- 鉑電阻Pt100和Pt1000溫度傳感器阻值計算 76次下載
- PT1000分度表 74次下載
- 溫度記錄器 1次下載
- 基于FAT16文件系統的嵌入式溫度記錄器 8次下載
- 集裝箱溫度數據遠程監控跟蹤系統 1220次閱讀
- 確定溫度記錄iButton的任務參數 591次閱讀
- IC形式直接讀數溫度探頭 858次閱讀
- 如何使用DS1678實時事件記錄器 946次閱讀
- pt100溫度傳感器量程是多少 1.2w次閱讀
- 基于鉑電阻PT1000和AD7731芯片實現熱敏電阻測試儀的設計 7908次閱讀
- 電阻溫度傳感器Pt1000的特性曲線分析及應用 2w次閱讀
- digilent高分辨率便攜式數據記錄器介紹 1698次閱讀
- 溫度變送器pt100接線 3.1w次閱讀
- TI TMP117系列數字溫度傳感器具有RTD級精度又無需校準 1.1w次閱讀
- Pt100分度表如何使用?PT100/PT1000熱電阻值計算 2.9w次閱讀
- PT1000電阻值轉化為溫度值的計算公式 10.2w次閱讀
- 鉑電阻溫度特性曲線圖分析 淺談鉑電阻應用原理 2.6w次閱讀
- 溫度傳感器探頭長度要求 pt100溫度傳感器探頭分析 3w次閱讀
- LCD顯示遠程溫度濕度數據記錄器與應用軟件的介紹 4869次閱讀
下載排行
本周
- 1VCC啟動電路(可下載)
- 748.32 KB | 2次下載 | 免費
- 2TTL通往RS232神奇之黑盒(一)(可下載)
- 515.72 KB | 2次下載 | 免費
- 3HAC1108TS型LVCMOS時鐘緩沖器產品說明書
- 386.75 KB | 1次下載 | 免費
- 460KW電機控制器硬件驅動電路設計(可下載)
- 720.01 KB | 1次下載 | 免費
- 5華為硬件工程師手冊目前最全版本
- 1.02 MB | 1次下載 | 2 積分
- 6晶體諧振器的工作原理
- 736.44 KB | 1次下載 | 免費
- 7錦銳MCU最新版燒錄軟件CACHIP_TOOL_4.0.0
- 1.14 MB | 0次下載 | 2 積分
- 8HACD1204QN型低抖動LVDS時鐘緩沖器產品說明書
- 460.09 KB | 次下載 | 免費
本月
- 1STM32CubeMX用于STM32配置和初始化C代碼生成
- 21.90 MB | 628次下載 | 免費
- 2STM32開發板教程之STM32開發指南免費下載
- 24.88 MB | 176次下載 | 3 積分
- 3EN60335-1安規標準 中文版本
- 1.86 MB | 53次下載 | 1 積分
- 4OAH0428最新規格書(中文)
- 2.52 MB | 28次下載 | 10 積分
- 5UHV系列雷電沖擊電壓發生器試驗裝置詳細說明使用
- 1.07 MB | 17次下載 | 免費
- 6介紹一些常用的電子元器件
- 3.20 MB | 8次下載 | 免費
- 7麻將機升降電路
- 0.12 MB | 7次下載 | 1 積分
- 8PC2608 1.5KV隔離1A恒流雙向均衡解決方案
- 3.30 MB | 6次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935124次下載 | 10 積分
- 2開源硬件-PMP21529.1-4 開關降壓/升壓雙向直流/直流轉換器 PCB layout 設計
- 1.48MB | 420063次下載 | 10 積分
- 3Altium DXP2002下載入口
- 未知 | 233088次下載 | 10 積分
- 4電路仿真軟件multisim 10.0免費下載
- 340992 | 191371次下載 | 10 積分
- 5十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183336次下載 | 10 積分
- 6labview8.5下載
- 未知 | 81583次下載 | 10 積分
- 7Keil工具MDK-Arm免費下載
- 0.02 MB | 73814次下載 | 10 積分
- 8LabVIEW 8.6下載
- 未知 | 65988次下載 | 10 積分
評論