From a037351f217e234c6d3aea7a888f81538cb107c5 Mon Sep 17 00:00:00 2001
From: Jan Grewe <jan@faked.org>
Date: Fri, 31 Jul 2020 01:52:28 +0200
Subject: [PATCH] first working version with deepsleep

---
 platformio.ini      |   2 -
 src/config.h.sample |  13 +--
 src/main.cpp        | 236 ++++++++++++++++++++++++++++++--------------
 3 files changed, 164 insertions(+), 87 deletions(-)

diff --git a/platformio.ini b/platformio.ini
index 925298c..a75b147 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -25,6 +25,4 @@ lib_deps =
   AXP202X_Library@1.1.2
   TinyGPSPlus@1.0.2
   MCCI Arduino LoRaWAN Library@0.8.0
-  OneButton@0.0.0-alpha+sha.eb583d713a
   CayenneLPP@1.1.0
-  
diff --git a/src/config.h.sample b/src/config.h.sample
index a260387..a457d04 100644
--- a/src/config.h.sample
+++ b/src/config.h.sample
@@ -1,10 +1,5 @@
+#define TIME_TO_SLEEP 900   // 15 minutes
 
-//char DevEUIString[] = "0102030405060708";
-//char AppEUIString[] = "70B3D50A0B0C0D0E";
-//char AppKeyString[] = "00112233445566778899AABBCCDDEEFF";
-
-#define TTN_DEVEUI { 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }; // LSB / little-endian / "byte order reversed"
-#define TTN_APPEUI { 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0xD5, 0xB3, 0x70 }; // LSB / little-endian / "byte order reversed"
-#define TTN_APPKEY { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; // MSB / big-endian / "byte order same as string"
-
-#define BUTTON_PIN 38
+#define TTN_DEVEUI { 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 };  // LSB
+#define TTN_APPEUI { 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0xD5, 0xB3, 0x70 };  // LSB
+#define TTN_APPKEY { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };  // MSB
diff --git a/src/main.cpp b/src/main.cpp
index e9ffc7d..b7e847c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -55,7 +55,7 @@ const cLoRaWAN::lmic_pinmap pinMap = {
     .nss = 18,
     .rxtx = cLoRaWAN::lmic_pinmap::LMIC_UNUSED_PIN,
     .rst = 23,
-    .dio = {26, 33, cLoRaWAN::lmic_pinmap::LMIC_UNUSED_PIN},
+    .dio = {26, 33, 32},
 };
 
 AXP20X_Class axp;
@@ -64,8 +64,11 @@ TinyGPSPlus gps;
 cLoRaWAN LoRaWAN{};
 CayenneLPP lpp(55);
 DynamicJsonDocument jsonBuffer(1024);
-OneButton btn = OneButton(BUTTON_PIN, true, true);
-unsigned long statusTimer;
+bool gpsFix = false;
+bool statusSending = false;
+bool statusSent = false;
+unsigned long timerGpsFix;
+RTC_DATA_ATTR int bootCount = 0;
 
 void onEvent(ev_t ev)
 {
@@ -90,6 +93,7 @@ void onEvent(ev_t ev)
         break;
     case EV_JOINED:
         Serial.println(F("EV_JOINED"));
+        axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
         break;
     case EV_RFU1:
         Serial.println(F("EV_RFU1"));
@@ -111,6 +115,9 @@ void onEvent(ev_t ev)
             Serial.println(LMIC.dataLen);
             Serial.println(F(" bytes of payload"));
         }
+        // this allows going to deep sleep
+        statusSending = false;
+        statusSent = true;
         break;
     case EV_LOST_TSYNC:
         Serial.println(F("EV_LOST_TSYNC"));
@@ -138,6 +145,9 @@ void onEvent(ev_t ev)
         break;
     case EV_JOIN_TXCOMPLETE:
         Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
+        // this allows restarting
+        statusSending = false;
+        statusSent = false;
         break;
     default:
         Serial.println(F("Unknown event " + (unsigned)ev));
@@ -145,39 +155,8 @@ void onEvent(ev_t ev)
     }
 }
 
