diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4a71b7e0..4744eaef 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,7 +44,7 @@ jobs: - name: Install Embedded Arm Toolchain arm-none-eabi-gcc if: steps.cache-toolchain.outputs.cache-hit != 'true' # Install toolchain if not found in cache - uses: fiam/arm-none-eabi-gcc@v1.0.2 + uses: fiam/arm-none-eabi-gcc@v1.0.4 with: # GNU Embedded Toolchain for Arm release name, in the V-YYYY-qZ format (e.g. "9-2019-q4") release: 9-2020-q2 @@ -83,10 +83,11 @@ jobs: if: steps.cache-mcuboot.outputs.cache-hit != 'true' # Install MCUBoot if not found in cache run: | cd ${{ runner.temp }} - git clone --branch v1.5.0 https://github.com/JuulLabs-OSS/mcuboot + git clone --branch v1.7.2 https://github.com/mcu-tools/mcuboot - name: Install imgtool dependencies - run: pip3 install --user -r ${{ runner.temp }}/mcuboot/scripts/requirements.txt + run: | + pip3 install --user -r ${{ runner.temp }}/mcuboot/scripts/requirements.txt - name: Install adafruit-nrfutil run: | @@ -99,6 +100,8 @@ jobs: - name: Checkout source files uses: actions/checkout@v2 + with: + submodules: recursive - name: Show files run: set ; pwd ; ls -l @@ -110,7 +113,7 @@ jobs: run: | mkdir -p build cd build - cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=${{ runner.temp }}/arm-none-eabi -DNRF5_SDK_PATH=${{ runner.temp }}/nrf5_sdk -DUSE_OPENOCD=1 ../ + cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=${{ runner.temp }}/arm-none-eabi -DNRF5_SDK_PATH=${{ runner.temp }}/nrf5_sdk -DUSE_OPENOCD=1 -DBUILD_DFU=1 ../ ######################################################################################### # Make and Upload DFU Package @@ -125,19 +128,10 @@ jobs: cd build make pinetime-mcuboot-app - - name: Create firmware image + - name: Unzip DFU package run: | - # The generated firmware binary looks like "pinetime-mcuboot-app-0.8.2.bin" - ls -l build/src/pinetime-mcuboot-app*.bin - ${{ runner.temp }}/mcuboot/scripts/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header build/src/pinetime-mcuboot-app*.bin build/src/pinetime-mcuboot-app-img.bin - ${{ runner.temp }}/mcuboot/scripts/imgtool.py verify build/src/pinetime-mcuboot-app-img.bin - - - name: Create DFU package - run: | - ~/.local/bin/adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/src/pinetime-mcuboot-app-img.bin build/src/pinetime-mcuboot-app-dfu.zip - unzip -v build/src/pinetime-mcuboot-app-dfu.zip # Unzip the package because Upload Artifact will zip up the files - unzip build/src/pinetime-mcuboot-app-dfu.zip -d build/src/pinetime-mcuboot-app-dfu + unzip build/src/pinetime-mcuboot-app-dfu*.zip -d build/src/pinetime-mcuboot-app-dfu - name: Upload DFU package uses: actions/upload-artifact@v2 diff --git a/CMakeLists.txt b/CMakeLists.txt index b442fc11..cc41a087 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(pinetime VERSION 1.3.0 LANGUAGES C CXX ASM) +project(pinetime VERSION 1.4.0 LANGUAGES C CXX ASM) set(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 14) diff --git a/README.md b/README.md index 6549ece9..0d5cad93 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,10 @@ -# PineTime +# InfiniTime [![Build PineTime Firmware](https://github.com/JF002/InfiniTime/workflows/Build%20PineTime%20Firmware/badge.svg?branch=master)](https://github.com/JF002/InfiniTime/actions) -> The PineTime is a free and open source smartwatch capable of running custom-built open operating systems. Some of the notable features include a heart rate monitor, a week-long battery as well as a capacitive touch IPS display that is legible in direct sunlight. It is a fully community driven side-project, which means that it will ultimately be up to the developers and end-users to determine when they deem the PineTime ready to ship. - -> We envision the PineTime as a companion for not only your PinePhone but also for your favorite devices — any phone, tablet, or even PC. - -*https://www.pine64.org/pinetime/* - -The **Pinetime** smartwatch is built around the NRF52832 MCU (512KB Flash, 64KB RAM), a 240*240 LCD display driven by the ST7789 controller, an accelerometer, a heart rate sensor, and a vibration motor. - -# InfiniTime - ![InfiniTime logo](images/infinitime-logo.jpg "InfiniTime Logo") -The goal of this project is to design an open-source firmware for the Pinetime smartwatch : +The goal of this project is to design an open-source firmware for the [Pinetime smartwatch](https://www.pine64.org/pinetime/) : - Code written in **modern C++**; - Build system based on **CMake**; @@ -22,6 +12,11 @@ The goal of this project is to design an open-source firmware for the Pinetime s - Using **[LittleVGL/LVGL 7](https://lvgl.io/)** as UI library... - ... and **[NimBLE 1.3.0](https://github.com/apache/mynewt-nimble)** as BLE stack. +## New to InfiniTime? + + - [Getting started with InfiniTime 1.0 (quick user guide, update bootloader and InfiniTime,...)](doc/gettingStarted/gettingStarted-1.0.md) + - [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/ota-gadgetbridge-nrfconnect.md) + ## Overview ![Pinetime screens](images/1.0.0/collage.png "PinetimeScreens") @@ -70,16 +65,12 @@ As of now, here is the list of achievements of this project: * [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS and Linux) * [Siglo](https://github.com/alexr4535/siglo) (on Linux) * **[Experimental]** [WebBLEWatch](https://hubmartin.github.io/WebBLEWatch/) Synchronize time directly from your web browser. [video](https://youtu.be/IakiuhVDdrY) + * **[Experimental]** [Infini-iOS](https://github.com/xan-m/Infini-iOS) (on iOS) - OTA (Over-the-air) update via BLE - [Bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader) based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/) ## Documentation -### Getting started - - - [Getting started with InfiniTime 1.0 (quick user guide, update bootloader and InfiniTime,...)](doc/gettingStarted/gettingStarted-1.0.md) - - [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/ota-gadgetbridge-nrfconnect.md) - ### Develop - [Generate the fonts and symbols](src/displayapp/fonts/README.md) diff --git a/doc/gettingStarted/ota-gadgetbridge-nrfconnect.md b/doc/gettingStarted/ota-gadgetbridge-nrfconnect.md index 1187a9b7..ffc27ed8 100644 --- a/doc/gettingStarted/ota-gadgetbridge-nrfconnect.md +++ b/doc/gettingStarted/ota-gadgetbridge-nrfconnect.md @@ -47,6 +47,8 @@ Read carefully the warning and tap **Install**: Wait for the transfer to finish. Your PineTime should reset and reboot with the new version of InfiniTime! +Don't forget to **validate** your firmware. In the InfiniTime go to the settings (swipe right, select gear icon) and Firmware option and click **validate**. Otherwise after reboot the previous firmware will be used. + ![Gadgetbridge 5](gadgetbridge5.jpg) ### Using NRFConnect @@ -64,6 +66,8 @@ Select **Distribution packet (ZIP)**: Browse to the DFU file you downloaded previously, the DFU transfer will start automatically. When the transfer is finished, your PineTime will reset and restart on the new version of InfiniTime! +Don't forget to **validate** your firmware. In the InfiniTime go to the settings (swipe right, select gear icon) and Firmware option and click **validate**. Otherwise after reboot the previous firmware will be used. + ![NRFConnect 3](nrfconnect3.jpg) ## How to flash InfiniTime using the SWD interface @@ -88,6 +92,10 @@ If you are using OpenOCD with a STLinkV2, you can find more info [on this page]( ### Using Gadgetbridge Good news! Gadgetbridge **automatically** synchronizes the time when connecting to your PineTime! +### Using any Chromium-based web browser +You can use it from your PC, Mac, Android. Browsers now have BLE support. +https://hubmartin.github.io/WebBLEWatch/ + ### Using NRFConnect You must enable the **CTS** *GATT server* into NRFConnect so that InfiniTime can synchronize the time with your smartphone. diff --git a/src/components/battery/BatteryController.cpp b/src/components/battery/BatteryController.cpp index cabf101b..4ef20a24 100644 --- a/src/components/battery/BatteryController.cpp +++ b/src/components/battery/BatteryController.cpp @@ -10,10 +10,7 @@ Battery* Battery::instance = nullptr; Battery::Battery() { instance = this; -} - -void Battery::Init() { - nrf_gpio_cfg_input(PinMap::Charging, static_cast GPIO_PIN_CNF_PULL_Pullup); + nrf_gpio_cfg_input(PinMap::Charging, static_cast GPIO_PIN_CNF_PULL_Disabled); } void Battery::Update() { @@ -76,5 +73,11 @@ void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) { nrfx_saadc_uninit(); isReading = false; + + systemTask->PushMessage(System::Messages::BatteryMeasurementDone); } } + +void Battery::Register(Pinetime::System::SystemTask* systemTask) { + this->systemTask = systemTask; +} diff --git a/src/components/battery/BatteryController.h b/src/components/battery/BatteryController.h index f33b05ef..8af27ea8 100644 --- a/src/components/battery/BatteryController.h +++ b/src/components/battery/BatteryController.h @@ -1,8 +1,7 @@ #pragma once #include #include -#include -#include +#include namespace Pinetime { namespace Controllers { @@ -11,8 +10,8 @@ namespace Pinetime { public: Battery(); - void Init(); void Update(); + void Register(System::SystemTask* systemTask); uint8_t PercentRemaining() const { return percentRemaining; @@ -47,6 +46,8 @@ namespace Pinetime { static void AdcCallbackStatic(nrfx_saadc_evt_t const* event); bool isReading = false; + + Pinetime::System::SystemTask* systemTask = nullptr; }; } } diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index a294ab78..a54ba976 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -114,7 +114,7 @@ namespace Pinetime { }; void setWakeUpMode(WakeUpMode wakeUp, bool enabled) { - if (!isWakeUpModeOn(wakeUp)) { + if (enabled != isWakeUpModeOn(wakeUp)) { settingsChanged = true; } settings.wakeUpMode.set(static_cast(wakeUp), enabled); diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index d6100ece..5e74baa0 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -136,9 +136,6 @@ void DisplayApp::InitHw() { brightnessController.Set(settingsController.GetBrightness()); } -uint32_t acc = 0; -uint32_t count = 0; -bool toggle = true; void DisplayApp::Refresh() { TickType_t queueTimeout; TickType_t delta; @@ -194,9 +191,6 @@ void DisplayApp::Refresh() { // clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected : // Screens::Clock::BleConnectionStates::NotConnected); break; - case Messages::UpdateBatteryLevel: - batteryController.Update(); - break; case Messages::NewNotification: LoadApp(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); break; diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h index 322505e6..8e4884db 100644 --- a/src/displayapp/Messages.h +++ b/src/displayapp/Messages.h @@ -7,7 +7,6 @@ namespace Pinetime { GoToRunning, UpdateDateTime, UpdateBleConnection, - UpdateBatteryLevel, TouchEvent, ButtonPushed, NewNotification, diff --git a/src/displayapp/screens/BatteryInfo.cpp b/src/displayapp/screens/BatteryInfo.cpp index 91c26512..ad9af153 100644 --- a/src/displayapp/screens/BatteryInfo.cpp +++ b/src/displayapp/screens/BatteryInfo.cpp @@ -55,8 +55,6 @@ BatteryInfo::~BatteryInfo() { void BatteryInfo::Refresh() { - batteryController.Update(); - batteryPercent = batteryController.PercentRemaining(); batteryVoltage = batteryController.Voltage(); diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index fd0b86da..417dff00 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -73,7 +73,6 @@ void Notifications::Refresh() { timeoutLinePoints[1].x = pos; lv_line_set_points(timeoutLine, timeoutLinePoints, 2); } - running = currentItem->IsRunning(); } bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) { @@ -153,7 +152,7 @@ Notifications::NotificationItem::NotificationItem(const char* title, uint8_t notifNb, Modes mode, Pinetime::Controllers::AlertNotificationService& alertNotificationService) - : notifNr {notifNr}, notifNb {notifNb}, mode {mode}, alertNotificationService {alertNotificationService} { + : mode {mode}, alertNotificationService {alertNotificationService} { lv_obj_t* container1 = lv_cont_create(lv_scr_act(), NULL); lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x222222)); diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h index f6f8b4c3..0b5271e7 100644 --- a/src/displayapp/screens/Notifications.h +++ b/src/displayapp/screens/Notifications.h @@ -43,21 +43,13 @@ namespace Pinetime { void OnCallButtonEvent(lv_obj_t*, lv_event_t event); private: - uint8_t notifNr = 0; - uint8_t notifNb = 0; - char pageText[4]; - lv_obj_t* container1; - lv_obj_t* t1; - lv_obj_t* l1; - lv_obj_t* l2; lv_obj_t* bt_accept; lv_obj_t* bt_mute; lv_obj_t* bt_reject; lv_obj_t* label_accept; lv_obj_t* label_mute; lv_obj_t* label_reject; - lv_obj_t* bottomPlaceholder; Modes mode; Pinetime::Controllers::AlertNotificationService& alertNotificationService; bool running = true; diff --git a/src/displayapp/screens/settings/Settings.cpp b/src/displayapp/screens/settings/Settings.cpp index f82b03c1..e3319f03 100644 --- a/src/displayapp/screens/settings/Settings.cpp +++ b/src/displayapp/screens/settings/Settings.cpp @@ -50,8 +50,8 @@ std::unique_ptr Settings::CreateScreen2() { std::array applications {{ {Symbols::shoe, "Steps", Apps::SettingSteps}, {Symbols::batteryHalf, "Battery", Apps::BatteryInfo}, + {Symbols::paintbrush, "PTS Colors", Apps::SettingPineTimeStyle}, {Symbols::check, "Firmware", Apps::FirmwareValidation}, - {Symbols::list, "About", Apps::SysInfo}, }}; return std::make_unique(1, 3, app, settingsController, applications); @@ -60,7 +60,7 @@ std::unique_ptr Settings::CreateScreen2() { std::unique_ptr Settings::CreateScreen3() { std::array applications {{ - {Symbols::paintbrush, "PTS Colors", Apps::SettingPineTimeStyle}, + {Symbols::list, "About", Apps::SysInfo}, {Symbols::none, "None", Apps::None}, {Symbols::none, "None", Apps::None}, {Symbols::none, "None", Apps::None}, diff --git a/src/main.cpp b/src/main.cpp index 441fbd95..0d4ba42f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,16 +81,13 @@ static constexpr uint32_t MaxTwiFrequencyWithoutHardwareBug {0x06200000}; Pinetime::Drivers::TwiMaster twiMaster {NRF_TWIM1, MaxTwiFrequencyWithoutHardwareBug, Pinetime::PinMap::TwiSda, Pinetime::PinMap::TwiScl}; Pinetime::Drivers::Cst816S touchPanel {twiMaster, touchPanelTwiAddress}; #ifdef PINETIME_IS_RECOVERY -static constexpr bool isFactory = true; #include "displayapp/DummyLittleVgl.h" #include "displayapp/DisplayAppRecovery.h" -Pinetime::Components::LittleVgl lvgl {lcd, touchPanel}; #else -static constexpr bool isFactory = false; #include "displayapp/LittleVgl.h" #include "displayapp/DisplayApp.h" -Pinetime::Components::LittleVgl lvgl {lcd, touchPanel}; #endif +Pinetime::Components::LittleVgl lvgl {lcd, touchPanel}; Pinetime::Drivers::Bma421 motionSensor {twiMaster, motionSensorTwiAddress}; Pinetime::Drivers::Hrs3300 heartRateSensor {twiMaster, heartRateSensorTwiAddress}; @@ -99,8 +96,8 @@ TimerHandle_t debounceTimer; TimerHandle_t debounceChargeTimer; Pinetime::Controllers::Battery batteryController; Pinetime::Controllers::Ble bleController; -void ble_manager_set_ble_connection_callback(void (*connection)()); -void ble_manager_set_ble_disconnection_callback(void (*disconnection)()); +static constexpr uint8_t pinTouchIrq = Pinetime::PinMap::Cst816sIrq; +static constexpr uint8_t pinPowerPresentIrq = Pinetime::PinMap::PowerPresent; Pinetime::Controllers::HeartRateController heartRateController; Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController); diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index 3a195e2d..ebe6a2b1 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -20,7 +20,9 @@ namespace Pinetime { EnableSleeping, DisableSleeping, OnNewDay, - OnChargingEvent + OnChargingEvent, + MeasureBatteryTimerExpired, + BatteryMeasurementDone, }; } } diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index d48b559a..18f5ecbf 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -49,6 +49,11 @@ void IdleTimerCallback(TimerHandle_t xTimer) { sysTask->OnIdle(); } +void MeasureBatteryTimerCallback(TimerHandle_t xTimer) { + auto* sysTask = static_cast(pvTimerGetTimerID(xTimer)); + sysTask->PushMessage(Pinetime::System::Messages::MeasureBatteryTimerExpired); +} + SystemTask::SystemTask(Drivers::SpiMaster& spi, Drivers::St7789& lcd, Pinetime::Drivers::SpiNorFlash& spiNorFlash, @@ -129,7 +134,8 @@ void SystemTask::Work() { twiMaster.Init(); touchPanel.Init(); dateTimeController.Register(this); - batteryController.Init(); + batteryController.Register(this); + batteryController.Update(); motorController.Init(); motionSensor.SoftReset(); timerController.Register(this); @@ -146,8 +152,6 @@ void SystemTask::Work() { displayApp.Register(this); displayApp.Start(); - displayApp.PushMessage(Pinetime::Applications::Display::Messages::UpdateBatteryLevel); - heartRateSensor.Init(); heartRateSensor.Disable(); heartRateApp.Start(); @@ -190,7 +194,9 @@ void SystemTask::Work() { idleTimer = xTimerCreate("idleTimer", pdMS_TO_TICKS(2000), pdFALSE, this, IdleTimerCallback); dimTimer = xTimerCreate("dimTimer", pdMS_TO_TICKS(settingsController.GetScreenTimeOut() - 2000), pdFALSE, this, DimTimerCallback); + measureBatteryTimer = xTimerCreate("measureBattery", batteryMeasurementPeriod, pdTRUE, this, MeasureBatteryTimerCallback); xTimerStart(dimTimer, 0); + xTimerStart(measureBatteryTimer, portMAX_DELAY); // Suppress endless loop diagnostic #pragma clang diagnostic push @@ -200,11 +206,6 @@ void SystemTask::Work() { uint8_t msg; if (xQueueReceive(systemTasksMsgQueue, &msg, 100)) { - - batteryController.Update(); - // the battery does not emit events when changing charge levels, so we piggyback - // on any system event to read and update the current values - Messages message = static_cast(msg); switch (message) { case Messages::EnableSleeping: @@ -234,7 +235,6 @@ void SystemTask::Work() { lcd.Wakeup(); displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning); - displayApp.PushMessage(Pinetime::Applications::Display::Messages::UpdateBatteryLevel); heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp); isSleeping = false; @@ -329,8 +329,18 @@ void SystemTask::Work() { stepCounterMustBeReset = true; break; case Messages::OnChargingEvent: + batteryController.Update(); motorController.RunForDuration(15); - // Battery level is updated on every message - there's no need to do anything + break; + case Messages::MeasureBatteryTimerExpired: + sendBatteryNotification = true; + batteryController.Update(); + break; + case Messages::BatteryMeasurementDone: + if (sendBatteryNotification) { + sendBatteryNotification = false; + nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining()); + } break; default: @@ -349,11 +359,6 @@ void SystemTask::Work() { } } - if (xTaskGetTickCount() - batteryNotificationTick > batteryNotificationPeriod) { - nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining()); - batteryNotificationTick = xTaskGetTickCount(); - } - monitor.Process(); uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG); dateTimeController.UpdateTime(systick_counter); diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 3bf72acd..1f8be75f 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -128,13 +128,15 @@ namespace Pinetime { uint8_t bleDiscoveryTimer = 0; TimerHandle_t dimTimer; TimerHandle_t idleTimer; + TimerHandle_t measureBatteryTimer; + bool sendBatteryNotification = false; bool doNotGoToSleep = false; void GoToRunning(); void UpdateMotion(); bool stepCounterMustBeReset = false; - static constexpr TickType_t batteryNotificationPeriod = 1000 * 60 * 10; // 1 tick ~= 1ms. 1ms * 60 * 10 = 10 minutes - TickType_t batteryNotificationTick = 0; + static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000); + TickType_t lastBatteryNotificationTime = 0; #if configUSE_TRACE_FACILITY == 1 SystemMonitor monitor;