From 1174622b4a95285fff8f425480752287c38f5332 Mon Sep 17 00:00:00 2001
From: Thomas Hooge <thomas@hoogi.de>
Date: Fri, 24 Jan 2025 12:42:07 +0100
Subject: [PATCH] System page for OBP40 and deep sleep improvements

---
 lib/obp60task/OBP60Extensions.cpp |  26 +++++++
 lib/obp60task/OBP60Extensions.h   |   2 -
 lib/obp60task/OBP60Keypad.h       |  10 ++-
 lib/obp60task/PageSystem.cpp      | 109 +++++++++++++++++++++---------
 lib/obp60task/config_obp40.json   |  11 +++
 lib/obp60task/obp60task.cpp       |  61 +++++++----------
 6 files changed, 144 insertions(+), 75 deletions(-)

diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp
index a98cba5..9f7df24 100644
--- a/lib/obp60task/OBP60Extensions.cpp
+++ b/lib/obp60task/OBP60Extensions.cpp
@@ -146,6 +146,32 @@ void deepSleep(CommonData &common){
     getdisplay().powerOff();                // Display power off
     setPortPin(OBP_POWER_50, false);        // Power off ePaper display
     // Stop system
+    esp_deep_sleep_start();             // Deep Sleep with weakup via touch pin
+}
+#endif
+#ifdef BOARD_OBP40S3
+// Deep sleep funktion
+void deepSleep(CommonData &common){
+    RTC_lastpage = common.data.actpage - 1;
+    // Switch off all power lines
+    setPortPin(OBP_BACKLIGHT_LED, false);   // Backlight Off
+    setFlashLED(false);                     // Flash LED Off
+    // Shutdown EInk display
+    getdisplay().setFullWindow();               // Set full Refresh
+    //getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
+    getdisplay().fillScreen(common.bgcolor);    // Clear screen
+    getdisplay().setTextColor(common.fgcolor);
+    getdisplay().setFont(&Ubuntu_Bold20pt7b);
+    getdisplay().setCursor(85, 150);
+    getdisplay().print("Sleep Mode");
+    getdisplay().setFont(&Ubuntu_Bold8pt7b);
+    getdisplay().setCursor(65, 175);
+    getdisplay().print("For wakeup press wheel and wait 5s");
+    getdisplay().nextPage();                // Partial update
+    getdisplay().powerOff();                // Display power off
+    setPortPin(OBP_POWER_EPD, false);       // Power off ePaper display
+    setPortPin(OBP_POWER_SD, false);        // Power off SD card
+    // Stop system
     esp_deep_sleep_start();             // Deep Sleep with weakup via GPIO pin
 }
 #endif
diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h
index 727f912..b6f653f 100644
--- a/lib/obp60task/OBP60Extensions.h
+++ b/lib/obp60task/OBP60Extensions.h
@@ -64,9 +64,7 @@ Point rotatePoint(const Point& origin, const Point& p, double angle);
 std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle);
 void fillPoly4(const std::vector<Point>& p4, uint16_t color);
 
-#ifdef BOARD_OBP60S3
 void deepSleep(CommonData &common);
-#endif
 
 uint8_t getLastPage();
 