-void setupPower()
-{
-    if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS))
-    {
-        Serial.println("AXP192 Begin PASS");
-    }
-    else
-    {
-        Serial.println("AXP192 Begin FAIL");
-    }
-    axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); // OLED  : off
-    axp.setPowerOutPut(AXP192_DCDC2, AXP202_OFF); // N/C   : off
-    axp.setPowerOutPut(AXP192_DCDC3, AXP202_ON);  // ESP32 : on
-    axp.setPowerOutPut(AXP192_LDO2, AXP202_ON);   // LORA  : on
-    axp.setPowerOutPut(AXP192_LDO3, AXP202_ON);   // GPS   : on
-    axp.setDCDC3Voltage(3300);                    // ESP32 : 3.3V
-    axp.setLDO2Voltage(3300);                     // LORA  : 3.3V
-    axp.setLDO3Voltage(3300);                     // GPS   : 3.3V
-    axp.setChgLEDMode(AXP20X_LED_OFF);
-}
-
-void setupGps()
+void printPowerStatus()
 {
-    GPS.begin(9600, SERIAL_8N1, 34, 12); // IO34: RX, IO12: TX
-}
-
-void printStatus()
-{
-    axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
-    while (GPS.available())
-    {
-        gps.encode(GPS.read());
-    }
     Serial.println("##############################");
     Serial.println("--- Power ---");
     Serial.printf("DCDC1/OLED Status   : %s\n", axp.isDCDC1Enable() ? "enabled" : "disabled");
@@ -204,79 +183,184 @@ void printStatus()
     }
     Serial.println("--- Chip ---");
     Serial.printf("Chip Temp : %.1f C°\n", axp.getTemp() / 10);
+    Serial.println("##############################");
+}
+
+void printGpsStatus()
+{
+    while (GPS.available())
+    {
+        gps.encode(GPS.read());
+    }
+    Serial.println("##############################");
     Serial.println("--- GPS ---");
-    Serial.print("Latitude   : ");
-    Serial.println(gps.location.lat(), 5);
-    Serial.print("Longitude  : ");
-    Serial.println(gps.location.lng(), 4);
-    Serial.print("Satellites : ");
-    Serial.println(gps.satellites.value());
-    Serial.print("Altitude   : ");
-    Serial.print(gps.altitude.meters());
-    Serial.println("M");
-    Serial.print("Time       : ");
-    Serial.print(gps.time.hour());
-    Serial.print(":");
-    Serial.print(gps.time.minute());
-    Serial.print(":");
-    Serial.println(gps.time.second());
-    Serial.print("Speed      : ");
-    Serial.println(gps.speed.kmph());
+    Serial.printf("Has Fix    : %s\n", gps.satellites.isValid() ? "true" : "false");
+    Serial.printf("Latitude   : %f\n", gps.location.lat());
+    Serial.printf("Longitude  : %f\n", gps.location.lng());
+    Serial.printf("Satellites : %d\n", gps.satellites.value());
+    Serial.printf("Altitude   : %g m\n", gps.altitude.meters());
+    Serial.printf("Time       : %02d:%02d:%02d\n", gps.time.hour(), gps.time.minute(), gps.time.second());
+    Serial.printf("Speed      : %g\n", gps.speed.kmph());
     Serial.println("##############################");
-    axp.setChgLEDMode(AXP20X_LED_OFF);
 }
 
