Hello everyone,
I have developed a LoRaWAN sensor that transmits its data to TTN . The hardware is based on a ESP board. The software for the sensor is written using the Arduino IDE and is based on the LMIC library. When powered on, the sensor operates correctly, transmitting data to TTN as expected. I’m sending one packet everytime the 9DOF Sensor moves this is like every 10 minutes or something.
I tried implementing deep sleep mode to save power. While this partially improved the battery life, I ran into a different problem: the data transmission became unreliable. The node transmits the data as expected, and the packets are received by the gateway. However, the data does not consistently appear on TTN. Sometimes no data arrives for a long time, and sometimes multiple packets are received in a row. I also have implemented the LMIC_shutdown(); function but it really did not help.
Has anyone experienced similar issues when using deep sleep with LMIC and LoRaWAN? If so, were you able to find a solution? Any advice or insights would be greatly appreciated.
I also uploaded my Code, i hope someone can help me.
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_LSM303_U.h>
#include <esp_sleep.h>
#include <Adafruit_9DOF.h>
#include <LSM303.h>
#include <WiFi.h>
#include <Preferences.h>
#include <lmic.h>
#include <hal/hal.h>
#include <lmic.h>
#include <Preferences.h>
// Sensor-Instanzen erstellen
Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(54321);
Adafruit_LSM303_Mag_Unified mag = Adafruit_LSM303_Mag_Unified(12345);
LSM303 lsm;
// Variablen für Kalibrierung
float closedAccelX, closedAccelY, closedAccelZ;
float closedMagX, closedMagY, closedMagZ;
float openAccelX, openAccelY, openAccelZ;
float openMagX, openMagY, openMagZ;
float tiltedAccelX, tiltedAccelY, tiltedAccelZ; // Werte für den gekippten Zustand
float tiltedMagX, tiltedMagY, tiltedMagZ;
float halfOpenAccelX, halfOpenAccelY, halfOpenAccelZ; // Werte für den halb offenen Zustand
float halfOpenMagX, halfOpenMagY, halfOpenMagZ;
// Schwellenwerte für Statuserkennung
float accelThreshold = 0.5; // Toleranzwert für Beschleunigung
float magThreshold = 5.0; // Toleranzwert für Magnetometer
float halfOpenThreshold = 5.0; // Schwellenwert für "halb offen"
float tiltThreshold = 1.5; // Schwellenwert für Kipp-Winkel (z.B. y-Achse)
unsigned long tiltStartTime = 0;
const unsigned long tiltDelayTime = 2000; // 2 Sekunden Verzögerung für "gekippt"
// Taster-Pin und Status
const int buttonPin = 13; // Pin für den Taster
bool lastButtonState = HIGH; // Letzter Tasterstatus
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // Entprellzeit in Millisekunden
Preferences preferences;
// Funktionsprototypen
void calibrateClosedState();
void calibrateOpenState();
void calibrateTiltedState(); // Funktion für gekippten Zustand
void calibrateHalfOpenState(); // Funktion für halb offenen Zustand
void saveCalibrationData();
void loadCalibrationData();
void printCalibrationData();
bool isButtonPressed();
void checkWindowState();
String getWindowState(sensors_event_t *accelEvent, sensors_event_t *magEvent);
float calculateWindowOpenPercentage(sensors_event_t *accelEvent, sensors_event_t *magEvent);
float calculateTiltPercentage(sensors_event_t *accelEvent);
// Variablen für Deep Sleep und Interrupt
unsigned long noMagneticChangeDuration = 0; // Zeit ohne Magnetfeldänderung
const unsigned long noMagneticChangeThreshold = 10000; // 10 Sekunden ohne Änderung, um in Sleep zu gehen
const unsigned long stayAwakeDurationAfterWakeup = 5000; // 5 Sekunden wach bleiben nach dem Aufwachen
const unsigned long outputInterval = 1000; // Zeitintervall zwischen Ausgaben in Millisekunden
unsigned long lastOutputTime = 0; // Zeitpunkt der letzten Ausgabe
unsigned long wakeupStartTime = 0; // Zeitpunkt, an dem der ESP aufgewacht ist
const int sda = 21;
const int scl = 22;
const int intPin = 15; // Interrupt-Pin für LSM303
// Magnetfeldänderungsschwellenwert
float magneticThreshold = 5.0;
// Flag, um festzustellen, ob der ESP nach dem Wakeup wach bleiben soll
bool stayAwakeAfterWakeup = false;
unsigned long noMovementDuration = 0; // Dauer ohne Bewegung
const unsigned long noMovementThreshold = 5000; // Zeit ohne Bewegung (5 Sekunden)
bool movementDetected = false; // Flag für die Bewegungserkennung
// TTN Keys (Little Endian Format)
static const u1_t PROGMEM DEVEUI[8] = { }; // DevEUI
static const u1_t PROGMEM APPEUI[8] = { }; // AppEUI
static const u1_t PROGMEM APPKEY[16] = {}; // AppKey
char mydata[32]; // Speicher für die Nachricht
static osjob_t sendjob;
unsigned long halfOpenStartTime = 0; // Startzeit des halb offenen Zustands
const unsigned long halfOpenTimeout = 10000; // Timeout für 10 Sekunden
float previousAccelMovement = 0;
float previousMagMovement = 0;
unsigned long lastMovementCheckTime = 0;
float fluctuationThreshold = 0.05; // 5% fluctuation threshold
float lastAccelY = 0; // Variable für die vorherige Beschleunigung auf der Y-Achse
bool isTilted = false; // Zustand "Gekippt" merken
// Pin mapping for the ESP32 (adjust for your setup)
const lmic_pinmap lmic_pins = {
.nss = 5, // LoRa Chip Select (NSS)
.rxtx = LMIC_UNUSED_PIN,
.rst = 14, // Reset Pin
.dio = {26, 33, 32} // DIO0, DIO1, DIO2
};
// Provide keys to the LMIC library
void os_getArtEui(u1_t *buf) { memcpy_P(buf, APPEUI, 8); }
void os_getDevEui(u1_t *buf) { memcpy_P(buf, DEVEUI, 8); }
void os_getDevKey(u1_t *buf) { memcpy_P(buf, APPKEY, 16); }
bool sendComplete = false; // Flag für abgeschlossene Übertragung
unsigned long lastMovementTime = 0; // Zeit der letzten Bewegung
// Handle LoRaWAN events
void onEvent(ev_t ev) {
switch(ev) {
case EV_TXCOMPLETE:
Serial.println("LoRa-Übertragung abgeschlossen.");
if (LMIC.txrxFlags & TXRX_ACK) {
Serial.println("ACK erhalten.");
}
sendComplete = true; // Übertragung ist abgeschlossen
break;
case EV_JOINING:
Serial.println("Starte LoRaWAN-Join...");
break;
case EV_JOINED:
Serial.println("Erfolgreich bei TTN verbunden!");
break;
default:
Serial.print("Unbekanntes Ereignis: ");
Serial.println((unsigned)ev);
break;
}
}
void do_send(osjob_t *j) {
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
sensors_event_t accelEvent, magEvent;
mag.getEvent(&magEvent);
accel.getEvent(&accelEvent);
// Bestimme den aktuellen Fensterstatus
String windowState = getWindowState(&accelEvent, &magEvent);
uint8_t status = 0; // 0 = Unbekannt, 1 = Gekippt, 2 = Offen
uint8_t percentage = 0; // Prozentwert (0–100)
if (windowState == "Gekippt") {
status = 1; // Gekippt
percentage = static_cast<uint8_t>(calculateTiltPercentage(&accelEvent));
} else if (windowState == "Offen") {
status = 2; // Offen
percentage = static_cast<uint8_t>(calculateWindowOpenPercentage(&accelEvent, &magEvent));
}
// Nachricht zusammenstellen (2 Byte)
uint8_t message[2] = {status, percentage};
// Nachricht an TTN senden
LMIC_setTxData2(1, message, sizeof(message), 0);
// Debug-Ausgabe
Serial.print(F("Packet queued: Status = "));
Serial.print(status);
Serial.print(", Percentage = ");
Serial.println(percentage);
}
}
void setup(void) {
Serial.begin(115200);
Wire.begin(sda, scl); // I2C Initialisierung
pinMode(buttonPin, INPUT_PULLUP);
pinMode(intPin, INPUT); // Setzt den Interrupt-Pin für den LSM303
Serial.println(F("Starting LoRaWAN..."));
os_init();
LMIC_reset();
LMIC_startJoining();
do_send(&sendjob);
Serial.println("Fensterstatus-Erkennung gestartet.");
// Sensoren initialisieren
if (!accel.begin()) {
Serial.println("Sensorinitialisierung fehlgeschlagen!");
while (1);
}
if (!mag.begin()) {
Serial.println("Magnetometerinitialisierung fehlgeschlagen!");
while (1);
}
// Kalibrierungswerte laden oder initialisieren
preferences.begin("calibration", false); // Namespace öffnen
if (preferences.getBool("calibrated", false)) {
loadCalibrationData();
printCalibrationData();
} else {
Serial.println("Bitte das Fenster in den *geschlossenen Zustand* bringen.");
delay(5000);
calibrateClosedState();
Serial.println("Bitte das Fenster in den *offenen Zustand* bringen.");
delay(10000);
calibrateOpenState();
Serial.println("Bitte das Fenster in den *gekippten Zustand* bringen.");
delay(5000);
calibrateTiltedState();
Serial.println("Bitte das Fenster in den *halb offenen Zustand* bringen.");
delay(10000);
calibrateHalfOpenState();
saveCalibrationData();
printCalibrationData();
}
// Magnetometer-Register konfigurieren
lsm.writeMagReg(LSM303::CRA_REG_M, 0x10); // 15 Hz Output-Datenrate für das Magnetometer
lsm.writeMagReg(LSM303::MR_REG_M, 0x00); // Continuous-Conversion-Mode
lsm.writeMagReg(LSM303::INT_CTRL_M, 0xF8); // Interrupt auf X, Y und Z aktivieren
lsm.writeMagReg(LSM303::INT_THS_L_M, 0x20); // Schwellenwert (Low-Byte, z. B. 0x20 = ~0.25 Gauss)
lsm.writeMagReg(LSM303::INT_THS_H_M, 0x00); // Schwellenwert (High-Byte)
// ESP32 auf externen Interrupt über GPIO12 vorbereiten
esp_sleep_enable_ext0_wakeup(GPIO_NUM_15, 1); // Interrupt bei steigender Flanke auf GPIO12 (INT LSM303)
WiFi.mode(WIFI_OFF); // WiFi deaktivieren, um Strom zu sparen
btStop(); // Bluetooth deaktivieren
setCpuFrequencyMhz(80); // CPU-Frequenz reduzieren für geringeren Stromverbrauch
// Lese aktuelle Magnetometer- und Accelerometerdaten
sensors_event_t magEvent, accelEvent;
mag.getEvent(&magEvent);
accel.getEvent(&accelEvent);
// Nach dem Wakeup wach bleiben
stayAwakeAfterWakeup = true;
wakeupStartTime = millis(); // Speichere die Zeit des Wakeups
}
void loop(void) {
static String lastWindowState = ""; // Zustand speichern
static unsigned long lastStatusTime = 0;
const unsigned long statusRepeatInterval = 2000; // Status alle 2 Sekunden wiederholen
static unsigned long noMovementDuration = millis();
os_runloop_once(); // Process LoRaWAN events
// Button für Kalibrierung überprüfen
if (isButtonPressed()) {
Serial.println("Kalibrierung wird zurückgesetzt...");
delay(5000);
calibrateClosedState();
delay(5000);
calibrateOpenState();
delay(10000);
calibrateTiltedState();
delay(5000);
calibrateHalfOpenState();
saveCalibrationData();
printCalibrationData();
delay(2000);
}
// Nach Wakeup kurze Verzögerung
if (millis() - wakeupStartTime < 2000) {
delay(2000);
}
// Magnetometer- und Accelerometer-Daten abrufen
sensors_event_t magEvent;
mag.getEvent(&magEvent);
sensors_event_t accelEvent;
accel.getEvent(&accelEvent);
// Fensterstatus bestimmen
String currentWindowState = getWindowState(&accelEvent, &magEvent);
if (millis() - lastStatusTime > statusRepeatInterval) {
lastStatusTime = millis();
if (isTilted) {
float tiltPercentage = calculateTiltPercentage(&accelEvent);
Serial.print("Fenster gekippt, Kippwinkel: ");
Serial.print(tiltPercentage, 1);
Serial.println("%");
} else if (currentWindowState == "Offen") {
float openPercentage = calculateWindowOpenPercentage(&accelEvent, &magEvent);
Serial.print("Fenster offen: ");
Serial.print(openPercentage, 1);
Serial.println("%");
}
}
float accelMovementClosed = sqrt(pow(accelEvent.acceleration.x - closedAccelX, 2) +
pow(accelEvent.acceleration.y - closedAccelY, 2) +
pow(accelEvent.acceleration.z - closedAccelZ, 2));
float accelMovementOpen = sqrt(pow(accelEvent.acceleration.x - openAccelX, 2) +
pow(accelEvent.acceleration.y - openAccelY, 2) +
pow(accelEvent.acceleration.z - openAccelZ, 2));
float accelMovementTilted = sqrt(pow(accelEvent.acceleration.x - tiltedAccelX, 2) +
pow(accelEvent.acceleration.y - tiltedAccelY, 2) +
pow(accelEvent.acceleration.z - tiltedAccelZ, 2));
float accelMovementHalfOpen = sqrt(pow(accelEvent.acceleration.x - halfOpenAccelX, 2) +
pow(accelEvent.acceleration.y - halfOpenAccelY, 2) +
pow(accelEvent.acceleration.z - halfOpenAccelZ, 2));
// Minimalen Bewegungswert aus allen Zuständen berechnen
float accelMovement = min(min(accelMovementClosed, accelMovementOpen),
min(accelMovementTilted, accelMovementHalfOpen));
// Analog für Magnetometerdaten
float magMovementClosed = sqrt(pow(magEvent.magnetic.x - closedMagX, 2) +
pow(magEvent.magnetic.y - closedMagY, 2) +
pow(magEvent.magnetic.z - closedMagZ, 2));
float magMovementOpen = sqrt(pow(magEvent.magnetic.x - openMagX, 2) +
pow(magEvent.magnetic.y - openMagY, 2) +
pow(magEvent.magnetic.z - openMagZ, 2));
float magMovementTilted = sqrt(pow(magEvent.magnetic.x - tiltedMagX, 2) +
pow(magEvent.magnetic.y - tiltedMagY, 2) +
pow(magEvent.magnetic.z - tiltedMagZ, 2));
float magMovementHalfOpen = sqrt(pow(magEvent.magnetic.x - halfOpenMagX, 2) +
pow(magEvent.magnetic.y - halfOpenMagY, 2) +
pow(magEvent.magnetic.z - halfOpenMagZ, 2));
// Minimalen Bewegungswert aus allen Zuständen berechnen
float magMovement = min(min(magMovementClosed, magMovementOpen),
min(magMovementTilted, magMovementHalfOpen));
float accelFluctuation = abs(accelMovement - previousAccelMovement) / previousAccelMovement;
float magFluctuation = abs(magMovement - previousMagMovement) / previousMagMovement;
// Überprüfe, ob die Fluktuationen größer als der Schwellenwert (5%) sind
if (sendComplete) {
// Fluktuationen prüfen, um zu entscheiden, ob Deep Sleep aktiviert wird
if (accelFluctuation <= fluctuationThreshold && magFluctuation <= fluctuationThreshold) {
Serial.println("Fensterzustand stabil, Deep Sleep aktivieren.");
LMIC_shutdown(); // Shutdown LMIC to prevent reinitialization issues
Serial.println("LMIC SHUTDOWN");
delay(1000);
esp_deep_sleep_start(); // Deep Sleep aktivieren
} else {
// Wenn Bewegung oder Änderung erkannt wird, speichere die neuen Werte
previousAccelMovement = accelMovement;
previousMagMovement = magMovement;
lastMovementCheckTime = millis();
}
// Überprüfen, ob keine Bewegung erkannt wurde und in den Deep Sleep gehen
if (accelMovement > accelThreshold) {
noMovementDuration = millis(); // Timer zurücksetzen bei Bewegungserkennung
movementDetected = true;
} else {
movementDetected = false;
}
// Keine Bewegung für länger als den Schwellwert -> Deep Sleep
if (millis() - noMovementDuration > noMovementThreshold) {
Serial.println("Keine Bewegung erkannt, prüfe Übertragungsstatus...");
if (sendComplete) { // Nur in den Deep Sleep gehen, wenn die Übertragung abgeschlossen ist
Serial.println("Übertragung abgeschlossen, ESP geht in Deep Sleep...");
LMIC_shutdown(); // Shutdown LMIC to prevent reinitialization issues
Serial.println("LMIC SHUTDOWN");
delay(1000);
esp_deep_sleep_start(); // Deep-Sleep aktivieren
} else {
Serial.println("Warte auf abgeschlossene Übertragung...");
}
}
// LoRaWAN Status überprüfen und Daten nur senden, wenn keine Übertragung anhängig ist
if (!(LMIC.opmode & OP_TXRXPEND) && sendComplete) {
Serial.println("Sende Daten über LoRa...");
sendComplete = false; // Übertragung gestartet
do_send(&sendjob); // Send-Job anlegen
}
}
}
float calculateWindowOpenPercentage(sensors_event_t *accelEvent, sensors_event_t *magEvent) {
// Berechne die Differenz im Magnetfeld (Magnetometer)
float magDiffFromClosed = sqrt(
pow(magEvent->magnetic.x - closedMagX, 2) +
pow(magEvent->magnetic.y - closedMagY, 2) +
pow(magEvent->magnetic.z - closedMagZ, 2)
);
float magDiffFromOpen = sqrt(
pow(magEvent->magnetic.x - openMagX, 2) +
pow(magEvent->magnetic.y - openMagY, 2) +
pow(magEvent->magnetic.z - openMagZ, 2)
);
// Berechne den Prozentsatz der Fensteröffnung anhand der Magnetometerdaten
float magPercentage = (magDiffFromClosed / (magDiffFromClosed + magDiffFromOpen)) * 100.0;
// Begrenze den Prozentsatz zwischen 0 und 100
magPercentage = constrain(magPercentage, 0, 100);
return magPercentage; // Prozentsatz der Fensteröffnung
}
float calculateTiltPercentage(sensors_event_t *accelEvent) {
float currentTiltY = abs(accelEvent->acceleration.y);
float tiltDiffY = abs(currentTiltY - closedAccelY);
// Prozentuale Berechnung basierend auf gekipptem Zustand
float tiltPercentage = (tiltDiffY / abs(tiltedAccelY - closedAccelY)) * 100.0;
tiltPercentage = constrain(tiltPercentage, 0, 100); // Begrenzen auf 0-100%
return tiltPercentage;
}
String getWindowState(sensors_event_t *accelEvent, sensors_event_t *magEvent) {
// Berechne die Änderung auf der Y-Achse (Beschleunigung)
float accelChangeY = abs(accelEvent->acceleration.y - lastAccelY);
lastAccelY = accelEvent->acceleration.y;
// Wenn der Y-Wert sich signifikant ändert, Fenster als "Gekippt" erkennen
if (accelChangeY > tiltThreshold) { // Jede Änderung größer als tiltThreshold (z.B. 1.5)
isTilted = true; // Zustand "Gekippt" merken
} else {
// Wenn der Kippwinkel zurück zu 0% geht (also der Zustand neutral ist), zurücksetzen
if (isTilted) {
float tiltPercentage = calculateTiltPercentage(accelEvent);
if (tiltPercentage < 10) { // Wenn der Kippwinkel fast 0% erreicht, setze den Zustand zurück
isTilted = false; // Kippzustand zurücksetzen
}
}
}
// Wenn das Fenster "Gekippt" ist, geben wir diesen Zustand zurück
if (isTilted) {
return "Gekippt";
} else {
// Berechne den Öffnungsgrad des Fensters basierend auf dem Magnetometer
float openPercentage = calculateWindowOpenPercentage(accelEvent, magEvent);
if (openPercentage > 0) {
return "Offen";
}
}
return "Unbekannt";
}
void calibrateClosedState() {
sensors_event_t accelEvent;
sensors_event_t magEvent;
accel.getEvent(&accelEvent);
mag.getEvent(&magEvent);
closedAccelX = accelEvent.acceleration.x;
closedAccelY = accelEvent.acceleration.y;
closedAccelZ = accelEvent.acceleration.z;
closedMagX = magEvent.magnetic.x;
closedMagY = magEvent.magnetic.y;
closedMagZ = magEvent.magnetic.z;
// Ausgabe der kalibrierten Daten
Serial.println("Geschlossener Zustand kalibriert:");
Serial.print("Accel: ");
Serial.print(closedAccelX); Serial.print(", ");
Serial.print(closedAccelY); Serial.print(", ");
Serial.println(closedAccelZ);
Serial.print("Mag: ");
Serial.print(closedMagX); Serial.print(", ");
Serial.print(closedMagY); Serial.print(", ");
Serial.println(closedMagZ);
}
void calibrateOpenState() {
sensors_event_t accelEvent;
sensors_event_t magEvent;
accel.getEvent(&accelEvent);
mag.getEvent(&magEvent);
openAccelX = accelEvent.acceleration.x;
openAccelY = accelEvent.acceleration.y;
openAccelZ = accelEvent.acceleration.z;
openMagX = magEvent.magnetic.x;
openMagY = magEvent.magnetic.y;
openMagZ = magEvent.magnetic.z;
// Ausgabe der kalibrierten Daten
Serial.println("Offener Zustand kalibriert:");
Serial.print("Accel: ");
Serial.print(openAccelX); Serial.print(", ");
Serial.print(openAccelY); Serial.print(", ");
Serial.println(openAccelZ);
Serial.print("Mag: ");
Serial.print(openMagX); Serial.print(", ");
Serial.print(openMagY); Serial.print(", ");
Serial.println(openMagZ);
}
void calibrateTiltedState() {
sensors_event_t accelEvent;
sensors_event_t magEvent;
accel.getEvent(&accelEvent);
mag.getEvent(&magEvent);
tiltedAccelX = accelEvent.acceleration.x;
tiltedAccelY = accelEvent.acceleration.y;
tiltedAccelZ = accelEvent.acceleration.z;
tiltedMagX = magEvent.magnetic.x;
tiltedMagY = magEvent.magnetic.y;
tiltedMagZ = magEvent.magnetic.z;
// Ausgabe der kalibrierten Daten
Serial.println("Gekippter Zustand kalibriert:");
Serial.print("Accel: ");
Serial.print(tiltedAccelX); Serial.print(", ");
Serial.print(tiltedAccelY); Serial.print(", ");
Serial.println(tiltedAccelZ);
Serial.print("Mag: ");
Serial.print(tiltedMagX); Serial.print(", ");
Serial.print(tiltedMagY); Serial.print(", ");
Serial.println(tiltedMagZ);
}
void calibrateHalfOpenState() {
sensors_event_t accelEvent;
sensors_event_t magEvent;
accel.getEvent(&accelEvent);
mag.getEvent(&magEvent);
halfOpenAccelX = accelEvent.acceleration.x;
halfOpenAccelY = accelEvent.acceleration.y;
halfOpenAccelZ = accelEvent.acceleration.z;
halfOpenMagX = magEvent.magnetic.x;
halfOpenMagY = magEvent.magnetic.y;
halfOpenMagZ = magEvent.magnetic.z;
// Ausgabe der kalibrierten Daten
Serial.println("Halb-offener Zustand kalibriert:");
Serial.print("Accel: ");
Serial.print(halfOpenAccelX); Serial.print(", ");
Serial.print(halfOpenAccelY); Serial.print(", ");
Serial.println(halfOpenAccelZ);
Serial.print("Mag: ");
Serial.print(halfOpenMagX); Serial.print(", ");
Serial.print(halfOpenMagY); Serial.print(", ");
Serial.println(halfOpenMagZ);
}
void saveCalibrationData() {
preferences.putFloat("closedAccelX", closedAccelX);
preferences.putFloat("closedAccelY", closedAccelY);
preferences.putFloat("closedAccelZ", closedAccelZ);
preferences.putFloat("closedMagX", closedMagX);
preferences.putFloat("closedMagY", closedMagY);
preferences.putFloat("closedMagZ", closedMagZ);
preferences.putFloat("openAccelX", openAccelX);
preferences.putFloat("openAccelY", openAccelY);
preferences.putFloat("openAccelZ", openAccelZ);
preferences.putFloat("openMagX", openMagX);
preferences.putFloat("openMagY", openMagY);
preferences.putFloat("openMagZ", openMagZ);
preferences.putFloat("tiltedAccelX", tiltedAccelX);
preferences.putFloat("tiltedAccelY", tiltedAccelY);
preferences.putFloat("tiltedAccelZ", tiltedAccelZ);
preferences.putFloat("tiltedMagX", tiltedMagX);
preferences.putFloat("tiltedMagY", tiltedMagY);
preferences.putFloat("tiltedMagZ", tiltedMagZ);
preferences.putFloat("halfOpenAccelX", halfOpenAccelX);
preferences.putFloat("halfOpenAccelY", halfOpenAccelY);
preferences.putFloat("halfOpenAccelZ", halfOpenAccelZ);
preferences.putFloat("halfOpenMagX", halfOpenMagX);
preferences.putFloat("halfOpenMagY", halfOpenMagY);
preferences.putFloat("halfOpenMagZ", halfOpenMagZ);
preferences.putBool("calibrated", true);
preferences.end();
}
void loadCalibrationData() {
closedAccelX = preferences.getFloat("closedAccelX", 0);
closedAccelY = preferences.getFloat("closedAccelY", 0);
closedAccelZ = preferences.getFloat("closedAccelZ", 0);
closedMagX = preferences.getFloat("closedMagX", 0);
closedMagY = preferences.getFloat("closedMagY", 0);
closedMagZ = preferences.getFloat("closedMagZ", 0);
openAccelX = preferences.getFloat("openAccelX", 0);
openAccelY = preferences.getFloat("openAccelY", 0);
openAccelZ = preferences.getFloat("openAccelZ", 0);
openMagX = preferences.getFloat("openMagX", 0);
openMagY = preferences.getFloat("openMagY", 0);
openMagZ = preferences.getFloat("openMagZ", 0);
tiltedAccelX = preferences.getFloat("tiltedAccelX", 0);
tiltedAccelY = preferences.getFloat("tiltedAccelY", 0);
tiltedAccelZ = preferences.getFloat("tiltedAccelZ", 0);
tiltedMagX = preferences.getFloat("tiltedMagX", 0);
tiltedMagY = preferences.getFloat("tiltedMagY", 0);
tiltedMagZ = preferences.getFloat("tiltedMagZ", 0);
halfOpenAccelX = preferences.getFloat("halfOpenAccelX", 0);
halfOpenAccelY = preferences.getFloat("halfOpenAccelY", 0);
halfOpenAccelZ = preferences.getFloat("halfOpenAccelZ", 0);
halfOpenMagX = preferences.getFloat("halfOpenMagX", 0);
halfOpenMagY = preferences.getFloat("halfOpenMagY", 0);
halfOpenMagZ = preferences.getFloat("halfOpenMagZ", 0);
}
void printCalibrationData() {
Serial.println("Kalibrierungsdaten:");
Serial.print("Geschlossen: Accel("); Serial.print(closedAccelX); Serial.print(", "); Serial.print(closedAccelY); Serial.print(", "); Serial.print(closedAccelZ); Serial.println(") Mag("); Serial.print(closedMagX); Serial.print(", "); Serial.print(closedMagY); Serial.print(", "); Serial.print(closedMagZ); Serial.println(")");
Serial.print("Offen: Accel("); Serial.print(openAccelX); Serial.print(", "); Serial.print(openAccelY); Serial.print(", "); Serial.print(openAccelZ); Serial.println(") Mag("); Serial.print(openMagX); Serial.print(", "); Serial.print(openMagY); Serial.print(", "); Serial.print(openMagZ); Serial.println(")");
Serial.print("Gekippt: Accel("); Serial.print(tiltedAccelX); Serial.print(", "); Serial.print(tiltedAccelY); Serial.print(", "); Serial.print(tiltedAccelZ); Serial.println(") Mag("); Serial.print(tiltedMagX); Serial.print(", "); Serial.print(tiltedMagY); Serial.print(", "); Serial.print(tiltedMagZ); Serial.println(")");
Serial.print("Halb offen: Accel("); Serial.print(halfOpenAccelX); Serial.print(", "); Serial.print(halfOpenAccelY); Serial.print(", "); Serial.print(halfOpenAccelZ); Serial.println(") Mag("); Serial.print(halfOpenMagX); Serial.print(", "); Serial.print(halfOpenMagY); Serial.print(", "); Serial.print(halfOpenMagZ); Serial.println(")");
}
bool isButtonPressed() {
bool buttonState = digitalRead(buttonPin) == LOW; // LOW = gedrückt
if (buttonState && (millis() - lastDebounceTime > debounceDelay)) {
lastDebounceTime = millis();
return true;
}
return false;
}```
Best regards,
Leon