diff --git a/lib/obp60task/OBP60Keypad.h b/lib/obp60task/OBP60Keypad.h
index 122355c..937f44e 100644
--- a/lib/obp60task/OBP60Keypad.h
+++ b/lib/obp60task/OBP60Keypad.h
@@ -60,7 +60,7 @@ void initKeys(CommonData &commonData) {
 
   #ifdef HARDWARE_V21
   // Keypad functions for original OBP60 hardware
-  int readKeypad(GwLog* logger, uint thSensitivity) {
+  int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
     
     // Touch sensor values
     // 35000 - Not touched
@@ -261,7 +261,7 @@ void initKeys(CommonData &commonData) {
     }
 
   // Keypad functions for OBP60 clone (thSensitivity is inactiv)
-  int readKeypad(GwLog* logger, uint thSensitivity) {
+  int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
     pinMode(UP, INPUT);
     pinMode(DOWN, INPUT);
     pinMode(CONF, INPUT);
@@ -279,7 +279,11 @@ void initKeys(CommonData &commonData) {
       }
       // If key pressed longer than 200ms
       if(millis() > starttime + 200 && keycode == keycodeold) {
-        keystatus = keycode;
+        if (use_syspage and keycode == 3) {
+            keystatus = 12;
+        } else {
+            keystatus = keycode;
+        }
         // Copy keycode
         keycodeold = keycode;
         while(readSensorpads() > 0){} // Wait for pad release
diff --git a/lib/obp60task/PageSystem.cpp b/lib/obp60task/PageSystem.cpp
index e56d94b..7f601a1 100644
--- a/lib/obp60task/PageSystem.cpp
+++ b/lib/obp60task/PageSystem.cpp
@@ -4,6 +4,7 @@
 #include "OBP60Extensions.h"
 #include "images/logo64.xbm"
 #include <esp32/clk.h>
+#include "qrcode.h"
 
 #define STRINGIZE_IMPL(x) #x
 #define STRINGIZE(x) STRINGIZE_IMPL(x)
@@ -66,7 +67,8 @@ public:
             if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
             return 0;
         }
-        // grab cursor keys to disable page navigation
+#ifdef BOARD_OBP60S3
+        // grab cursor key to disable page navigation
         if (key == 3) {
             return 0;
         }
@@ -74,20 +76,55 @@ public:
         if (key == 4) {
             ESP.restart();
         }
-#ifdef BOARD_OBP60S3
         // standby / deep sleep
         if (key == 5) {
            deepSleep(*commonData);
         }
-#endif
         // Code for keylock
         if (key == 11) {
             commonData->keylock = !commonData->keylock;
             return 0;
         }
+#endif
+#ifdef BOARD_OBP40S3
+        // grab cursor keys to disable page navigation
+        if (key == 9 or key == 10) {
+            return 0;
+        }
+        // standby / deep sleep
+        if (key == 12) {
+            deepSleep(*commonData);
+        }
+#endif
         return key;
     }
 
+    void displayBarcode(String serialno, uint16_t x, uint16_t y, uint16_t s) {
+        // Barcode with serial number
+        // x, y is top left corner
+        // s is pixel size of a single box
+        QRCode qrcode;
+        uint8_t qrcodeData[qrcode_getBufferSize(4)];
+        #ifdef BOARD_OBP40S3
+        String prefix = "OBP40:SN:";
+        #endif
+        #ifdef BOARD_OBP60S3
+        String prefix = "OBP60:SN:";
+        #endif
+        qrcode_initText(&qrcode, qrcodeData, 4, 0, (prefix + serialno).c_str());
+        int16_t x0 = x;
+        for (uint8_t j = 0; j < qrcode.size; j++) {
+            for (uint8_t i = 0; i < qrcode.size; i++) {
+                if (qrcode_getModule(&qrcode, i, j)) {
+                    getdisplay().fillRect(x, y, s, s, commonData->fgcolor);
+                }
+                x += s;
+            }
+            y += s;
+            x = x0;
+        }
+    }
+
     virtual void displayPage(PageData &pageData){
         GwConfigHandler *config = commonData->config;
         GwLog *logger = commonData->logger;
@@ -114,30 +151,36 @@ public:
 
         if (mode == 'N') {
             getdisplay().setFont(&Ubuntu_Bold12pt7b);
-            getdisplay().setCursor(20, 50);
+            getdisplay().setCursor(8, 50);
             getdisplay().print("System Information");
 
             getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
 
             getdisplay().setFont(&Ubuntu_Bold8pt7b);
 
-            char ssid[23];
-            snprintf(ssid, 23, "MCUDEVICE-%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
-            getdisplay().setCursor(20, 70);
-            getdisplay().print(ssid);
-            getdisplay().setCursor(20, 100);
-            getdisplay().print("Press STBY for white page and standby");
+            char ssid[13];
+            snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
+            displayBarcode(String(ssid), 320, 200, 2);
+            getdisplay().setCursor(8, 70);
+            getdisplay().print(String("MUDEVICE-") + String(ssid));
+
+            getdisplay().setCursor(8, 90);
+            getdisplay().print("Firmware Version: ");
+            getdisplay().print(VERSINFO);
+
+            getdisplay().setCursor(8, 265);
+            #ifdef BOARD_OBP60S3
+            getdisplay().print("Press STBY to enter deep sleep mode");
+            #endif
+            #ifdef BOARD_OBP40S3
+            getdisplay().print("Press wheel to enter deep sleep mode");
+            #endif
 
             getdisplay().setCursor(2, y0);
             getdisplay().print("Simulation:");
             getdisplay().setCursor(120, y0);
             getdisplay().print(simulation ? "on" : "off");
 
-            getdisplay().setCursor(202, y0);
-            getdisplay().print("Wifi:");
-            getdisplay().setCursor(300, y0);
-            getdisplay().print(commonData->status.wifiApOn ? "On" : "Off");
-
             getdisplay().setCursor(2, y0 + 16);
             getdisplay().print("Environment:");
             getdisplay().setCursor(120, y0 + 16);
@@ -145,9 +188,9 @@ public:
 
             // total RAM free
             int Heap_free = esp_get_free_heap_size();
-            getdisplay().setCursor(202, y0 + 16);
+            getdisplay().setCursor(202, y0);
             getdisplay().print("Total free:");
-            getdisplay().setCursor(300, y0 + 16);
+            getdisplay().setCursor(300, y0);
             getdisplay().print(String(Heap_free));
 
             getdisplay().setCursor(2, y0 + 32);
@@ -157,37 +200,39 @@ public:
 
             // RAM free for task
             int RAM_free = uxTaskGetStackHighWaterMark(NULL);
-            getdisplay().setCursor(202, y0 + 32);
+            getdisplay().setCursor(202, y0 + 16);
             getdisplay().print("Task free:");
-            getdisplay().setCursor(300, y0 + 32);
+            getdisplay().setCursor(300, y0 + 16);
             getdisplay().print(String(RAM_free));
 
-            getdisplay().setCursor(2, y0 + 48);
+            // FRAM available / status
+            getdisplay().setCursor(202, y0 + 32);
+            getdisplay().print("FRAM:");
+            getdisplay().setCursor(300, y0 + 32);
+            getdisplay().print(hasFRAM ? "available" : "not found");
+
+            getdisplay().setCursor(202, y0 + 64);
             getdisplay().print("CPU speed:");
-            getdisplay().setCursor(120, y0 + 48);
+            getdisplay().setCursor(300, y0 + 64);
             getdisplay().print(cpuspeed);
             getdisplay().print(" / ");
             int cpu_freq = esp_clk_cpu_freq() / 1000000;
             getdisplay().print(String(cpu_freq));
 
-            getdisplay().setCursor(202, y0 + 64);
+            getdisplay().setCursor(2, y0 + 64);
             getdisplay().print("GPS:");
-            getdisplay().setCursor(300, y0 + 64);
+            getdisplay().setCursor(120, y0 + 64);
             getdisplay().print(gps_module);
 
             getdisplay().setCursor(2, y0 + 80);
-            getdisplay().print("FRAM:");
-            getdisplay().setCursor(120, y0 + 80);
-            getdisplay().print(hasFRAM ? "available" : "not found");
-
-            getdisplay().setCursor(202, y0 + 80);
             getdisplay().print("RTC:");
-            getdisplay().setCursor(300, y0 + 80);
+            getdisplay().setCursor(120, y0 + 80);
             getdisplay().print(rtc_module);
 
-            getdisplay().setCursor(2, y0 + 120);
-            getdisplay().print("Firmware Version: ");
-            getdisplay().print(VERSINFO);
+            getdisplay().setCursor(2, y0 + 96);
+            getdisplay().print("Wifi:");
+            getdisplay().setCursor(120, y0 + 96);
+            getdisplay().print(commonData->status.wifiApOn ? "On" : "Off");
 
 
         } else {
diff --git a/lib/obp60task/config_obp40.json b/lib/obp60task/config_obp40.json
index d18533c..3a4b6bf 100644
--- a/lib/obp60task/config_obp40.json
+++ b/lib/obp60task/config_obp40.json
@@ -913,6 +913,17 @@
             "obp40": "true"
         }
     },
+    {
+        "name": "systemPage",
+        "label": "System Page",
+        "type": "boolean",
+        "default": "false",
+        "description": "Use wheel button for system page or direct deep sleep mode",
+        "category":"OBP40 Pages",
+        "capabilities": {
+            "obp40": "true"
+        }
+    },
     {
         "name": "imageFormat",
         "label": "Screenshot Format",
diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp
index 3defb2b..a55e62b 100644
--- a/lib/obp60task/obp60task.cpp
+++ b/lib/obp60task/obp60task.cpp
@@ -118,7 +118,7 @@ void OBP60Init(GwApi *api){
     #endif
 
     #ifdef BOARD_OBP60S3
-    touchSleepWakeUpEnable(TP1, 45);
+    touchSleepWakeUpEnable(TP1, 45); // TODO sensitivity should be configurable via web interface
     touchSleepWakeUpEnable(TP2, 45);
     touchSleepWakeUpEnable(TP3, 45);
     touchSleepWakeUpEnable(TP4, 45);
@@ -169,6 +169,7 @@ typedef struct {
         GwLog* logger = NULL;
 //        GwApi* api = NULL;
         uint sensitivity = 100;
+        bool use_syspage = true;
     } MyData;
 
 // Keyboard Task
@@ -180,7 +181,7 @@ void keyboardTask(void *param){
 
     // Loop for keyboard task
     while (true){
-        keycode = readKeypad(data->logger, data->sensitivity);
+        keycode = readKeypad(data->logger, data->sensitivity, data->use_syspage);
         //send a key event
         if(keycode != 0){
             xQueueSend(data->queue, &keycode, 0);
@@ -336,38 +337,6 @@ void underVoltageDetection(GwApi *api, CommonData &common){
     }
 }
 
-#ifdef BOARD_OBP40S3
-// Deep sleep funktion 
-void deepSleep(CommonData &common){
-    // Switch off all power lines
-    setPortPin(OBP_BACKLIGHT_LED, false);   // Backlight Off
-    setFlashLED(false);                     // Flash LED Off            
-    buzzer(TONE4, 20);                      // Buzzer tone 4kHz 20ms
-    // Shutdown EInk display
-    getdisplay().setFullWindow();               // Set full Refresh
-    //getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
-    getdisplay().fillScreen(common.bgcolor);    // Clear screen
-    getdisplay().setTextColor(common.fgcolor);
-    getdisplay().setFont(&Ubuntu_Bold20pt7b);
-    getdisplay().setCursor(85, 150);
-    getdisplay().print("Sleep Mode");
-    getdisplay().setFont(&Ubuntu_Bold8pt7b);
-    getdisplay().setCursor(65, 175);
-    getdisplay().print("For wakeup press wheel and wait 5s");
-    getdisplay().nextPage();                // Partial update
-    getdisplay().powerOff();                // Display power off
-    setPortPin(OBP_POWER_EPD, false);       // Power off ePaper display
-    setPortPin(OBP_POWER_SD, false);        // Power off SD card
-    // Stop system
-    while(true){
-        esp_deep_sleep_start();             // Deep Sleep with weakup via GPIO pin
-    }
-
-}
-#endif
-
-
-
 // OBP60 Task
 //####################################################################################
 void OBP60Task(GwApi *api){
@@ -412,6 +381,9 @@ void OBP60Task(GwApi *api){
     bool refreshmode = api->getConfig()->getConfigItem(api->getConfig()->refresh,true)->asBoolean();
     String fastrefresh = api->getConfig()->getConfigItem(api->getConfig()->fastRefresh,true)->asString();
     uint fullrefreshtime = uint(api->getConfig()->getConfigItem(api->getConfig()->fullRefreshTime,true)->asInt());
+    #ifdef BOARD_OBP40S3
+    bool syspage_enabled = config->getBool(config->systemPage);
+    #endif
 
     #ifdef DISPLAY_GDEY042T81
         getdisplay().init(115200, true, 2, false);  // Use this for Waveshare boards with "clever" reset circuit, 2ms reset pulse
@@ -453,16 +425,24 @@ void OBP60Task(GwApi *api){
     // Set start page
     int pageNumber = int(api->getConfig()->getConfigItem(api->getConfig()->startPage,true)->asInt()) - 1;
 
-#ifdef BOARD_OBP60S3
     LOG_DEBUG(GwLog::LOG,"Checking wakeup...");
+#ifdef BOARD_OBP60S3
     if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TOUCHPAD) {
         LOG_DEBUG(GwLog::LOG,"Wake up by touch pad %d",esp_sleep_get_touchpad_wakeup_status());
         pageNumber = getLastPage();
     } else {
         LOG_DEBUG(GwLog::LOG,"Other wakeup reason");
     }
-    LOG_DEBUG(GwLog::LOG,"...done");
 #endif
+#ifdef BOARD_OBP40S3
+    if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
+        LOG_DEBUG(GwLog::LOG,"Wake up by key");
+        pageNumber = getLastPage();
+    } else {
+        LOG_DEBUG(GwLog::LOG,"Other wakeup reason");
+    }
+#endif
+    LOG_DEBUG(GwLog::LOG,"...done");
 
     int lastPage=pageNumber;
 
@@ -525,6 +505,9 @@ void OBP60Task(GwApi *api){
     allParameters.page0=3;
     allParameters.queue=xQueueCreate(10,sizeof(int));
     allParameters.sensitivity= api->getConfig()->getInt(GwConfigDefinitions::tSensitivity);
+    #ifdef BOARD_OBP40S3
+    allParameters.use_syspage = syspage_enabled;
+    #endif
     xTaskCreate(keyboardTask,"keyboard",2000,&allParameters,configMAX_PRIORITIES-1,NULL);
     SharedData *shared=new SharedData(api);
     createSensorTask(shared);
@@ -621,10 +604,11 @@ void OBP60Task(GwApi *api){
                 LOG_DEBUG(GwLog::LOG,"new key from keyboard %d",keyboardMessage);
                 keypressed = true;
 
-                if (keyboardMessage == 12) {
+                if (keyboardMessage == 12 and !systemPage) {
                     LOG_DEBUG(GwLog::LOG, "Calling system page");
                     systemPage = true; // System page is out of band
                     syspage->setupKeys();
+                    keyboardMessage = 0;
                 }
                 else {
                     currentPage = pages[pageNumber].page;
@@ -632,6 +616,7 @@ void OBP60Task(GwApi *api){
                         // exit system mode with exit key number 1
                         systemPage = false;
                         currentPage->setupKeys();
+                        keyboardMessage = 0;
                     }
                 }
                 if (systemPage) {
@@ -652,7 +637,7 @@ void OBP60Task(GwApi *api){
                     }
                     #ifdef BOARD_OBP40S3
                     // #3 Deep sleep mode for OBP40
-                    if (keyboardMessage == 3){
+                    if ((keyboardMessage == 3) and !syspage_enabled){
                         deepSleep(commonData);
                     }
                     #endif