-void do_send()
+void setupPower()
 {
-    // Check if there is not a current TX/RX job running
-    if (LMIC.opmode & OP_TXRXPEND)
+    if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS))
     {
-        Serial.println(F("OP_TXRXPEND, not sending"));
+        Serial.println("AXP192 Begin PASS");
     }
     else
     {
+        Serial.println("AXP192 Begin FAIL");
+    }
+    axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); // OLED  : off
+    axp.setPowerOutPut(AXP192_DCDC2, AXP202_OFF); // N/C   : off
+    axp.setPowerOutPut(AXP192_DCDC3, AXP202_ON);  // ESP32 : on
+    axp.setPowerOutPut(AXP192_LDO2, AXP202_ON);   // LORA  : on
+    axp.setPowerOutPut(AXP192_LDO3, AXP202_ON);   // GPS   : on
+    /*
+    axp.setDCDC3Voltage(3300);                    // ESP32 : 3.3V
+    axp.setLDO2Voltage(3300);                     // LORA  : 3.3V
+    axp.setLDO3Voltage(3300);                     // GPS   : 3.3V
+    */
+    axp.setChgLEDMode(AXP20X_LED_OFF);
+    printPowerStatus();
+}
+
+void setupGps()
+{
+    GPS.begin(9600, SERIAL_8N1, 34, 12); // IO34: RX, IO12: TX
+}
+
+bool checkGpsFix()
+{
+    if (millis() - timerGpsFix >= 1000 * 1) // 1 second
+    {
+        Serial.println("Waiting for GPS fix...");
         while (GPS.available())
         {
             gps.encode(GPS.read());
         }
-        lpp.reset();
-        lpp.addGPS(1, gps.location.lat(), gps.location.lng(), gps.altitude.meters());
-        lpp.addAnalogInput(2, axp.getBattVoltage()/1000);
+        if (gps.satellites.value() >= 3 &&
+            gps.satellites.isValid() &&
+            gps.location.isValid())
+        {
+            printGpsStatus();
+            return true;
+        }
+        timerGpsFix = millis();
+    }
+    return false;
+}
+
+void createPayload()
+{
+    while (GPS.available())
+    {
+        gps.encode(GPS.read());
+    }
+    lpp.reset();
+    lpp.addGPS(1, gps.location.lat(), gps.location.lng(), gps.altitude.meters());
+    lpp.addAnalogInput(2, axp.getBattVoltage() / 1000);
+    // debug output
+    JsonObject json = jsonBuffer.to<JsonObject>();
+    lpp.decodeTTN(lpp.getBuffer(), lpp.getSize(), json);
+    serializeJsonPretty(json, Serial);
+    Serial.println();
+}
+
+void sendPayload()
+{
+    if (LMIC.opmode & OP_TXRXPEND)
+    {
+        Serial.println(F("OP_TXRXPEND, not sending"));
+    }
+    else
+    {
         LMIC_setTxData2(1, lpp.getBuffer(), lpp.getSize(), 0);
         Serial.println(F("Packet queued"));
         Serial.println();
-        JsonObject json = jsonBuffer.to<JsonObject>();
-        lpp.decodeTTN(lpp.getBuffer(), lpp.getSize(), json);
-        serializeJsonPretty(json, Serial);
-        Serial.println();
     }
 }
 
-static void handleClick()
+void sendStatus()
 {
-    printStatus();
-    do_send();
+    Serial.println("Sending status...");
+    axp.setChgLEDMode(AXP20X_LED_BLINK_4HZ);
+    createPayload();
+    sendPayload();
+    statusSending = true;
+}
+
+void doSleep()
+{
+    Serial.println("Going to sleep for " + String(TIME_SLEEP) + " seconds.");
+    axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // LORA : off
+    axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // GPS  : off
+    axp.setChgLEDMode(AXP20X_LED_OFF);
+    esp_sleep_enable_ext0_wakeup(GPIO_NUM_38, LOW); // wake up with "user" button (middle)
+    esp_sleep_enable_timer_wakeup(1000000 * TIME_SLEEP);
+    esp_deep_sleep_start();
+}
+
+void print_wakeup_reason()
+{
+    esp_sleep_wakeup_cause_t wakeup_reason;
+
+    wakeup_reason = esp_sleep_get_wakeup_cause();
+
+    switch (wakeup_reason)
+    {
+    case ESP_SLEEP_WAKEUP_EXT0:
+        Serial.println("Wakeup caused by external signal using RTC_IO");
+        break;
+    case ESP_SLEEP_WAKEUP_EXT1:
+        Serial.println("Wakeup caused by external signal using RTC_CNTL");
+        break;
+    case ESP_SLEEP_WAKEUP_TIMER:
+        Serial.println("Wakeup caused by timer");
+        break;
+    case ESP_SLEEP_WAKEUP_TOUCHPAD:
+        Serial.println("Wakeup caused by touchpad");
+        break;
+    case ESP_SLEEP_WAKEUP_ULP:
+        Serial.println("Wakeup caused by ULP program");
+        break;
+    default:
+        Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason);
+        break;
+    }
 }
 
 void setup()
 {
     Serial.begin(115200);
     Wire.begin(21, 22);
+    ++bootCount;
+    Serial.println("Boot number: " + String(bootCount));
+    print_wakeup_reason();
     setupPower();
     setupGps();
     LoRaWAN.begin(pinMap);
-    btn.attachClick(handleClick);
-    printStatus();
-    statusTimer = millis();
+    timerGpsFix = millis();
+    axp.setChgLEDMode(AXP20X_LED_BLINK_1HZ);
 }
 
 void loop()
 {
     LoRaWAN.loop();
-    btn.tick();
-    if (millis() - statusTimer >= 1000 * 30) {
-        printStatus();
-        do_send();
-        statusTimer = millis();
-   }
+
+    if (!gpsFix)
+    {
+        gpsFix = checkGpsFix();
+    }
+    else if (gpsFix && !statusSending && !statusSent)
+    {
+        sendStatus();
+    }
+    else if (statusSent)
+    {
+        doSleep();
+    }
 }
-- 
GitLab