From faed0d49006605e33905f1914f04c90f6138bf6e Mon Sep 17 00:00:00 2001 From: hubmartin Date: Sun, 15 Aug 2021 16:47:41 +0200 Subject: [PATCH 01/16] Remove unnecessary pin_set, save 50ms --- src/drivers/Cst816s.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/Cst816s.cpp b/src/drivers/Cst816s.cpp index fd9792b3..affc432f 100644 --- a/src/drivers/Cst816s.cpp +++ b/src/drivers/Cst816s.cpp @@ -19,8 +19,8 @@ Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaste void Cst816S::Init() { nrf_gpio_cfg_output(pinReset); - nrf_gpio_pin_set(pinReset); - vTaskDelay(50); + //nrf_gpio_pin_set(pinReset); + //vTaskDelay(5); nrf_gpio_pin_clear(pinReset); vTaskDelay(5); nrf_gpio_pin_set(pinReset); From 099364e619374c4ad9bc0ba9136c31c35a82faf7 Mon Sep 17 00:00:00 2001 From: hubmartin Date: Sun, 15 Aug 2021 16:48:41 +0200 Subject: [PATCH 02/16] Remove LCD reset and more cmds, save over 200ms --- src/drivers/St7789.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/drivers/St7789.cpp b/src/drivers/St7789.cpp index 0f1dc02e..74038b2b 100644 --- a/src/drivers/St7789.cpp +++ b/src/drivers/St7789.cpp @@ -171,15 +171,15 @@ void St7789::Sleep() { void St7789::Wakeup() { nrf_gpio_cfg_output(pinDataCommand); // TODO why do we need to reset the controller? - HardwareReset(); - SoftwareReset(); + //HardwareReset(); + //SoftwareReset(); SleepOut(); - ColMod(); - MemoryDataAccessControl(); - ColumnAddressSet(); - RowAddressSet(); - DisplayInversionOn(); - NormalModeOn(); + //ColMod(); + //MemoryDataAccessControl(); + //ColumnAddressSet(); + //RowAddressSet(); + //DisplayInversionOn(); + //NormalModeOn(); VerticalScrollStartAddress(verticalScrollingStartAddress); DisplayOn(); NRF_LOG_INFO("[LCD] Wakeup") From db50131ed49f2fdb639799ca12839862340f8396 Mon Sep 17 00:00:00 2001 From: hubmartin Date: Mon, 23 Aug 2021 15:02:40 +0200 Subject: [PATCH 03/16] Remove commented commands completely --- src/drivers/Cst816s.cpp | 2 -- src/drivers/St7789.cpp | 9 --------- 2 files changed, 11 deletions(-) diff --git a/src/drivers/Cst816s.cpp b/src/drivers/Cst816s.cpp index affc432f..2e70a469 100644 --- a/src/drivers/Cst816s.cpp +++ b/src/drivers/Cst816s.cpp @@ -19,8 +19,6 @@ Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaste void Cst816S::Init() { nrf_gpio_cfg_output(pinReset); - //nrf_gpio_pin_set(pinReset); - //vTaskDelay(5); nrf_gpio_pin_clear(pinReset); vTaskDelay(5); nrf_gpio_pin_set(pinReset); diff --git a/src/drivers/St7789.cpp b/src/drivers/St7789.cpp index 74038b2b..4d81cf27 100644 --- a/src/drivers/St7789.cpp +++ b/src/drivers/St7789.cpp @@ -170,16 +170,7 @@ void St7789::Sleep() { void St7789::Wakeup() { nrf_gpio_cfg_output(pinDataCommand); - // TODO why do we need to reset the controller? - //HardwareReset(); - //SoftwareReset(); SleepOut(); - //ColMod(); - //MemoryDataAccessControl(); - //ColumnAddressSet(); - //RowAddressSet(); - //DisplayInversionOn(); - //NormalModeOn(); VerticalScrollStartAddress(verticalScrollingStartAddress); DisplayOn(); NRF_LOG_INFO("[LCD] Wakeup") From 55ff9b0c81ff32caaa18069a26057ad18589d022 Mon Sep 17 00:00:00 2001 From: NeroBurner Date: Fri, 10 Sep 2021 19:39:58 +0200 Subject: [PATCH 04/16] Add python packages click and cryptography to build instructions The script `tools/mcuboot/imgtool/main.py` imports the python packages `click` and `cryptography` to create the target `pinetime-mcuboot-app`. Add it tothe build instructions, as it was not installed on my system. https://github.com/JF002/InfiniTime/blob/6f9f0e8b0e42a5526d47ca664534fb6b0ccb6ace/tools/mcuboot/imgtool/main.py#L19 https://github.com/JF002/InfiniTime/blob/6f9f0e8b0e42a5526d47ca664534fb6b0ccb6ace/tools/mcuboot/imgtool/keys/__init__.py#L19 Also add the commands to install the python requirements into a python venv virtual environment. --- doc/buildAndProgram.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/buildAndProgram.md b/doc/buildAndProgram.md index e97bb30d..5b22014e 100644 --- a/doc/buildAndProgram.md +++ b/doc/buildAndProgram.md @@ -3,7 +3,14 @@ To build this project, you'll need: - A cross-compiler : [ARM-GCC (9-2020-q2-update)](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads/9-2020-q2-update) - The NRF52 SDK 15.3.0 : [nRF-SDK v15.3.0](https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip) - - The `cbor` and `intelhex` modules for Python 3 + - The Python 3 modules `cbor`, `intelhex`, `click` and `cryptography` modules for the `mcuboot` tool (see [requirements.txt](../tools/mcuboot/requirements.txt)) + - To to keep the system clean a python virtual environment (`venv`) can be used to install the python modules into + ```sh + python -m venv .venv + source .venv/bin/activate + python -m pip install wheel + python -m pip install -r tools/mcuboot/requirements.txt + ``` - A reasonably recent version of CMake (I use 3.16.5) ## Build steps From fd4fbfddb9508ea6ed9073523edd3a5f29e8195a Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Wed, 13 Oct 2021 13:04:40 +0300 Subject: [PATCH 05/16] Flashlight brightness control --- src/displayapp/DisplayApp.cpp | 2 +- src/displayapp/screens/FlashLight.cpp | 114 ++++++++++++++---- src/displayapp/screens/FlashLight.h | 16 ++- .../screens/settings/QuickSettings.cpp | 2 +- 4 files changed, 102 insertions(+), 32 deletions(-) diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index e763cda6..20a67088 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -390,7 +390,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) break; case Apps::FlashLight: currentScreen = std::make_unique(this, *systemTask, brightnessController); - ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); + ReturnApp(Apps::QuickSettings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::StopWatch: currentScreen = std::make_unique(this, *systemTask); diff --git a/src/displayapp/screens/FlashLight.cpp b/src/displayapp/screens/FlashLight.cpp index 4bc5b558..dcb31a7f 100644 --- a/src/displayapp/screens/FlashLight.cpp +++ b/src/displayapp/screens/FlashLight.cpp @@ -5,30 +5,41 @@ using namespace Pinetime::Applications::Screens; namespace { - static void event_handler(lv_obj_t* obj, lv_event_t event) { - FlashLight* screen = static_cast(obj->user_data); + void event_handler(lv_obj_t* obj, lv_event_t event) { + auto* screen = static_cast(obj->user_data); screen->OnClickEvent(obj, event); } } FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app, System::SystemTask& systemTask, - Controllers::BrightnessController& brightness) + Controllers::BrightnessController& brightnessController) : Screen(app), systemTask {systemTask}, - brightness {brightness} + brightnessController {brightnessController} { - brightness.Backup(); - brightness.Set(Controllers::BrightnessController::Levels::High); - // Set the background - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); + brightnessController.Backup(); - flashLight = lv_label_create(lv_scr_act(), NULL); - lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + brightnessLevel = brightnessController.Level(); + + flashLight = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48); lv_label_set_text_static(flashLight, Symbols::highlight); - lv_obj_align(flashLight, NULL, LV_ALIGN_CENTER, 0, 0); + lv_obj_align(flashLight, nullptr, LV_ALIGN_CENTER, 0, 0); + + for (auto & i : indicators) { + i = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_size(i, 15, 10); + lv_obj_set_style_local_border_width(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 2); + } + + lv_obj_align(indicators[1], flashLight, LV_ALIGN_OUT_BOTTOM_MID, 0, 5); + lv_obj_align(indicators[0], indicators[1], LV_ALIGN_OUT_LEFT_MID, -8, 0); + lv_obj_align(indicators[2], indicators[1], LV_ALIGN_OUT_RIGHT_MID, 8, 0); + + SetIndicators(); + SetColors(); backgroundAction = lv_label_create(lv_scr_act(), nullptr); lv_label_set_long_mode(backgroundAction, LV_LABEL_LONG_CROP); @@ -44,27 +55,80 @@ FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app, FlashLight::~FlashLight() { lv_obj_clean(lv_scr_act()); - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - brightness.Restore(); + lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); + brightnessController.Restore(); systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); } -void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) { - if (obj == backgroundAction) { - if (event == LV_EVENT_CLICKED) { - isOn = !isOn; - - if (isOn) { - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); - lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - } else { - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); - } +void FlashLight::SetColors() { + if (isOn) { + lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + for (auto & i : indicators) { + lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_WHITE); + lv_obj_set_style_local_border_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + } + } else { + lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + for (auto & i : indicators) { + lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_BLACK); + lv_obj_set_style_local_border_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); } } } +void FlashLight::SetIndicators() { + using namespace Pinetime::Controllers; + + if (brightnessLevel == BrightnessController::Levels::High) { + lv_obj_set_state(indicators[1], LV_STATE_DEFAULT); + lv_obj_set_state(indicators[2], LV_STATE_DEFAULT); + } else if (brightnessLevel == BrightnessController::Levels::Medium) { + lv_obj_set_state(indicators[1], LV_STATE_DEFAULT); + lv_obj_set_state(indicators[2], LV_STATE_DISABLED); + } else { + lv_obj_set_state(indicators[1], LV_STATE_DISABLED); + lv_obj_set_state(indicators[2], LV_STATE_DISABLED); + } +} + +void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) { + if (obj == backgroundAction && event == LV_EVENT_CLICKED) { + isOn = !isOn; + SetColors(); + } +} + bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + using namespace Pinetime::Controllers; + + if (event == TouchEvents::SwipeLeft) { + if (brightnessLevel == BrightnessController::Levels::High) { + brightnessLevel = BrightnessController::Levels::Medium; + brightnessController.Set(brightnessLevel); + SetIndicators(); + } else if (brightnessLevel == BrightnessController::Levels::Medium) { + brightnessLevel = BrightnessController::Levels::Low; + brightnessController.Set(brightnessLevel); + SetIndicators(); + } + return true; + } + if (event == TouchEvents::SwipeRight) { + if (brightnessLevel == BrightnessController::Levels::Low) { + brightnessLevel = BrightnessController::Levels::Medium; + brightnessController.Set(brightnessLevel); + SetIndicators(); + } else if (brightnessLevel == BrightnessController::Levels::Medium) { + brightnessLevel = BrightnessController::Levels::High; + brightnessController.Set(brightnessLevel); + SetIndicators(); + } + return true; + } + return false; } diff --git a/src/displayapp/screens/FlashLight.h b/src/displayapp/screens/FlashLight.h index 7f5ca6c5..f2c65bbe 100644 --- a/src/displayapp/screens/FlashLight.h +++ b/src/displayapp/screens/FlashLight.h @@ -1,10 +1,10 @@ #pragma once -#include #include "Screen.h" -#include -#include "systemtask/SystemTask.h" #include "components/brightness/BrightnessController.h" +#include "systemtask/SystemTask.h" +#include +#include namespace Pinetime { @@ -20,12 +20,18 @@ namespace Pinetime { void OnClickEvent(lv_obj_t* obj, lv_event_t event); private: + void SetIndicators(); + void SetColors(); + Pinetime::System::SystemTask& systemTask; - Controllers::BrightnessController& brightness; + Controllers::BrightnessController& brightnessController; + + Controllers::BrightnessController::Levels brightnessLevel; lv_obj_t* flashLight; lv_obj_t* backgroundAction; - bool isOn = true; + lv_obj_t* indicators[3]; + bool isOn = false; }; } } diff --git a/src/displayapp/screens/settings/QuickSettings.cpp b/src/displayapp/screens/settings/QuickSettings.cpp index 691c40c8..dd626072 100644 --- a/src/displayapp/screens/settings/QuickSettings.cpp +++ b/src/displayapp/screens/settings/QuickSettings.cpp @@ -131,7 +131,7 @@ void QuickSettings::OnButtonEvent(lv_obj_t* object, lv_event_t event) { if (object == btn2 && event == LV_EVENT_CLICKED) { running = false; - app->StartApp(Apps::FlashLight, DisplayApp::FullRefreshDirections::None); + app->StartApp(Apps::FlashLight, DisplayApp::FullRefreshDirections::Up); } else if (object == btn1 && event == LV_EVENT_CLICKED) { From ea1bc8192587cb95b89068043cdd44b104d2279e Mon Sep 17 00:00:00 2001 From: carlosperate Date: Sat, 16 Oct 2021 23:10:36 +0100 Subject: [PATCH 06/16] Docs: Clarify Docker image needs to be built 1st, fix URL and typos. --- doc/buildWithDocker.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/buildWithDocker.md b/doc/buildWithDocker.md index 2e4ecbb6..a57893c9 100644 --- a/doc/buildWithDocker.md +++ b/doc/buildWithDocker.md @@ -11,7 +11,9 @@ Based on Ubuntu 18.04 with the following build dependencies: ## Run a container to build the project -The `infinitime-build` image contains all the dependencies you need. The default `CMD` will compile sources found in `/sources`, so you need only mount your code. +The `infinitime-build` image contains all the dependencies you need. The default `CMD` will compile sources found in `/sources`, so you need only mount your code. + +Before continuing, make sure you first build the image as indicated in the [Build the image](#build-the-image) section, or check the [Using the image from Docker Hub](#using-the-image-from-docker-hub) section if you prefer to use a pre-made image. This example will build the firmware, generate the MCUBoot image and generate the DFU file. For cloning the repo, see [these instructions](../doc/buildAndProgram.md#clone-the-repo). Outputs will be written to **/build/output**: @@ -28,10 +30,10 @@ docker run --rm -it -v $(pwd):/sources infinitime-build /opt/build.sh pinetime-a The image is built using 1000:1000 for the user id and group id. If this is different to your user or group ids (run `id -u` and `id -g` to find out what your id values are if you are unsure), you will need to override them via the `--user` parameter in order to prevent permission errors with the output files (and the cmake build cache). -Running with this image is the same as above, you just specify the ids to `docker run` +Running with this image is the same as above, you just specify the ids to `docker run`: ```bash -docker run --rm -it -v $(pwd):/sources --user $(id -u):$(id -g) pfeerick/infinitime-build +docker run --rm -it -v $(pwd):/sources --user $(id -u):$(id -g) infinitime-build ``` Or you can specify your user id and group id (by number, not by name) directly: @@ -42,7 +44,7 @@ docker run --rm -it -v $(pwd):/sources --user 1234:1234 infinitime-build ## Using the image from Docker Hub -The image is avaiable via Docker Hub for both the amd64 and arm64v8 architectures at [pfeerick/infinitime-build](https://hub.docker.com/repository/docker/pfeerick/infinitime-build). +The image is available via Docker Hub for both the amd64 and arm64v8 architectures at [pfeerick/infinitime-build](https://hub.docker.com/r/pfeerick/infinitime-build). It can be pulled (downloaded) using the following command: From 2e7d4763dcf0e548e09a6ebc121cab80c37dcc23 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sat, 16 Oct 2021 20:45:08 -0700 Subject: [PATCH 07/16] Add docs from infinitime Go library's repo and fix typos and grammar --- doc/ble.md | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 237 insertions(+), 5 deletions(-) diff --git a/doc/ble.md b/doc/ble.md index 518b99c8..cdb449d8 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -2,23 +2,59 @@ ## Introduction This page describes the BLE implementation and API built in this firmware. -**Note** : I'm a beginner in BLE related technologies and the information of this document reflect my current knowledge and understanding of the BLE stack. These informations might be erroneous or incomplete. Feel free to submit a PR if you think you can improve these. +**Note** : I'm a beginner in BLE related technologies and the information of this document reflect my current knowledge and understanding of the BLE stack. This information might be erroneous or incomplete. Feel free to submit a PR if you think you can improve these. + +--- + +### Table of Contents + +- [BLE Connection](#ble-connection) +- [BLE UUIDs](#ble-uuids) +- [BLE Services](#ble-services) + - [CTS](#cts) + - [ANS](#ans) +- [Getting Information](#getting-information) + - [Firmware Version](#firmware-version) + - [Battery Level](#battery-level) + - [Heart Rate](#heart-rate) +- [Notifications](#notifications) + - [New Alert](#new-alert) + - [Notification Event](#notification-event) +- [Firmware Upgrades](#firmware-upgrades) + - [Step one](#step-one) + - [Step two](#step-two) + - [Step three](#step-three) + - [Step four](#step-four) + - [Step five](#step-five) + - [Step six](#step-six) + - [Step seven](#step-seven) + - [Step eight](#step-eight) + - [Step nine](#step-nine) +- [Music Control](#music-control) + - [Events](#events) + - [Status](#status) + - [Artist, Track, and Album](#artist-track-and-album) +- [Time](#time) + +--- ## BLE Connection -When starting the firmware start a BLE advertising : it sends small messages that can be received by any *central* device in range. This allows the device to announce its presence to other devices. +When starting, the firmware starts BLE advertising. It sends small messages that can be received by any *central* device in range. This allows the device to announce its presence to other devices. -A companion application (running on a PC, RaspberryPi, smartphone) which received this avertising packet can request a connection to the device. This connection procedure allows the 2 devices to negotiate communication parameters, security keys,... +A companion application (running on a PC, Raspberry Pi, smartphone, etc.) which receives this advertising packet can request a connection to the device. This connection procedure allows the 2 devices to negotiate communication parameters, security keys, etc. -When the connection is established, the pinetime will try to discover services running on the companion application. For now **CTS** (**C**urrent **T**ime **S**ervice) and **ANS** (**A**lert **N**otification **S**ervice) are supported. +When the connection is established, the PineTime will try to discover services running on the companion application. For now **CTS** (**C**urrent **T**ime **S**ervice) and **ANS** (**A**lert **N**otification **S**ervice) are supported. If **CTS** is detected, it'll request the current time to the companion application. If **ANS** is detected, it will listen to new notifications coming from the companion application. ![BLE connection sequence diagram](ble/connection_sequence.png "BLE connection sequence diagram") +--- + ## BLE UUIDs When possible, InfiniTime tries to implement BLE services defined by the BLE specification. -When the service does not exist in the BLE specification, InfiniTime implement custom services. As all BLE services, custom services are identified by a UUID. Here is how to define the UUID of custom services in InfiniTime: +When the service does not exist in the BLE specification, InfiniTime implements custom services. Custom services are identified by a UUID, as are all BLE services. Here is how to define the UUID of custom services in InfiniTime: ``` - Base UUID : xxxxxxxx-78fc-48fe-8e23-433b3a1942d0 @@ -38,6 +74,8 @@ The following custom services are implemented in InfiniTime: * Navigation Service : 00010000-78fc-48fe-8e23-433b3a1942d0 ``` +--- + ## BLE services [List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/) @@ -49,3 +87,197 @@ The following custom services are implemented in InfiniTime: ![ANS sequence diagram](./ble/ans_sequence.png "ANS sequence diagram") +--- + +### Getting Information + +The InfiniTime firmware exposes some information about itself through BLE. The BLE characteristic UUIDs for this information are as follows: + +- Firmware Version: `00002a26-0000-1000-8000-00805f9b34fb` +- Battery Level: `00002a19-0000-1000-8000-00805f9b34fb` +- Heart Rate: `00002a37-0000-1000-8000-00805f9b34fb` + +#### Firmware Version + +Reading a value from the firmware version characteristic will yield a UTF-8 encoded string containing the version of InfiniTime being run on the device. Example: `1.6.0`. + +#### Battery Level + +Reading from the battery level characteristic yields a single byte of data. This byte can be converted to an unsigned 8-bit integer which will be the battery percentage. This characteristic allows notify for updates as the value changes. + +#### Heart Rate + +Reading from the heart rate characteristic yields two bytes of data. I am not sure of the function of the first byte. It appears to always be zero. The second byte can be converted to an unsigned 8-bit integer which is the current heart rate. This characteristic also allows notify for updates as the value changes. + +--- + +### Notifications + +InfiniTime uses the Alert Notification Service (ANS) for notifications. The relevant UUIDs are as follows: + +- New Alert: `00002a46-0000-1000-8000-00805f9b34fb` +- Notification Event: `00020001-78fc-48fe-8e23-433b3a1942d0` + +#### New Alert + +The new alert characteristic allows sending new notifications to InfiniTime. It requires the following format: + +``` +\x00<\x00-separated data> +``` + +For example, here is what a normal notification looks like in Golang (language of `itd`): + +```go +// \x00 is the category for simple alert, and there is one new notifcation, hence \x01. +"\x00\x01\x00Test Title\x00Test Body" +``` + +A call notification looks like so: + +```go +// \x03 is the category for calls, and there is one new call notifcation, hence \x01. +"\x03\x01\x00Mary" +``` + +The `\x00` stands for hexadecimal `00` which means null. + +Here is the list of categories and commands: + +- Simple Alert: `0` +- Email: `1` +- News: `2` +- Call Notification: `3` +- Missed Call: `4` +- SMS/MMS: `5` +- Voicemail: `6` +- Schedule: `7` +- High Prioritized Alert: `8` +- Instant Message: `9` +- All Alerts: `0xFF` + +These lists and information were retrieved from the following pages in the Nordic docs: + +- [Alert Notification Service Client](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v12.2.0%2Fgroup__ble__ans__c.html) +- [Alert Notification Application](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v13.0.0%2Fble_sdk_app_alert_notification.html) + +#### Notification Event + +A call notification in InfiniTime contains three buttons. Decline, Accept, and Mute. The notification event characteristic contains the button tapped by the user on a call notification. This characteristic only allows notify, **not** read. + +Enabling notifications from this characteristic, you get a single byte whenever the user taps a button on the call notification. This byte is an unsigned 8-bit integer that signifies one of the buttons. The numbers are as follows: + +- 0: Declined +- 1: Accepted +- 2: Muted + +--- + +### Firmware Upgrades + +Firmware upgrades in InfiniTime are probably the most complex of the BLE operations. It is a nine step process requiring multiple commands be sent to multiple characteristics. The relevant UUIDs are as follows: + +- Control Point: `00001531-1212-efde-1523-785feabcd123` +- Packet: `00001532-1212-efde-1523-785feabcd123` + +A DFU upgrade archive for InfiniTime consists of multiple files. The most important being the .bin and .dat files. The first is the actual firmware, while the second is a packet that initializes DFU. Both are needed for a DFU upgrade. + +The first thing to do is to enable notifications on the control point characteristic. This will be needed for verifying that the proper responses are being sent back from InfiniTime. + +#### Step one + +For the first step, write `0x01`, `0x04` to the control point characteristic. This will signal InfiniTime that a DFU upgrade is to be started. + +#### Step two + +In step two, send the total size in bytes of the firmware file to the packet characteristic. This value should be an unsigned 32-bit integer encoded as little-endian. In front of this integer should be 8 null bytes. This is because there are three items that can be updated and each 4 bytes is for one of those. The last four are for the InfiniTime application, so those are the ones that need to be set. + +#### Step three + +Before running step three, wait for a response from the control point. This response should be `0x10`, `0x01`, `0x01` which indicates a successful DFU start. In step three, send `0x02`, `0x00` to the control point. This will signal InfiniTime to expect the init packet on the packet characteristic. + +#### Step four + +The previous step prepared InfiniTime for this one. In this step, send the contents of the .dat init packet file to the packet characteristic. After this, send `0x02`, `0x01` indicating that the packet has been sent. + +#### Step five + +Before running this step, wait to receive `0x10`, `0x02`, `0x01` which indicates that the packet has been received. During this step, send the packet receipt interval to the control point. The firmware file will be sent in segments of 20 bytes each. The packet receipt interval indicates how many segments should be received before sending a receipt containing the amount of bytes received so that it can be confirmed to be the same as the amount sent. This is very useful for detecting packet loss. `itd` uses `0x08`, `0x0A` which indicates 10 segments. + +#### Step six + +In step six, write `0x03` to the control point, indicating that the firmware will be sent next on the packet characteristic. + +#### Step seven + +This step is the most difficult. Here, the actual firmware is sent to InfiniTime. + +As mentioned before, the firmware file must be split up into segments of 20 bytes each and sent to the packet characteristic one by one. Every 10 segments (or whatever you have set the interval to), check for a response starting with `0x11`. The rest of the response will be the amount of bytes received encoded as a little-endian unsigned 32-bit integer. Confirm that this matches the amount of bytes sent, and then continue sending more segments. + +#### Step eight + +Before running this step, wait to receive `0x10`, `0x03`, `0x01` which indicates a successful receipt of the firmware image. In this step, write `0x04` to the control point to signal InfiniTime to validate the image it has received. + +#### Step nine + +Before running this step, wait to receive `0x10`, `0x04`, `0x01` which indicates that the image has been validated. In this step, send `0x05` to the control point as a command with no response. This signals InfiniTime to activate the new firmware and reboot. + +Once all of these steps are complete, the DFU is complete. Don't forget to validate the firmware in the settings. + +--- + +### Music Control + +InfiniTime contains a music controller app which is meant to control the music playback and volume through the companion. + +The following UUIDs are relevant to this: + +- Events: `00000001-78fc-48fe-8e23-433b3a1942d0` +- Status: `00000002-78fc-48fe-8e23-433b3a1942d0` +- Artist: `00000003-78fc-48fe-8e23-433b3a1942d0` +- Track: `00000004-78fc-48fe-8e23-433b3a1942d0` +- Album: `00000005-78fc-48fe-8e23-433b3a1942d0` + +#### Events + +The events characteristic is meant to respond to user input in the music controller app. + +Enabling notifications on this characteristic gives you a single byte upon any event. This byte can be converted to an unsigned 8-bit integer which corresponds to each possible event. Here are the events: + +- App Opened: `0xe0` +- Play: `0x00` +- Pause: `0x01` +- Next: `0x03` +- Previous: `0x04` +- Volume up: `0x05` +- Volume down: `0x06` + +#### Status + +The status characteristic allows setting the playing status of music. Send `0x01` to the status characteristic for playing, and `0x01` for paused. + +#### Artist, Track, and Album + +These characteristics all work the same way. Simply send a UTF-8 encoded string to the relevant characteristic in order to set the value in the app. + +--- + +### Time + +InfiniTime allows setting its time via the Current Time Service (CTS) + +The UUID for the current time characteristic is: `00002a2b-0000-1000-8000-00805f9b34fb` + +This characteristic expects a particular format: + +- Year (`uint16`) +- Month (`uint8`) +- Day (`uint8`) +- Hour (`uint8`) +- Minute (`uint8`) +- Second (`uint8`) +- Weekday (`uint8`) +- Microsecond divided by `1e6*256` (`uint8`) +- Binary 0001 (`uint8`) + +Write all of these together, encoded as little-endian, to the current time characteristic. \ No newline at end of file From 75339323d24af0ffae60ac2b09fe6b364e6d6254 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sat, 16 Oct 2021 20:52:25 -0700 Subject: [PATCH 08/16] Fix grammar mistakes --- doc/ble.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ble.md b/doc/ble.md index cdb449d8..a46ee132 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -2,7 +2,7 @@ ## Introduction This page describes the BLE implementation and API built in this firmware. -**Note** : I'm a beginner in BLE related technologies and the information of this document reflect my current knowledge and understanding of the BLE stack. This information might be erroneous or incomplete. Feel free to submit a PR if you think you can improve these. +**Note** : I'm a beginner in BLE related technologies and the information in this document reflects my current knowledge and understanding of the BLE stack. This information might be erroneous or incomplete. Feel free to submit a PR if you think you can improve it. --- From 95618dd2388d5fbb271e73d52913747a0b42d9eb Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sat, 16 Oct 2021 21:01:12 -0700 Subject: [PATCH 09/16] Fix error in music status docs --- doc/ble.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ble.md b/doc/ble.md index a46ee132..2f8ba41c 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -254,7 +254,7 @@ Enabling notifications on this characteristic gives you a single byte upon any e #### Status -The status characteristic allows setting the playing status of music. Send `0x01` to the status characteristic for playing, and `0x01` for paused. +The status characteristic allows setting the playing status of music. Send `0x01` to the status characteristic for playing, and `0x00` for paused. #### Artist, Track, and Album From b0c4ade926dfde0fc985ca6560fed9ff76d8c9fc Mon Sep 17 00:00:00 2001 From: stephanie-eng Date: Mon, 18 Oct 2021 00:35:47 -0400 Subject: [PATCH 10/16] Fixed Apps tutorial to compile properly Changed case in minimal example to properly reference Pinetime namespace --- doc/code/Apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/code/Apps.md b/doc/code/Apps.md index b3bab042..355b44b7 100644 --- a/doc/code/Apps.md +++ b/doc/code/Apps.md @@ -51,7 +51,7 @@ MyApp.h: #include "displayapp/screens/Screen.h" #include -namespace PineTime { +namespace Pinetime { namespace Applications { namespace Screens { class MyApp : public Screen { From 86bcea9e99ac8f68251ca615320478f75a29aca9 Mon Sep 17 00:00:00 2001 From: NeroBurner Date: Mon, 18 Oct 2021 08:08:43 +0200 Subject: [PATCH 11/16] CI: add pintime-recovery to build jobs The recovery image "it is the last chance before a brick", as described in https://github.com/InfiniTimeOrg/InfiniTime/issues/742#issuecomment-943665960 So just build it to make sure it doesn't silently break, but don't upload it. --- .github/workflows/main.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bd24359a..3b753a37 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -153,6 +153,14 @@ jobs: name: pinetime-app.out path: build/src/pinetime-app*.out + ######################################################################################### + # Make but don't Upload the Recovery Firmware to be sure it builds correctly + + - name: Make pinetime-recovery + run: | + cd build + make pinetime-recovery + ######################################################################################### # Finish From 02d52d94d524d421043ff3b46dcc753051c7b5f7 Mon Sep 17 00:00:00 2001 From: Stephanie Date: Mon, 18 Oct 2021 12:18:35 -0400 Subject: [PATCH 12/16] Added missing semicolon --- doc/code/Apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/code/Apps.md b/doc/code/Apps.md index 355b44b7..7c2d7a05 100644 --- a/doc/code/Apps.md +++ b/doc/code/Apps.md @@ -58,7 +58,7 @@ namespace Pinetime { public: MyApp(DisplayApp* app); ~MyApp() override; - } + }; } } } From e2d8397c0864c71e1dcf9afbdd9fdc5edc84132d Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Wed, 20 Oct 2021 15:37:56 +0300 Subject: [PATCH 13/16] Fix issue templates --- .github/ISSUE_TEMPLATE/bug-report.yaml | 1 - .github/ISSUE_TEMPLATE/feature-request.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index efcba56c..f0fb076e 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -1,6 +1,5 @@ name: Bug Report description: File a bug report -title: "[Bug]: " labels: ["bug"] body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml index 092ef909..26e4fa0f 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -1,6 +1,5 @@ name: Feature Request description: File a feature request -title: "" labels: ["feature request"] body: - type: markdown From 76234de7d6aa997a892a8d845013d673ab4d3bcb Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 20 Oct 2021 16:41:48 +0300 Subject: [PATCH 14/16] Fixed the feature request template --- .github/ISSUE_TEMPLATE/feature-request.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml index 092ef909..26e4fa0f 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -1,6 +1,5 @@ name: Feature Request description: File a feature request -title: "" labels: ["feature request"] body: - type: markdown From dcd209a3d1370a30fd27fd51a527b1eb6cf92480 Mon Sep 17 00:00:00 2001 From: xan-m <88455615+xan-m@users.noreply.github.com> Date: Thu, 21 Oct 2021 10:15:21 -0700 Subject: [PATCH 15/16] Changed name of iOS companion app I had to remove iOS from the app name, so this update changes the name and link here to reflect that change --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6719aa3e..765aa863 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ 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) + * **[Experimental]** [InfiniLink](https://github.com/xan-m/InfiniLink) (on iOS) - OTA (Over-the-air) update via BLE - [Bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader) based on [MCUBoot](https://www.mcuboot.com) From f56bd4ce9de35edc0c9fb178c950f26c80d99b94 Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Fri, 22 Oct 2021 12:42:06 +0300 Subject: [PATCH 16/16] Update coding style --- doc/contribute.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/contribute.md b/doc/contribute.md index 21746433..595a5996 100644 --- a/doc/contribute.md +++ b/doc/contribute.md @@ -94,3 +94,10 @@ If there are no preconfigured rules for your IDE, you can use one of the existin - **Includes** : - files from the project : `#include "relative/path/to/the/file.h"` - external files and std : `#include ` + - Only use [primary spellings for operators and tokens](https://en.cppreference.com/w/cpp/language/operator_alternative) + - Use auto sparingly. Don't use auto for [fundamental/built-in types](https://en.cppreference.com/w/cpp/language/types) and [fixed width integer types](https://en.cppreference.com/w/cpp/types/integer), except when initializing with a cast to avoid duplicating the type name. + - Examples: + - `auto* app = static_cast(instance);` + - `auto number = static_cast(variable);` + - `uint8_t returnValue = MyFunction();` + - Use nullptr instead of NULL