我的目的是开发一种温度测量和数据记录设备,因为我没有。
它应该在显示器上显示温度测量数据,同时将其存储在 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 接口对软件进行编程。代码需要额外的库。
程序的不同功能都可以在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(外壳柔软/顶部柔软)
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !