InfiniTime.git

commit 18686ac2cbf6b97dd0250234ce128bd9ad350d6e

Author: JF <jf@codingfield.com>

Merge branch 'manualValidation' of JF/PineTime into develop

 bootloader/README.md | 23 ++
 src/CMakeLists.txt | 6 
 src/Components/FirmwareValidator/FirmwareValidator.cpp | 20 ++
 src/Components/FirmwareValidator/FirmwareValidator.h | 18 ++
 src/DisplayApp/Apps.h | 2 
 src/DisplayApp/DisplayApp.cpp | 2 
 src/DisplayApp/DisplayApp.h | 2 
 src/DisplayApp/Screens/ApplicationList.cpp | 28 +-
 src/DisplayApp/Screens/ApplicationList.h | 2 
 src/DisplayApp/Screens/FirmwareValidation.cpp | 91 ++++++++++++
 src/DisplayApp/Screens/FirmwareValidation.h | 42 +++++
 src/DisplayApp/Screens/Symbols.h | 1 
 src/SystemTask/SystemTask.cpp | 8 -


diff --git a/bootloader/README.md b/bootloader/README.md
index 79112076de2540a7c9b86196869cc5066acb9f4a..34846f1bb034e40db0520c3d4b55ae7d11b35b9f 100644
--- a/bootloader/README.md
+++ b/bootloader/README.md
@@ -113,4 +113,25 @@ `
 sudo dfu.py -z /home/jf/nrf52/bootloader/dfu.zip -a <pinetime MAC address> --legacy
 `
 
-**Note** : dfu.py is a slightly modified version of [this repo](https://github.com/daniel-thompson/ota-dfu-python).
\ No newline at end of file
+**Note** : dfu.py is a slightly modified version of [this repo](https://github.com/daniel-thompson/ota-dfu-python).
+
+See [this page](../doc/CompanionApps/NrfconnectOTA.md) for more info about OTA with NRFConect
+
+### Firmware validation
+Once the OTA is done, InfiniTime will reset the watch to apply the update. When the watch reboots, the new firmware is running.
+
+One last step is needed to finalize the upgrade : the new firmware must be manually validated. If the watch resets while the image is not validated, the bootloader will automatically revert to the previous version of the firmware.
+
+If the new firmware is working correctly, open the application menu and tap on the 'check' app. This apps displays the version of the firmware that is currently running, and allows you to validate the firmware, or reset the device to rollback to the previous version.
+
+Firmware validation application in the menu:
+
+![Firmware Validation App](../doc/CompanionApps/firmwareValidationApp.jpg "Firmware Validation App")
+
+The firmware is not validated yet. Tap 'Validate' to validate it, or 'Reset' to rollback to the previous version.
+
+![Firmware Not Validated](../doc/CompanionApps/firmwareNoValidated.jpg "Firmware Not Validated")
+
+The firmware is validated!
+
+![Firmware Validated](../doc/CompanionApps/firmwareValidated.jpg "Firmware Validated")




diff --git a/doc/CompanionApps/firmwareNoValidated.jpg b/doc/CompanionApps/firmwareNoValidated.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..28df7eaa8497ef58ae4d057afbd2bdb27bcdb7cb
Binary files /dev/null and b/doc/CompanionApps/firmwareNoValidated.jpg differ




diff --git a/doc/CompanionApps/firmwareValidated.jpg b/doc/CompanionApps/firmwareValidated.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..0d6f99b5b900879016f7e4940a3775ef12ee2e01
Binary files /dev/null and b/doc/CompanionApps/firmwareValidated.jpg differ




diff --git a/doc/CompanionApps/firmwareValidationApp.jpg b/doc/CompanionApps/firmwareValidationApp.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..d78ad0c1331ec039ca6a627dc15db615bac2da57
Binary files /dev/null and b/doc/CompanionApps/firmwareValidationApp.jpg differ




diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b1c45ffa6db7f318c2f2a33586d706860381572d..c4487afe9bc300153070ccd5497fe81e380d2a3e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -337,6 +337,7 @@   DisplayApp/Screens/SystemInfo.cpp
   DisplayApp/Screens/Label.cpp
   DisplayApp/Screens/FirmwareUpdate.cpp
   DisplayApp/Screens/Music.cpp
+  DisplayApp/Screens/FirmwareValidation.cpp
   DisplayApp/Screens/ApplicationList.cpp
   main.cpp
   drivers/St7789.cpp
@@ -359,6 +360,7 @@   Components/Ble/DfuService.cpp
   Components/Ble/CurrentTimeService.cpp
   Components/Ble/AlertNotificationService.cpp
   Components/Ble/MusicService.cpp
+  Components/FirmwareValidator/FirmwareValidator.cpp
   drivers/Cst816s.cpp
   FreeRTOS/port.c
   FreeRTOS/port_cmsis_systick.c
@@ -417,6 +419,7 @@   DisplayApp/Screens/SystemInfo.h
   DisplayApp/Screens/ScreenList.h
   DisplayApp/Screens/Label.h
   DisplayApp/Screens/FirmwareUpdate.h
+  DisplayApp/Screens/FirmwareValidation.h
   DisplayApp/Screens/ApplicationList.h
   DisplayApp/Apps.h
   drivers/St7789.h
@@ -436,7 +439,8 @@   Components/Ble/DeviceInformationService.h
   Components/Ble/CurrentTimeClient.h
   Components/Ble/AlertNotificationClient.h
   Components/Ble/DfuService.h
-    drivers/Cst816s.h
+  Components/FirmwareValidator/FirmwareValidator.h
+  drivers/Cst816s.h
   FreeRTOS/portmacro.h
   FreeRTOS/portmacro_cmsis.h
   libs/date/includes/date/tz.h




diff --git a/src/Components/FirmwareValidator/FirmwareValidator.cpp b/src/Components/FirmwareValidator/FirmwareValidator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..244d5c06bf704afdc9727e02a5248a82c4e381f9
--- /dev/null
+++ b/src/Components/FirmwareValidator/FirmwareValidator.cpp
@@ -0,0 +1,20 @@
+#include <drivers/InternalFlash.h>
+#include <hal/nrf_rtc.h>
+
+#include "FirmwareValidator.h"
+
+using namespace Pinetime::Controllers;
+
+bool FirmwareValidator::IsValidated() const {
+  auto* imageOkPtr = reinterpret_cast<uint32_t *>(validBitAdress);
+  return (*imageOkPtr) == validBitValue;
+}
+
+void FirmwareValidator::Validate() {
+  if(!IsValidated())
+    Pinetime::Drivers::InternalFlash::WriteWord(validBitAdress, validBitValue);
+}
+
+void FirmwareValidator::Reset() {
+  NVIC_SystemReset();
+}




diff --git a/src/Components/FirmwareValidator/FirmwareValidator.h b/src/Components/FirmwareValidator/FirmwareValidator.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa576d8872157563227a112beab9401450a9444f
--- /dev/null
+++ b/src/Components/FirmwareValidator/FirmwareValidator.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <cstdint>
+
+namespace Pinetime {
+  namespace Controllers {
+    class FirmwareValidator {
+      public:
+        void Validate();
+        bool IsValidated() const;
+
+        void Reset();
+      private:
+        static constexpr uint32_t validBitAdress {0x7BFE8};
+        static constexpr uint32_t validBitValue {1};
+    };
+  }
+}




diff --git a/src/DisplayApp/Apps.h b/src/DisplayApp/Apps.h
index 2ee7429985395693187551bc310587e3a7140910..2666d905776f3692b94aed6564ea9fb5e4cd537d 100644
--- a/src/DisplayApp/Apps.h
+++ b/src/DisplayApp/Apps.h
@@ -2,6 +2,6 @@ #pragma once
 
 namespace Pinetime {
   namespace Applications {
-    enum class Apps {None, Launcher, Clock, SysInfo, Meter, Gauge, Brightness, Music};
+    enum class Apps {None, Launcher, Clock, SysInfo, Meter, Gauge, Brightness, Music, FirmwareValidation};
   }
 }
\ No newline at end of file




diff --git a/src/DisplayApp/DisplayApp.cpp b/src/DisplayApp/DisplayApp.cpp
index 1a530467dea75e3fe25d85709cb24b1f86e3c402..935578070e44eed06fdbdc4348dae18074b76678 100644
--- a/src/DisplayApp/DisplayApp.cpp
+++ b/src/DisplayApp/DisplayApp.cpp
@@ -16,6 +16,7 @@ #include 
 #include <Components/Ble/NotificationManager.h>
 #include <DisplayApp/Screens/FirmwareUpdate.h>
 #include <DisplayApp/Screens/ApplicationList.h>
+#include <DisplayApp/Screens/FirmwareValidation.h>
 #include "../SystemTask/SystemTask.h"
 
 using namespace Pinetime::Applications;
@@ -189,6 +190,7 @@       case Apps::Meter: currentScreen.reset(new Screens::Meter(this)); break;
       case Apps::Gauge: currentScreen.reset(new Screens::Gauge(this)); break;
       case Apps::Brightness : currentScreen.reset(new Screens::Brightness(this, brightnessController)); break;
       case Apps::Music : currentScreen.reset(new Screens::Music(this, systemTask.nimble().music())); break;
+      case Apps::FirmwareValidation: currentScreen.reset(new Screens::FirmwareValidation(this, validator)); break;
     }
     nextApp = Apps::None;
   }




diff --git a/src/DisplayApp/DisplayApp.h b/src/DisplayApp/DisplayApp.h
index 6bf020bbb94e89ee82f6aac25502f6433aa88883..a6df95c19829c0cc3f2bc6a27030b1f1d80a2bd8 100644
--- a/src/DisplayApp/DisplayApp.h
+++ b/src/DisplayApp/DisplayApp.h
@@ -17,6 +17,7 @@ #include 
 #include <drivers/Watchdog.h>
 #include <DisplayApp/Screens/Modal.h>
 #include <Components/Ble/NotificationManager.h>
+#include <Components/FirmwareValidator/FirmwareValidator.h>
 #include "TouchEvents.h"
 #include "Apps.h"
 
@@ -80,6 +81,7 @@         bool onClockApp = false; // TODO find a better way to know that we should handle gestures and button differently for the Clock app.
         Controllers::BrightnessController brightnessController;
         std::unique_ptr<Screens::Modal> modal;
         Pinetime::Controllers::NotificationManager& notificationManager;
+        Pinetime::Controllers::FirmwareValidator validator;
     };
   }
 }




diff --git a/src/DisplayApp/Screens/ApplicationList.cpp b/src/DisplayApp/Screens/ApplicationList.cpp
index c7c096f4152cb12d56a5b6e2efe4c0325059f771..575426f0c749a59fc315b1e0fe0c54edc3e82f84 100644
--- a/src/DisplayApp/Screens/ApplicationList.cpp
+++ b/src/DisplayApp/Screens/ApplicationList.cpp
@@ -11,7 +11,7 @@ ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp *app) :
         Screen(app),
         screens{app, {
                 [this]() -> std::unique_ptr<Screen> { return CreateScreen1(); },
-                //[this]() -> std::unique_ptr<Screen> { return CreateScreen2(); },
+                [this]() -> std::unique_ptr<Screen> { return CreateScreen2(); },
                 //[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
           }
         } {}
@@ -39,13 +39,15 @@ }
 
 std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
   std::array<Screens::Tile::Applications, 6> applications {
-          {{Symbols::asterisk, Apps::Meter},
-           {Symbols::tachometer, Apps::Gauge},
-           {Symbols::clock, Apps::Clock},
-           {Symbols::music, Apps::Music},
-           {Symbols::list, Apps::SysInfo},
-           {Symbols::sun, Apps::Brightness}
+          {{Symbols::clock, Apps::Clock},
+          {Symbols::music, Apps::Music},
+          {Symbols::sun, Apps::Brightness},
+          {Symbols::list, Apps::SysInfo},
+          {Symbols::check, Apps::FirmwareValidation},
+          {Symbols::none, Apps::None}
           }
+
+
   };
 
   return std::unique_ptr<Screen>(new Screens::Tile(app, applications));
@@ -53,12 +55,12 @@ }
 
 std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
   std::array<Screens::Tile::Applications, 6> applications {
-          {{"0", Apps::Meter},
-           {"1", Apps::Gauge},
-           {"2", Apps::Clock},
-           {"3", Apps::Music},
-           {"4", Apps::SysInfo},
-           {"5", Apps::Brightness}
+          {{Symbols::tachometer, Apps::Gauge},
+           {Symbols::asterisk, Apps::Meter},
+           {Symbols::none, Apps::None},
+           {Symbols::none, Apps::None},
+           {Symbols::none, Apps::None},
+           {Symbols::none, Apps::None}
           }
   };
 




diff --git a/src/DisplayApp/Screens/ApplicationList.h b/src/DisplayApp/Screens/ApplicationList.h
index 372cbb7aba384352a31340fa9acf18639f9e649e..a1e6811b3c3fcaaff970e6f3a69d7f1cf75f3d67 100644
--- a/src/DisplayApp/Screens/ApplicationList.h
+++ b/src/DisplayApp/Screens/ApplicationList.h
@@ -22,7 +22,7 @@           bool OnTouchEvent(TouchEvents event) override;
         private:
           bool running = true;
 
-          ScreenList<1> screens;
+          ScreenList<2> screens;
           std::unique_ptr<Screen> CreateScreen1();
           std::unique_ptr<Screen> CreateScreen2();
           std::unique_ptr<Screen> CreateScreen3();




diff --git a/src/DisplayApp/Screens/FirmwareValidation.cpp b/src/DisplayApp/Screens/FirmwareValidation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70d325755af6e4323cee5aab1321c049501dfd03
--- /dev/null
+++ b/src/DisplayApp/Screens/FirmwareValidation.cpp
@@ -0,0 +1,91 @@
+#include <libs/lvgl/lvgl.h>
+#include "FirmwareValidation.h"
+#include "../DisplayApp.h"
+#include "../../Version.h"
+#include "../../Components/FirmwareValidator/FirmwareValidator.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+namespace {
+  static void ButtonEventHandler(lv_obj_t * obj, lv_event_t event)
+  {
+    FirmwareValidation* screen = static_cast<FirmwareValidation *>(obj->user_data);
+    screen->OnButtonEvent(obj, event);
+  }
+
+}
+
+FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app,
+                                       Pinetime::Controllers::FirmwareValidator &validator)
+                                       : Screen{app}, validator{validator} {
+  labelVersionInfo = lv_label_create(lv_scr_act(), NULL);
+  lv_obj_align(labelVersionInfo, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0);
+  lv_label_set_text(labelVersionInfo, "Version : ");
+  lv_label_set_align(labelVersionInfo, LV_LABEL_ALIGN_LEFT);
+
+
+  labelVersionValue = lv_label_create(lv_scr_act(), NULL);
+  lv_obj_align(labelVersionValue, labelVersionInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+  lv_label_set_recolor(labelVersionValue, true);
+  sprintf(version, "%d.%d.%d", Version::Major(), Version::Minor(), Version::Patch());
+  lv_label_set_text(labelVersionValue, version);
+
+  labelIsValidated = lv_label_create(lv_scr_act(), NULL);
+  lv_obj_align(labelIsValidated, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 50);
+  lv_label_set_recolor(labelIsValidated, true);
+  lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK);
+  lv_obj_set_width(labelIsValidated, 240);
+
+  if(validator.IsValidated())
+    lv_label_set_text(labelIsValidated, "You have already\n#00ff00 validated# this firmware#");
+  else {
+    lv_label_set_text(labelIsValidated,
+                      "Please #00ff00 Validate# this version or\n#ff0000 Reset# to rollback to the previous version.");
+
+    buttonValidate = lv_btn_create(lv_scr_act(), NULL);
+    lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+    buttonValidate->user_data = this;
+    lv_obj_set_event_cb(buttonValidate, ButtonEventHandler);
+
+    labelButtonValidate = lv_label_create(buttonValidate, NULL);
+    lv_label_set_recolor(labelButtonValidate, true);
+    lv_label_set_text(labelButtonValidate, "#00ff00 Validate#");
+
+    buttonReset = lv_btn_create(lv_scr_act(), NULL);
+    buttonReset->user_data = this;
+    lv_obj_align(buttonReset, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
+    lv_obj_set_event_cb(buttonReset, ButtonEventHandler);
+
+    labelButtonReset = lv_label_create(buttonReset, NULL);
+    lv_label_set_recolor(labelButtonReset, true);
+    lv_label_set_text(labelButtonReset, "#ff0000 Reset#");
+  }
+}
+
+
+FirmwareValidation::~FirmwareValidation() {
+  lv_obj_clean(lv_scr_act());
+}
+
+bool FirmwareValidation::Refresh() {
+  return running;
+}
+
+bool FirmwareValidation::OnButtonPushed() {
+  running = false;
+  return true;
+}
+
+void FirmwareValidation::OnButtonEvent(lv_obj_t *object, lv_event_t event) {
+  if(object == buttonValidate && event == LV_EVENT_PRESSED) {
+    validator.Validate();
+    running  = false;
+  } else if(object == buttonReset && event == LV_EVENT_PRESSED) {
+    validator.Reset();
+  }
+
+}
+
+




diff --git a/src/DisplayApp/Screens/FirmwareValidation.h b/src/DisplayApp/Screens/FirmwareValidation.h
new file mode 100644
index 0000000000000000000000000000000000000000..947f5575ba0fa5a4eed356e7c26781b1d9049dda
--- /dev/null
+++ b/src/DisplayApp/Screens/FirmwareValidation.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <cstdint>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+
+namespace Pinetime {
+  namespace Controllers {
+    class FirmwareValidator;
+  }
+
+  namespace Applications {
+    namespace Screens {
+
+      class FirmwareValidation : public Screen{
+        public:
+          FirmwareValidation(DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator);
+          ~FirmwareValidation() override;
+
+          bool Refresh() override;
+          bool OnButtonPushed() override;
+
+          void OnButtonEvent(lv_obj_t *object, lv_event_t event);
+
+        private:
+          Pinetime::Controllers::FirmwareValidator& validator;
+
+          lv_obj_t* labelVersionInfo;
+          lv_obj_t* labelVersionValue;
+          char version[9];
+          lv_obj_t* labelIsValidated;
+          lv_obj_t* buttonValidate;
+          lv_obj_t* labelButtonValidate;
+          lv_obj_t* buttonReset;
+          lv_obj_t* labelButtonReset;
+          bool running = true;
+      };
+    }
+  }
+}




diff --git a/src/DisplayApp/Screens/Symbols.h b/src/DisplayApp/Screens/Symbols.h
index 940006d5dd50bac892d960c4fee0d3500c38a3d5..54c3f8f02b39b75667fc566e0b5534f5af82d90d 100644
--- a/src/DisplayApp/Screens/Symbols.h
+++ b/src/DisplayApp/Screens/Symbols.h
@@ -4,6 +4,7 @@ namespace Pinetime {
   namespace Applications {
     namespace Screens {
       namespace Symbols {
+        static constexpr char* none = "";
         static constexpr char* batteryFull = "\xEF\x89\x80";
         static constexpr char* batteryEmpty = "\xEF\x89\x84";
         static constexpr char* batteryThreeQuarter = "\xEF\x89\x81";




diff --git a/src/SystemTask/SystemTask.cpp b/src/SystemTask/SystemTask.cpp
index 9f57f6f6356b0d14a99dadd076f6454a7e15ba40..d4b78187d959ee03a28e4862297b0669e3e0c89a 100644
--- a/src/SystemTask/SystemTask.cpp
+++ b/src/SystemTask/SystemTask.cpp
@@ -58,14 +58,6 @@   APP_GPIOTE_INIT(2);
 
   spi.Init();
   spiNorFlash.Init();
-
-  // Write the 'image OK' flag if it's not already done
-  // TODO implement a better verification mecanism for the image (ask for user confirmation via UI/BLE ?)
-  uint32_t* imageOkPtr = reinterpret_cast<uint32_t *>(0x7BFE8);
-  uint32_t imageOk = *imageOkPtr;
-  if(imageOk != 1)
-    Pinetime::Drivers::InternalFlash::WriteWord(0x7BFE8, 1);
-
   nimbleController.Init();
   nimbleController.StartAdvertising();
   lcd.Init();