Edit page

PineTime full documentation



The PineTime

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, and a capacitive touch IPS display that is legible in direct sunlight. It is a fully community driven side-project which anyone can contribute to, allowing you to keep control of your device.


Getting started

Read these first:

The frequently asked question can be found in the article FAQ.


Accessory

Cases

There are no cases for PineTime yet, but some cases for Fitbit are suitable for it. Cases for Fitbit have one microphone hole, which is unnecessary for the PineTime, but otherwise they fit perfectly (such as the Soft TPU case for Fitbit Versa 2/Versa Lite).

The community designed the following cases:

Watch band

The PineTime uses a standard 20mm watch band / strap. There is a thread in the forum discussing this.

Due to the watches design. Retention for the spring bars are recessed into the watch. Not all 20mm bands / straps work. This is especially the case for Nato style bands / straps being too thick.

Known working bands:


Software

Bluetooth

This page is under construction, but aims to get you more familiar with Bluetooth and how the PineTime utilizes it

PineTime Bluetooth LE agreements

These are aimed for firmware developers primarily, and should describe how to design your firmware to be interoperable with the rest.

External references

Companion apps

PineTime/InfiniTime needs a companion app to e.g. upload a firmware, get notifications from a phone, or just get the date/time. Here are some companion apps:

  • Gadgetbridge (Android >= 4.4) - Companion mobile app, supports updating firmware/bootloader, send notifications, etc.
  • Amazfish (SailfishOS, Ubuntu Touch and Linux) - Companion mobile and desktop app, supports updating firmware/bootloader, send notifications, etc.
  • Watchmate (Linux)
  • Siglo (Linux) - Companion desktop app.
  • PinetimeFlasher (Windows) - Companion desktop app, only supports flashing firmware.
  • nRFConnect (iOS) - Only supports flashing firmware. The app is closed source and versions after 4.24.3 donā€™t work for the PineTime anymore
  • InfiniLink (iOS) - Companion mobile app in development. Supports updating firmware/bootloader, setting date and time, Apple Music controls, and battery and heart rate data.
  • ITD (Linux)

Default OS

The current default operating system on the PineTime is called InfiniTime, you can find more information about the firmware on its GitHub page. First devkits shipped with a proprietary custom firmware.

You can find a list of available firmware and other software here: Development

Firmware versioning for companion apps

BLE defines a standard service (Device Information Service, DIS) that allows BLE devices to expose information about them: hardware version, software version.

Companion apps will use these information to detect which firmware (or fork of a firmware) is running on the Pinetime they are connected to.

How to use the Device Information Service to differentiate firmwares?

Weā€™ve briefly talked about this on the community chat and this proposal is the result of this discussion. This is a request for comments.

Fields from the Device Information Service:

FieldDescriptionExample value
Manufacturer nameManufacturerPINE64
Model numberName of the device“PineTime”, “P8”
Serial numberNot needed by companion apps as they can use the BLE MAC address?
Hardware revisionVersion of the hardware1.0a
Software revisionProject name or fork name“InfiniTime”, “InfiniTime-forkname”, “Hypnos”
Firmware revisionVersion of the firmware“0.7.1”

InfiniTime

InfiniTime

InfiniTime (also known as JFā€™s project, FreeRTOS firmware or Pinetime-JF) is an open source (GPLv3) firmware written in {cpp} and based on FreeRTOS, LVGL and NimBLE. This firmware (version 0.7.1) is preloaded at the factory since the new batch of PineTime devkits and sealed PineTimes of end of September 2020. This page contains general information about the project. Actual project documentation and nuances are documented in the repository

Read this first!

  • InfiniTime and the bootloader are still in heavy development, and some features might not work as expected. Any issues should be reported on GitHub.
  • Do not use a sealed PineTime as a development device, there is a fair amount of chances that youā€™ll brick it with very experimental firmware.
  • OTA is relatively stable, but when youā€™re on an outdated bootloader version it can freeze which will require you to wait for the battery to run out.

Known issues

warning

Please read this section carefully before upgrading the firmware on your PineTime, especially if you are using a sealed device!

Black screen after a reset in sleep mode

Description

This issue occurs if you are using the original bootloader (from InfiniTime 0.7.1), upgraded from InfiniTime 0.7.1 to 0.8.2 and the watch resets while the display is OFF: the watch is stuck on a black screen and does not respond to touch, button press or BLE.

The reset can be triggered by the watchdog or manually by long pressing the button. If that doesnā€™t work, wait for the battery to run out, itā€™ll reboot then.

This issue is caused by InfiniTime 0.8.2 that put the external SPI flash memory to sleep before switching the display off and by the bootloader that cannot wake the memory chip up. The bootloader is stuck in an infinite loop.

More info: https://github.com/lupyuen/pinetime-rust-mynewt/issues/24 and https://github.com/JF002/InfiniTime/issues/60

Workaround

On a development kit, a simple reset via SWD is needed to unlock the bootloader.

On a sealed device, the only way to work around this issue is to wait for the battery to drain completely before trying again.

This issue can be avoided by ensuring that the display is ON when manually resetting the device (long push on the button).

Fixed version

This issues is fixed in InfiniTime 0.8.3

Releases

In the order of oldest first.

Version 0.7.1

Link: Version 0.7.1

This is the version that ships with PineTime devkits and sealed PineTime as of September 2020.

  • Time synchronization over BLE
  • Display brightness setting
  • Notifications over BLE
  • Integration with AmazFish (SailFishOS) and Gadgetbridge (Android)
  • OTA using NRFConnect

Bootloader: https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7

Version 0.8.2

Link: Version 0.8.2

  • Music control app
  • Paint app
  • Manual image validation after OTA

Bootloader: https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4.

This new version of the bootloader fixes Black screen after a reset in sleep mode and enables the watchdog before launching the firmware.

Version 0.8.3

Link: Version 0.8.3

  • Fixes the “bootloader stuck after a reset” mentioned in 0.8.2 above

Version 0.9.0

Link: Version 0.9.0

  • Touch panel fixed (no more freezing)
  • Improved music application (possibility to control both volumes and browsing, display the song progression)
  • Notification UI rewritten: can now display up to 100 characters
  • Improved BLE connectivity

Version 0.10.0

Link: Version 0.10.0

  • 2 new games: Pong and 2048
  • Fixed display glitches and improved needed time to turn on the display

Version 0.11.0

Link: Version 0.11.0

  • Heart-rate sensor application:
  • Displays the current value, and when running, this value is also displayed on the clock face
  • Value exposed over BLE, meaning other applications can read it (e.g. companion app such as Amazfish)
  • Navigation app: InfiNav - works e.g. with PureMaps on SailfishOS

Version 0.12.0

Link: Version 0.12.0

  • Improved BLE connection
  • OTA updates much more reliable

Version 0.13.0

Link: Version 0.13.0

  • Vibration
  • Call notification: it is possible to accept/ignore/reject a call from the PineTime
  • Music app got nicer icons
  • BLE connectivity improved a bit more

Version 0.14.0 “Green Avocado”

Link: Version 0.14.0 “Green Avocado”

  • LVGL 7
  • Bugfixes to the build process

Version 0.14.1

Link: Version 0.14.1

Version 0.15.0 “Yellow Banana”

Link: Version 0.15.0 “Yellow Banana”

  • Analog watch face
  • Support for switching watch faces
  • Stopwatch app

Version 1.0.0 “Red Cherry”

Link: Version 1.0.0 “Red Cherry”

  • Motion sensor integration
  • Step countin
  • UI redesign
  • Quick action menu:
  • Brightness setting
  • Do not disturb mode (disable vibrations on notifications)
  • Flash light application
  • Settings menu allowing configuration of:
  • Display timeout
  • Wakeup source (button only, single tap, double tap and wrist rotation)
  • Time format (12/24H)
  • Watchface
  • New navigation flow:
  • User settings stored in flash memory and restored on reset

Version 1.1.0 “Dragon Fruit”

Link: Version 1.1.0 “Dragon Fruit”

  • Steps application
  • Timer application
  • UI improvements
  • Clang-format and clang-tidy config files
  • Bugfixes

Version 1.2.0 “Blue-purple Elderberry”

Link: Version 1.2.0 “Blue-purple Elderberry”

  • Added support for alternate accelerometer part BMA425
  • Metronome app
  • Memory usage optimizations
  • Bugfixes, minor improvements and code cleanup

Version 1.3.0 “Purple Fig”

Link: Version 1.3.0 “Purple Fig”

  • LittleFS integration
  • New watchface, PineTimeStyle
  • Battery level notification on BLE (supported by Gadgetbridge)
  • Improved stopwatch app, Paddle game and call notifications
  • Firmware update app is now more foolproof
  • The SPI flash is put in sleep mode when the watch goes to sleep (only if the new bootloader is detected)
  • UI improvements (better ’tick’ handling in LVGL, more consistent refresh rate)
  • Various improvements and code cleaning

Version 1.4.0 “Pink Grapefruit”

Link: Version 1.4.0 “Pink Grapefruit”

  • Improved touch driver
  • Color customization for background, text and lateral bar of PineTimeStyle
  • UI improvements for better use of the small screen, and screen dimming before display sleeps
  • Call notification improvements
  • Battery level improvements
  • Attempts to fix bootloop issue

Version 1.5.0 “Huckleberry”

Link: Version 1.5.0 “Huckleberry”

  • Alarm app
  • Vibration improvements
  • Ability to recover time after a reset
  • BLE improvements
  • Battery level improvements

Version 1.6.0 “Ice Apple”

Link: Version 1.6.0 “Ice Apple”

  • A new BLE Fix

Version 1.7.0 “Jackfruit”

Link: Version 1.7.0 “Jackfruit”

  • More accurate battery percentage
  • Faster wakeup (video)
  • New touch handler
  • Wake on notification and charge
  • Memory usage and speed optimizations
  • Motion service (video)
  • Flashlight brightness changes
  • Manual date and time setting
  • Battery percentage reporting
  • Disabled Touch Controller error check
  • Paddle game bounds checking

Version 1.7.1

Link: Version 1.7.1

  • Touchscreen failure edge case

Version 1.8.0 “Fuzzy Kiwi”

Link: Version 1.8.0 “Fuzzy Kiwi”

  • Improved gesture consistency
  • Digital watchface: Changed the color of the BLE icon to the official “Bluetoothā„¢ blue”
  • PineTimeStyle: Integrated color picker into the watchface (long tap on the PTS watch face, and then tap on the gear icon that appears to open the color settings) * PineTimeStyle: Fixed alignment of the icons
  • Settings: Styled checkboxes as radio buttons
  • Paddle: Speed randomization
  • InfiniPaint: Vibration on color change (long tap to change color when running InfiniPaint)
  • BLE secure pairing
  • BLE file system API
  • Weather service (integrations are planned for the future)
  • Trip meter in Step app
  • Chimes: Short vibration every hour or half an hour. Settings are available in the 3rd page of settings
  • Shake to wake: The calibration of the sensitivity is available in the 3rd page of settings

Version 1.9.0 “Limeberry”

Link: Version 1.9.0 “Limeberry”

  • Terminal watchface
  • Enable/Disable BLE
  • InfiniSim, the LVGL simulator for InfiniTime
  • Improve notification and call notification timeout
  • Improve heart-rate measurements
  • Improve Alarm App
  • Better 12-hours mode integration (in settings, alarm and status bar)
  • Code cleanup and many improvements needed by InfiniSim
  • Fix display corruption when the timer is triggered
  • Fix freeze in Music app when the title/album name were too long

Version 1.10.0 “Yellow Mango”

Link: Version 1.10.0 “Yellow Mango”

  • Notifications can now be dismissed by right swiping on the notification
  • Faster sleep
  • New sharper battery icon
  • Timer UI was overhauled
  • Tweaked and improved display gamma to improve the colors displayed by the LCD
  • Flashlight now defaults to max brightness
  • Fixed track progress in Music app
  • Fixed alarm sometimes not ringing

Version 1.11.0 “Red Nectarine”

Link: Version 1.11.0 “Red Nectarine”

  • Ability to install external resources, such as watch faces, images, and fonts.
  • Created a document to better communicate the vision of InfiniTime project to users, developers and everyone else
  • Compatibility with LFCLK calibration and reduced the power consumption of Nimble
  • Limit backlight brightness when flashlight is off
  • Watch face inspired by the G7710, with day of year and week number info (+ battery level %)
  • Infineat watch face + its external resources
  • Added pink color for PinetimeStyle watch face
  • PineTimeStyle watch face : make step count display configurable (full gauge for step count, half gauge with seconds and numerical display of the step count).

Oneshot 1.11 - FOSDEM 2023 Edition

Link: Oneshot 1.11 - FOSDEM 2023 Edition

  • Oneshot version, which was created for the “Free and Open source Software Developers’ European Meeting”
  • Digital and analog watchfaces with FOSDEMā€™s logo as the background

Version 1.12.0 “Olallieberry”

Link: Version 1.12.0 “Olallieberry”

  • Project maintenance
  • Added Watchmate to the list of companion apps
  • Better battery level monitoring, and battery indicator alert
  • Date and time settings are combined into a single setting page
  • Updated settings list style

Version 1.13.0 “Pomegranate”

Link: Version 1.13.0 “Pomegranate”

  • New heart rate processing algorithm, provides more accurate and faster results
  • New memory management (heap unification)
  • Weather integration in PineTimeStyle watch face
  • Major power optimizations
  • Bug fix in shake wake

InfiniTime weather

Infinitime features a weather subsystem which stores weather data on the watch in a timeline which can be queried by apps or watchfaces. It can store many different types of data (see here) and each entry includes a timestamp and an expiry time. When entries expire they are removed from the timeline automatically.

Sending weather data to the watch ==

Weather data must be sent to the watch via a companion app. Currently, ITD and Gadgetbridge have this functionality implemented.

ITD ===

ITD is the easiest option as the feature simply needs enabling in the config file with a location specified. It uses data from MET.no and provides one hour of data at a time.

Gadgetbridge ===

Gadgetbridge is slightly more difficult to set up as it requires a separate app to fetch the weather data. Details are available on the Gadgetbridge website.

Displaying weather data ==

Currently there are 2 ways to display weather data on the watch, a debug app which is disabled by default, and the PineTimeStyle watchface. To enable it, press and hold on the watch face, tap the settings icon and enable weather.

The easiest way to enable the debug app is to change the entry for the navigation app in DisplayApp.cpp to load the weather debug app instead:

    case Apps::Navigation:
      //currentScreen = std::make_unique<Screens::Navigation>(this, systemTask->nimble().navigation());
      currentScreen = std::make_unique<Screens::Weather>(this, systemTask->nimble().weather());
      break;

Reprogramming the PineTime

Introduction

The PineTime Dev Kit comes with the back not glued down to allow it to be easily reprogrammed, however the kit does not include an hardware programmer/debugger.

Before you can use your debugger/programmer, you probably have to wire up your pinetime. Heavily recommended you read this first.

There is a bewildering variety of different hardware programmers available but whatever programmer you have there are only a few tasks you will have to learn about:

  • Unlocking the device Note: PineTime watches shipped after 20 Sep 2020 do not require unlocking. They are shipped unlocked.
  • Uploading new software
  • Running a debugger

All of these are described in this article.

Unlocking the device is a one-time action that is needed to enable the debug port and provide full access to the device. Unlocking the device will erase all existing software from the internal flash.

SWD Pinout

See Devkit wiring

The following steps have been tested with the Segger JLink embedded in the NRF52-DK development board.

Hookup

Connect the Pinetime SWD pins to the debugger (P20 on NRF52-DK)

PinetimeJLink
GNDGND
SWDCLKSWDCLK
SWDIOSWDIO
VCC (3.3V)VTG (target detect)

Unlocking the FLASH

Unlocking the device and erase the memory.

You need to execute this step only once, to remove the read protection on the memory. Note that it will erase the whole flash memory of the MCU| :

nrfjprog -f NRF52 --recover

Uploading new software

  1. Program the BLE softdevice (if needed by the firmware). Replace PATH_TO_NRF_SDK by the path where you unzipped the NRF52 SDK :

    nrfjprog -f NRF52 --program /PATH_TO_NRF_SDK/components/softdevice/s132/hex/s132_nrf52_6.1.1_softdevice.hex --sectorerase
    
  2. Program the firmware (replace firmware.hex by the actual filename of the firmware):

    nrfjprog -f NRF52 --program firmware.hex --sectorerase
    
  3. Reset and run the new firmware:

    nrfjprog -f NRF52 --reset
    

OpenOCD

OpenOCD, the Open On-Chip Debugger supports multiple different adapters. You can read more about it here: https://openocd.org/

Adapters

These examples allow you to use telnet to issue futher commands to the devkit. Using them you can connect to 127.0.0.1 (localhost) port 4444 using telnet and invoke OpenOCD commands. GDB should also be available on port 3333.

You can simplify your life by creating a configuration file that contains the interface, transport, target and speed configuration. Things like CLion also need one to work properly.

If you arenā€™t using the latest version of OpenOCD, you might need to substitute things in these examples with older syntax (e.g. instead of adapter speed itā€™s adapter_khz).

CMSIS-DAP

This is a generic specification that is supported by a bunch of different hardware, things such as DAPLink also use it.

Issue this command to initialize a connection to the devkit:

openocd \
   -c 'source [find interface/cmsis-dap.cfg]' \
   -c 'transport select swd' \
   -c 'source [find target/nrf52.cfg]' \
   -c 'adapter speed 8000' \
   -c 'init'

Start OpenOCD:

openocd \
   -c 'source [find interface/jlink.cfg]' \
   -c 'transport select swd' \
   -c 'source [find target/nrf52.cfg]' \
   -c 'adapter speed 8000' \
   -c 'init'

Raspberry Pi

openocd \
   -c 'source bcm2835spi' \
   -c 'bcm2835spi_speed 31200' \
   -c 'source [find target/nrf52.cfg]' \
   -c 'init'

Connect PineTime SWD Pins to ST-Link v2 as follows:

PineTimeST-Link
GNDGND
SWDCLKSWDCLK
SWDIOSWDIO
VCC (3.3V)3.3V

Note that only the bottom row of pins on ST-Link are used.

To flash PineTime with ST-Link on Linux and macOS, use PineTime Updater

ST-Link canā€™t be used to remove nRF52 flash protection

Unlocking the device

If you need to disable access port protection then you can do this using the following commands below.

This can be done in two ways:

Appending this to OpenOCD command line:

-c 'nrf52.dap apreg 1 0x04' -c 'nrf52.dap apreg 1 0x04 0x01' -c 'nrf52.dap apreg 1 0x04'

Or by using the telnet connection, just type in telnet localhost 4444 and then you can issue commands to OpenOCD:

Note: Unlocking the device to remove access port protection will erase the contents of flash.

telnet localhost 4444
  Trying 127.0.0.1...
  Connected to localhost.
  Escape character is '^]'.
  Open On-Chip Debugger
  > nrf52.dap apreg 1 0x04
  0x00000000
  > nrf52.dap apreg 1 0x04 0x01
  > nrf52.dap apreg 1 0x04
  0x00000001

(If the nrf52.dap command cannot be found, try just dap instead.)

Uploading new software

Just issue this command, replace code.hex with your own (and cmsis-dap.cfg with an appropriate adapter).

openocd \
    -c 'source [find interface/cmsis-dap.cfg]' \
    -c 'transport select swd' \
    -c 'source [find target/nrf52.cfg]' \
    -c 'init' \
    -c 'halt' \
    -c 'nrf5 mass_erase' \
    -c 'program code.hex verify' \
    -c 'reset' \
    -c 'exit'

Black Magic Probe

BlackMagic Probe is an JTAG/SWD adapter with open-source firmware, allowing for it to be ported to a multitude of different boards. One of itā€™s defining features is lack of need for intermediate software such as OpenOCD - one would just need to connect to the GDB server running on the chip and proceed with debugging. For more information, refer to wiki.

Native adapters

The native adapters are the official Black Magic family of debug adapters, including the original Black Magic Probe and the Black Magic Probe Mini. By buying the official hardware you are supporting the continued development of the Black Magic Probe software.

Providing your native adapter is running up-to-date firmware then it can be used to program your PineTime.

STM32 (Blue Pill)

It is possible to flash a popular development board based on STM32F103C8T6 microcontroller, known as Blue Pill, to make a BlackMagic Probe device. For example, one may follow instructions in forum post or gist (mac os). Also, it is possible to use SWD pins on the board to flash other devices, instead using arbitrary pins on the board itself. See this link for more detals.

Other hardware

The Black Magic Probe firmware can be run on a variety of host devices. See BMP Debugger Hardware for more information.

Using the BMP to flash the PineTime

Refer to the BMP wiki for the full description of commands. Overall, the process on Linux is like following. (/dev/ttyBmpGdb is a symlink created by the udev rule). Itā€™s useful to create a gdb script file (or .gdbinit) with following commands:

target extended-remote /dev/ttyBmpGdb
monitor swdp_scan
attach 1
file %firmware file%

Then one may use load command to flash the firmware, compare-sections to verify the upload, or monitor erase_mass to erase the firmware.

Then, proceed with debugging as normal.

Switching your PineTime between InfiniTime and Wasp-os

Flashing_`reloader-mcuboot.zip`

Flashing `micropython.zip`

Introduction

Both Infinitime and Wasp-os are very cool OSā€™es for the PineTime and many people will want to try both. This is possible, even with a sealed device!

Both devices use the same Nordic (legacy) DFU protocol for updating firmware over the air. But the BLE stack and the bootloaders for both are different. Thatā€™s why we need to use the reloader. However, instructions you find elsewhere (including Daniel Thompsons video) are somewhat outdated.

Flashing can be done with any of

  • Gadgetbridge (Android >= 4.4)
  • Amazfish (SailfishOS and Linux)
  • Siglo (Linux desktop and Pinephone) (Use the ‘Manual OTA File’ option)
  • PinetimeFlasher (Windows)
  • ota-dfu-python (Linux CLI) which is included in sources of both Infinitime and Wasp-os
    • InfiniTime/bootloader/ota-dfu-python/dfu.py
    • wasp-os/tools/ota-dfu-python/dfu.py

note

We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to Gadgetbridge.

This guide has been last updated for Infinitime 1.1.0 and Wasp-os 0.4.1

InfiniTime to Wasp-os

All the zips you need can be found from the wasp-os installation guide.

  • Make sure the watch is running Infinitime and can be found by your companion device (PC, phone, etc).
  • First we need to flash the reloader, with the wasp-os bootloader as payload. To do this, flash: wasp-os-0.4/build-pinetime/reloader-mcuboot.zip
  • After flashing, it will boot using the Infinitime bootloader (green large pine cone), then itā€™ll hang for a few seconds, then itā€™ll show the reloader animation (blue smaller pine cone), and then itā€™ll boot the wasp-os bootloader.
  • Make sure the watch is on the screen with the pine cone and arrow. If it is in a bootloop instead, then reboot the watch by holding the button until the arrow appears.
  • Now you can flash micropython with wasp-os: wasp-os-0.4/build-pinetime/micropython.zip
  • Wasp-os should now boot. Enjoy!

Wasp-os to InfiniTime

Flashing_`reloader-infinitime-recovery-0.14.1.zip.zip`

Flashing `pinetime-mcuboot-app-dfu-1.1.0.zip.zip`

The reloader-factory.zip was broken in the original wasp-os 0.4 but was fixed in wasp-os 0.4.1. However the Infinitime binaries are outdated the 0.4 release and I do not recommend flashing these. Older InfiniTime versions have flaky BLE which makes upgrading from there very unreliable.

You can get newer binaries from the wasp-os projects CI system (see the wasp-os installation guide) or alternatively, I made my own containing just the InfiniTime 0.14.1 recovery firmware. This allows you to flash any future release of InfiniTime without having to find a new reloader zip. Get the reloader zip here and the latest Infinitime here.

  • Reboot the watch by holding the button until the pine cone arrow appears.
  • Flash the reloader zip: reloader-infinitime-recovery-0.14.1.zip
  • After flashing, the reloader will run (blue smaller pine cone), then itā€™ll boot the InfiniTime bootloader (large pine cone) will run.
  • Boot the watch into recovery mode by holding the button until the pine cone turns red. Itā€™ll boot again (large pine cone will turn green) and then the InfiniTime logo will appear.
  • You can now flash InfiniTime 1.1.0 (or any other version): pinetime-mcuboot-app-dfu-1.1.0.zip
  • InfiniTime should now boot. Enjoy!

Upgrade PineTime to InfiniTime 1.0.0

Devices shipped before July 2021 were pre-installed with InfiniTime 0.7.1, and an older bootloader. This features green text ‘PINE TIME’ on boot, and should be updated to the new bootloader as described below.

Devices shipped after July 2021 were pre-installed with InfiniTime 1.2 and the new bootloader, which can be identified by the green Pinecone image on boot. The instructions below are unnecessary for these devices.

Congratulations on receiving your new PineTime!

So now youā€™re probably wondering exactly how on earth do you go about upgrading your watch to the latest and greatest version of InfiniTime - you know, that version youā€™ve seen all those great pictures, videos and reviews of. To those of us that are developing stuff for it, itā€™s pretty easy and straightforward, but like with all technology, it is a bit tricky.

warning

Some people ran into issues during the update process that would temporarily make their watch unusable (display frozen or blank). The only know workaround consists of waiting for the battery to drain completely and try again. With the display off, and battery fully charged, you can expect a wait of 5-7 days so it is best to not fully charge it. If it freezes with the display on, it will likely be flat by the end of the day. Weā€™ve never heard of any PineTimes that were permanently bricked (were not recoverable), though.

In a nutshell, you need to:

  1. Charge your watch, but not to 100% - keep it at approximately 50% - for the reason described above.
  2. Update InfiniTime
  3. Update the bootloader
  4. Install the recovery firmware

Update Process

So how do you do this? Where do you start? Well, with a sealed PineTime, your only easy option is via Over The Air (OTA) Device Firmware Update (DFU), which is done via Bluetooth. There are a couple of different ways and apps you can use to do this. If you have an Android device, you can use Gadgetbridge or NRFConnect (NRFConnect is available on iOS devices as well). Otherwise, if your laptop or desktop computer has Bluetooth and runs Linux, you can use Siglo or Amazfish. You can also use these applications on your Pinebook Pro or Pinephone if you happen to have those devices.

The reason for installing the updates in the specified order is because newer versions of InfiniTime have a more robust Bluetooth update process, and since weā€™re updating everything over Bluetooth, the fewer retries and failures from that you have the better. It will still sometimes disconnect mid update, meaning youā€™ll need to try again, and possibly restart the watch a few times as well. And since the recovery firmware is new to the 1.0.0 version of the bootloader, itā€™s best to update that last.

This video shows the bootloader and recovery firmware installation procedure.

This video shows the bootloader installation and firmware update using Amazfish.

You can also read a detailed installation procedure in the documentation of the bootloader.

Update InfiniTime

To update the main InfiniTime app, you want to flash pinetime-mcuboot-app-dfu-1.0.0.zip.

After updating InfiniTime, ensure that you validate the firmware to prevent it from being automatically reverted/rolled back if your watch is reset/restarted. To do this, swipe right to access the quick actions panel, press the gear/settings icon, swipe up once to show the second page, press on the ‘Firmware’ option and then on ‘Validate’.

Video showing validation process

Gadgetbridge

  1. Connect your PineTime to GB
  2. Download the aforementioned file to your phone
  3. Find the file in your file manager
  4. ‘Open With’ Gadgetbridge F/W Installer (method varies by device) - on my phone, it is press and hold, select the file, and then choose ‘open with app’ from the more options menu
  5. Confirm that you wish to apply the update
  6. Wait for the update to complete

Video showing how to start the update

The app is closed source and versions after 4.24.3 donā€™t work with the PineTime. We recommend you use GadgetBridge instead.

  1. Download the aforementioned file to your phone.
  2. Open NRFConnect
  3. Scan for your device
  4. Connect to it
  5. Choose the DFU option at the top right of the screen
  6. Ensure the ‘Distribution packet (ZIP)’ option is selected, and press OK
  7. Select your previously downloaded file
  8. Wait for the update to complete

Video showing how to start the update

Siglo

  1. If your device was not detected upon start, press “Rescan” to find it.
  2. Select the “1.0.0” tag
  3. Select the aforementioned file/asset.
  4. Select “Flash It!”

Amazfish

  1. Run Amazfish (service + UI)
  2. Pair with you device:
    1. Unzip the DFU file to extract the .bin file.
    2. Click on “pair with watch” on the top
    3. Select “PineTime” (if your device is running InfiniTime 0.7.1 or lower) or “InfiniTime (if itā€™s running InfiniTime 0.8+) and choose your device in the list
  3. Click on “Download file” on the top
  4. Click on “Choose file” and select the .bin file you extracted from the DFU file
  5. Click on “Send file”
  6. Wait for the update to complete.

See it in action!

Update the bootloader

To update the bootloader, you want to flash reloader-mcuboot.zip. Once the bootloader is updated, you should notice that the boot logo has changed. Previously, it was a green “PineTime” logo, and now it is a large pinecone that is progressively drawn in green.

Video showing what the InfiniTime 1.0.0 bootloader looks like

Using Gadgetbridge

Same process as before, but with the file for this step.

Using NRFConnect

Same process as before, but with the file for this step.

Using Siglo

Same process as before, but select the “0.14.1” tag, and the file/asset for this step.

Using Amazfish

You may need to re-pair with your device by selecting “InfiniTime” (since youā€™ve already upgraded to InfiniTime 1.0) in the device type list. Then follow the same process as before, but with the file for this step.

Install the recovery firmware

warning

Donā€™t do this before updating the bootloader, otherwise your PineTime will freeze at the end of the process, and you will need to wait for the battery to go flat

To install the recovery firmware, you want to flash pinetime-mcuboot-recovery-loader-dfu-0.14.1.zip. You will know when this is running when it shows an InfiniTime logo with a progress bar running across the bottom whilst it is installing the recovery firmware.

Using Gadgetbridge

Same process as before, but with the file for this step.

Using NRFConnect

Same process as before, but with the file for this step.

Using Siglo

Same process as before, but with the file/asset for this step.

Using Amazfish

Same process as before, but with the file for this step.

Troubleshooting

Sometimes during the update process, the connection will drop, and the update will fail. Your PineTime isnā€™t broken, most likely the Bluetooth link dropped for a moment, so just try again. Try rebooting your phone, if it keeps failing, try restarting the watch by holding the power button down for approximately 8 seconds. Try to avoid holding down the button with the screen off. Or try with another device, just in case there are compatibility issues.

Version 1.0.0 of InfiniTime is merely the first version that was considered sufficiently feature-complete and stable enough for daily use. This isnā€™t to say there arenā€™t still bugs present (‘cause there are!). So there are a few bugs still present in the update process and the bootloader. One unfortunate bug appears to be that sometimes when the watch tries to restart after an update, the bootloader locks up, and the watch wonā€™t turn on. In this case, you will need to wait until the watch battery goes flat, to force the watch to reset. This will most likely involve waiting for a week, and then when you put the watch on the charging cradle, it will power up and you should be right to try again.

If you get stuck or have any questions, join us on your preferred chat platform or the product forum. Thereā€™s usually someone available who can help, or will get back to you in a few hours.


Flashing

External flash partitioning

The partition table

Partitions on a storage (disk of flash memory) have different sizes depending on the type of data they contain. Size and location for those partitions could vary along the time, specially if the operating systems (“firmware” on embedded systems) are changed or the same OS is upgraded and it has the need of reestructuring the partitions. In any case, an index has to describe the partition structure somewhere.

What is the basic info to know about a storage partitions?

  • The offset address: indicates where the partion starts on the storage.
  • The partition size: how long is the partition from its offset address.
  • The partition type: identifies what is the usage of the data contents.

Where to store the partition entries

To put that index of partitions hardcoded with the firmware is a bad idea. How to know if those partitions are initializaded with the expected data type? What if the user decides to try another OS and it initializates the the partitions just like the developer has decided to do so? Installed OS has to check the rightness of the storage on every boot? Definitely, the index of partitions must be out of the own OS code in order to keep organized the data on the flash memory.

PC

Nowadays, personal computers use partition tables on every disk (see GPT and MBR). This partitions tables are located very close to the beginning of the disk (reserving some initial sector of bytes for old bootloaders). However, this partition tables are designed for very large disks and they have some complexity related to PC and its computer history, not really needed for flash memories.

SBC

Single board computers and embedded systems with the U-Boot bootloader store the partition index as parameters in the mtdparts environment variable and it is passed to the OS as argument. This environment is hardcoded or stored in a defined partition as text. More recently, they use Device tree but this is also stored in a partition.

ESP32

ESP32 is a MCU managing partitions without hardcoded information and it is a good example to examine. It defines a partition table of 3Kb length at a fixed position with capacity to define 95 partitions. Maybe some excessive but the interesting thing is that partition table follow the pattern “offset-size-type” as basic information.

The summing-up is that every system using partion index and not hardcoded stores the partition entries in some storage space and, this partition table, can be considered a little partition too. It is located in a well-know offset address of the flash storage.

Paper

PineTime is a device under open development and it offers undreamed possibilities to develop and to flash several firmwares by users. It might be convenient that external storage (SPI flash) can be accessed by using a partition table so, different firmwares will be able to share partitions with user information, settings, bluetooth bindings, calibration parameters.

For example, it is possible to organize the flash to save a logo shown by the bootloader. And it doesnā€™t mean a mandatory requirement at the moment but bootloader or application can be changed and they will be able to found the logo to show or save a customized one.

What is the cost of managing a partition table? In this case, community (developers) should be discuss and publish a paper with a proposal for the partition table structures and implement in their firmware for the Pinetime. It must be simple and suitable in order to organize the flash storage in partitions without more overhead than read the partition table, look for the partitions those are interested in and use them.

Partition table draft-#0

Location of partition table: first page of external flash (256 bytes).

C structures

C code:

	#define OFFSET_T uint32_t
	#define SIZE_T   uint32_t

	/* bits 0-7 is type, bits 8-15 is subtype, bits 16-31 are flags */
	#define TYPE_T   uint32_t

	struct partition_entry_t {
	    OFFSET_T offset;
	    SIZE_T size;
	    TYPE_T type;   
	};

	struct partition_table_t {
	    uint32_t magic_bytes;           /* always 0x50494e45 ("PINE") */
	    uint32_t reserved[2];           /* reserved, do not use */
	    partition_entry_t entries[20];  /* 20*sizeof(partition_entry_t) == 240 */
	    uint32_t checksum;              /* CRC-32 MPEG2 variant (used by MCU bootloader) */
	};

	#define PARTITION_TYPE_NOT_USED		0x00
	#define PARTITION_TYPE_BOOT_LOGO 	0x01
	#define PARTITION_TYPE_FACTORY_IMG 	0x02
	#define PARTITION_TYPE_LITTLE_FS 	0x03

SD MCUBoot

The Problem

  • PineTime ships with the MCUBoot bootloader which boots to the InfiniTime Firmware
  • This assumes that the firmware (InfiniTime, Mynewt, Zephyr, …) has its own Bluetooth LE (BLE) stack and can handle firmware updates (DFU)
  • But some firmware (wasp-os, ATCwatch) require Nordic SoftDevice to be installed to support the BLE and DFU functions
  • InfiniTime Firmware does not support flashing of SoftDevice as firmware update, because SoftDevice is usually flashed at address 0x1000 (plus a 4k MBR at 0 to switch between the bootloader and the softdevice), which is used by the MCUBoot Bootloader

How do we allow SoftDevice-based firmware to be flashed to PineTime via DFU?

Dual Bootloader Modes

Process

  • PineTime will support 2 Bootloader Modes: MCUBoot and SoftDevice. Default is MCUBoot Mode.
  • The two bootloaders will not coexist, they will be mutually exclusive. This allows a different flash ROM map for each bootloader.

To switch from MCUBoot Mode to SoftDevice Mode:

  • We will use DFU to flash a SoftDevice Loader Firmware, which will boot and replace MCUBoot by SoftDevice
  • Thereafter, when PineTime boots, it will start SoftDevice and will be ready to accept SoftDevice-based firmware for DFU: wasp-os, ATCWatch

To switch from SoftDevice Mode to MCUBoot Mode:

  • When we need to switch back to MCUBoot-based firmware (InfiniTime, Mynewt, Zephyr, …), we will run an MCUBoot Loader (based on https://github.com/daniel-thompson/wasp-reloader ) to replace SoftDevice by MCUBoot plus a Minimal DFU Firmware that accepts DFU commands
  • After rebooting, MCUBoot starts the Minimal DFU Firmware, which will accept MCUBoot-based firmware for flashing
  • The MCUBoot-based firmware replaces the Minimal DFU Firmware

Switching modes with Companion App

  • The PineTime Companion App (e.g. Gadgetbridge) will switch modes automatically depending on the firmware to be flashed. So PineTime Owners will never have to switch modes themselves.
  • For SoftDevice-based firmware, the Companion App will also know which SoftDevice version is needed by the firmware, and load the right SoftDevice version.

Custom MBR

Itā€™s possible to customise (SoftDeviceā€™s) MBR so that it can coexist with MCUBoot.

Pros:

  • Seamless switching between the two different boot modes. Both MCUBoot and SD can be used.

Cons:

  • Technically more complex - documentation isnā€™t the best, SoftDeviceā€™s future support for custom MBR might be unknown

Should be explored further.

External resources about custom MBR

wasp-reloader

wasp-reloader is a simple bare-metal C program that, in a fairly brutal fashion, can rewrite critical parts of the internal flash. It has fairly robust checksumming to avoid flashing problems and verifies that what it written is what is supposed to be written. Having said that more checks (no self-overwrite, battery level) would be nice. When complete it stops updating the watchdog in order to provoke get a clean reset. As a standalone app it can be loaded into the secondary slot using Infinitime DFU upgrade.

Currently it has linker scripts for SD132 V6.1.1 and can flash any payload smaller than SD132.

To be used to install SoftDevice from mcuboot it would need linker scripts for mcuboot (to ensure the reloader sits high enough in flash to avoid a self-overwrite). To install mcuboot we need an Intel HEX file of the mcuboot payload and InfiniTime (we have to write it in one shot to keep BLE updates working after switching bootloaders). This may need custom linker scripts depending on how big the Infinitime binaries are.


Watchfaces

Custom watchface

This is a tutorial to help new users create custom watchfaces based on the InfiniTime Firmware for PineTime made by user JF002, thanks to him for its development.

We will explain some of the things we went through while creating some custom Watchfaces, so consider this as a log of experiments of sorts..

So stay tuned to it as it will be dynamically updated.

What you need to start

The entire building process will be done by GitHub, so all you need is a device which can give you a GitHub Web Client, a PC, or tablet to give you enough screen space to review your code and a steady internet connection.

Since the compiling and file management is done by GitHub online, you have nothing else to worry about other than working with the files that display the watch face.

So with those things settled, letā€™s start with the basics of a watchface.

Please remember that this is a public documentation, so you can make an account and help us improve this page.

Please make sure not to unilaterally remove info though, but offer an alternative. If it is indeed a better way, in time your alternative will grow into the main text, and the latter info will be pruned.

Overview

The Firmware (also called InfiniTime) we will be working with is made with a programming language named C++.

Basic knowledge of C/{cpp} is required to understand the advanced watch faces as that requires more complex code, but you can still do a some cool things without much knowledge of {cpp} programming, just some small edits to existing programs.

InfiniTime uses the LVGL graphics library to provide users with a simple and clean UI without overpowering the Nordic nRF52832 microcontroller, which is the brain of the watch.

To get the watchface to work, there are these basic steps. We will go over each step separately, so donā€™t be daunted, all will become clear soon.

For now, we will modify the existing watchface, change the positioning of the text labels, add an icon to an existing watchface and later on, we will do a full watchface.

The main file we need to focus on is the WatchFaceDigital.cpp file, which contains most of the data attributed to what we see on the watchface, including The time/day characters, the battery, and Bluetooth icons, and also pedometer count.

So, everything we will be doing in the basic modifications is purely messing with this single file.

Labels

What are labels?

Labels are considered as “elemental” parts that make up a screenā€™s Text-based UI by the LVGL library. Each label is also considered as an “object” or “obj” and can be manipulated. By changing the data attributed with the “obj”, for example, position, internal data like the strings/text etc. we can change what the label shows and where it shows it.

Modifying label data

letā€™s observe something small like the word “BPM” near the bottom of the watch face.

If we take a look at the source file of the watchface, (a.k.a. the clock.cpp file) we can observe that these particular lines are what attributes to the word “BPM” being displayed.

heartbeatBpm = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(heartbeatBpm, "BPM");

We can modify the text inside the quotes and replace the word ‘BPM’ between those quotes to something like ‘LOVE’ and the result after compiling the firmware again with the changes and flashing it to the watch would be that the text changes on the watch face and displays ‘LOVE’ in place of ‘BPM’.

If this happened correctly, then you have successfully made a custom new watchface|Now we can do something a bit more complex.

Now take for in instance the days of the week that we have on the bottom line with the date.

they are stored a “C array” which is basically multiple words separated by commas.. the date is in a format of

which in the source file is expressed as,

sprintf(dateStr, "%s %d %s %d", DayOfWeekToString(dayOfWeek), day, MonthToString(month), year);
lv_label_set_text(label_date, dateStr);

here,

%s %d %s %d

stores the print format of the variables, and

DayOfWeekToString(dayOfWeek), day, MonthToString(month), year

are the variables themselves

Now, if the date was a Saturday 11/7/2020, you can observe that the date looks like

SATURDAY 11 JUL 2020

as seen in the above image (the one where we changed BPM to LOVE)

by modifying the format of the variables, we can change how those words are arranged, and add some extra characters if we like (there is a catch to the list of characters you can use however, but it will be discussed later)

For example,

If we modify the format by adding a comma “,” in between the second “%s” and “%d” like this,

"%s %d %s, %d"

and changing that in the line

sprintf(dateStr, "%s %d %s, %d", DayOfWeekToString(dayOfWeek), day, MonthToString(month), year);

we can make the date become

SATURDAY 11 JUL, 2020

which means we now were able to modify how the text got displayed to make it a bit more nice

but if you havenā€™t noticed, the line containing the date is already full, meaning we will get some problems while displaying the date and causing it to wrap around, making a single character go to the next line and look more like,

SATURDAY 11 JUL, 202
0

So, why donā€™t we shorten the characters present in the date from being “SATURDAY” to simply just “sat.” (It will have the small period at the end, and is only 3 characters long). I will also convert the months of the year from Capital to small letters.

For that look into the part where the days of the week of are stored as text, and also while looking at it, we can solve another question, why was there two variables in the date format that looked like, DayOfWeekToString(dayOfWeek), and MonthToString(month) ?

It is because the system gives the date/ time as numbers (Monday-1, Tuesday-2 Wednesday-3 for the days, and 1-January, 2-February, 3-March ), and so, a function along with a C array is used to assign these numbers to Days/Months in text form as it is easier to read.

this is the Array containing the day of the week, (as text)

char const *Clock::DaysString[] = {
        "",
        "MONDAY",
        "TUESDAY",
        "WEDNESDAY",
        "THURSDAY",
        "FRIDAY",
        "SATURDAY",
        "SUNDAY"
};

and this Array stores the months of the year, (as text)

char const *Clock::MonthsString[] = {
        "",
        "JAN",
        "FEB",
        "MAR",
        "APR",
        "MAY",
        "JUN",
        "JUL",
        "AUG",
        "SEP",
        "OCT",
        "NOV",
        "DEC"
};

Here we can see that the days are stored in a full format as “SUNDAY”, “MONDAY”, “TUESDAY” etc. We can change all of them to a shorter format like “sun.”, “mon.”, “tue.”, to make it short and nice. While doing so, we can even make the months use small letters, as said before.

so the source file (clock.cpp) becomes,

(for the days of the week)

char const *Clock::DaysString[] = {
        "",
        "mon.",
        "tue.",
        "wed.",
        "thu.",
        "fri.",
        "sat.",
        "sun."
};

and

(for the months of the year)

char const *Clock::MonthsString[] = {
        "",
        "jan",
        "feb",
        "mar",
        "apr",
        "may",
        "jun",
        "jul",
        "aug",
        "sep",
        "oct",
        "nov",
        "dec"
};

which means now our original date, Saturday 11/7/2020 will become

sat. 11 Jul, 2020

you now know how to change the data present in a label object, and the format of it..,

Here is a fun idea you can try: you can even replace the days with whatever thing that tells you (or) reminds you the day of the week (like the food served in the cafƩ, Monday/taco, Tuesday/burger, Wednesday/pasta etc.)

note

When making the custom array, donā€™t forget to leave an empty "" as the first element of the array, This is because the date is given by the system in a natural numbers format (1,2,3, and so on) rather than a zero-starting format (0,1,2,3, and so on), which the C array uses to index. So the C array indexes the days as “"-0, “Monday”-1, “Tuesday”-2 etc. and the months as “"-0, “January”-1, “February”-2 and so on.

Label positioning

The locational placement in LVGL is done on a Cartesian plane, where each object can have dynamic origin placement, and the Y-axis is inverted. So going down is done with a positive Y-axis value and not negative as it is by default.

LVGL coord system

The position of the various objects in WatchFaceDigital.cpp are set by the line,

lv_obj_set_pos(<obj>, <new_x>, <new_y>)

and the top-left corner is the Cartesian origin, aka coordinates (0,0)

this image can show you how to decide label placement for lv_obj_set_pos(…)

We use another function, that is more advanced, that gives the positional alignment based on preset locations

lv_obj_align(obj, obj_ref, LV_ALIGN_..., x_ofs, y_ofs);

obj is your text label

obj_ref is a reference object to which obj will be aligned. If obj_ref = NULL , then the parent of obj will be used. If obj_ref = lv_scr_act(), then the whole screen will be used.

LV_ALIGN_… is the type of alignment; inside another object or next to the reference, for example IN_TOP_LEFT, OUT_BOTTOM_MID, …

x_ofs, y_ofs allow you to shift the object by a specified number of pixels after aligning it

Label positioning based on alignment is both a simple and complicated thing to understand, so here I have given something you can refer to while modifying the position of the various labels and objects.

You can also refer here to LVGLā€™s documentation of coordinate system https://docs.lvgl.io/master/overview/coords.html

List of the possible alignments: https://docs.lvgl.io/latest/en/html/widgets/obj.html#alignment

It is however recommended that you use the first method to set the location

lv_obj_set_pos(<obj>, <new_x>, <new_y>)

as it is simple and easier for beginners

Here is a small example.

Take the Label that tells the date, In the Digital Clock source file (WatchFaceDigital.cpp) it is this line,

lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);

by increasing the Value of the Y coordinate (60) to a higher value, we can bring the position of the Date downwards a bit away from the Time, and toward the Heartbeat count in the bottom row here I will increase it to 80, so it becomes..

lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 80);

and now we have made some space up top..

now letā€™s try something a bit complex,

Take the position argument for the label that tells you time. Here, in the source file (WatchFaceDigital.cpp),

lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);

this line determines the position of the Label telling time, as seen in the image

weā€™re modifying this, by changing the origin alignment parameter (here it is LV_ALIGN_IN_RIGHT_MID) to LV_ALIGN_IN_TOP_LEFT

you can alternatively swap the whole line to:

lv_obj_set_pos(label_time, 0, 0);

this makes the Time label/obj. to go to the top-left corner

but I will do something a little extra, I will modify the label that store the data and Time format, i.e this line,

sprintf(timeStr, "%c%c:%c%c", hoursChar[0],hoursChar[1],minutesChar[0], minutesChar[1]);

by removing the “:” colon in between the numbers, and replacing it with a Newline symbol “\n” I change it to become,

sprintf(timeStr, "%c%c\n%c%c", hoursChar[0],hoursChar[1],minutesChar[0], minutesChar[1]);

this gives it a nice wrapped text format in the top corner, and gives us some space to play with in the side, for things like Pictures and icons, which we will do next..

If you have been able to do these things, you now have completed the 2nd part of the tutorial, and now know how to change and modify the position of labels.

Using icons

The LVGL library allows for the use of widgets known as “Images”, In short it allows you to use small Icons like pictures with a small dedicated function, However, when this was attempted the first time we stumbled on some problems as LVGL v6 (used on the PineTime) is not much documented as the latest release (v7 as of August 2020) but also the existing code was only documented for C not {cpp}, after some painful attempts we were able to translate it into {cpp},

To bring images into Clock.cpp you will need to do the following,

  1. Have a small image that cannot exceed a maximum size of 240px x 240px (PineTime max resolution)
  2. Use this Image converter (Thanks to LVGL) https://lvgl.io/tools/imageconverter to convert your image to a C array and having the Color format as “True color” and the output format as “C array”. Make sure to use something simple as the name we will be using “bitmap” as the name, but will also be referred as for simplicity

note

for example we shall use = bitmap, but any simple word can be used, as long as it does not cause problems with system variables

Image size considerations

since the image will be using the flash directly, we need to be considerate about flash memory usage.

<picture_X> x <picture_Y> x 2

gives you the number of KB the image used in storage

where, <picture_X> <picture_Y> are the dimensions of the image horizontally and vertically

for example,

if <picture_X>=80px <picture_Y>=64px

then,

total storage used = 80 x 60 x 2 = 10.24KB

Please use the flash storage with consideration, when using other apps as well, excess usage of storage might mean the Firmware will not compile. The limit to storage to about 400Kb for the user, the firmware size must not exceed that.

Preparing the image for inclusion as an icon

Once you have obtained your C array from the LVGL converter, you can take a look inside it to see all the different formats of your image, try using something like Notepad++ or any of your favorite text editors to peek inside it,

there will be 4 sets of Arrays inside it that look like,

#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
 /*Pixel format: Red: 3 bit, Green: 3 bit, Blue: 2 bit*/
 0x00, 0x00, 0x00,...
...0x00, 0x00, 0x00, 
#endif

#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0
 /*Pixel format: Red: 5 bit, Green: 6 bit, Blue: 5 bit*/
 0x00, 0x00, 0x00,...
...0x00, 0x00, 0x00, 
#endif

#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0
 /*Pixel format: Red: 5 bit, Green: 6 bit, Blue: 5 bit BUT the 2 bytes are swapped*/
 0x00, 0x00, 0x00,...
...0x00, 0x00, 0x00, 
#endif

#if LV_COLOR_DEPTH == 32
 /*Pixel format: Fix 0xFF: 8 bit, Red: 8 bit, Green: 8 bit, Blue: 8 bit*/
 0x00, 0x00, 0x00,...
...0x00, 0x00, 0xff, 
#endif
};

And another small bit of info we will need for later that looks like,

 const lv_img_dsc_t bitmap = {
  .header.always_zero = 0,
  .header.w = 40,
  .header.h = 40,
  .data_size = 1600 * LV_COLOR_SIZE / 8,
  .header.cf = LV_IMG_CF_TRUE_COLOR,
  .data = bitmap_map,
 };

note

There are some header files at the top, which we can ignore.

RGB565 image format

The PineTime uses a display that uses a 16 bit color space, also known as RGB565.

These 16 bit are assigned to RGB as 5 bits each for Red and Blue and 6 bits for Green, so 5+6+5=16 bits are required, so each pixelā€™s color occupies 2 bytes of data, and since 2^16^ is equal to 65,536 it allows us to view 65,536 or 65k colors

The way it packs these bits is by converting the bits into 2x 4+4 bit hex-code, so for example,

if the color of a pixel in Binary is 10110100 01011111 (this color is approximately Lavender purple)

It is split as 1011 & 0100 for the first byte and 0101 & 1111 for the second byte and so, converting the binary into Hex-code,

the two parts are 0xB4 and 0xF5

These two parts in conjunction are used for determining the color of one pixel.

also from the binary, it is observed that,

The bits 10110 is used for Red, 100010 is used for green, and 11111 is used for blue.

Flipping the bytes

The LVGL library has a feature that allows you to flip the two bytes of the pixel, so if the two parts were, …0xB4,0xF5,… ,it will change it to become, …0xF5,0xB4,…

The reason for this is to allow the use of 8-bit SPI interfaces, but we do not require it, and if set with wrong parameter we could get problems with the color…

To make sure you are ready for the next step, make sure that inside your LVGL configuration file (located at src/libs/lv_conf.h)

this parameter,

#define LV_COLOR_16_SWAP   1

is set to “1” as seen.

note

If you havenā€™t modified it or tampered with it with your GitHub fork, you shouldnā€™t have a problem as it is correct by default, and you can skip these steps

Creating an Object from the Array

To include the Icon, first Identify the Array you need to copy to the source (clock.cpp)

The one we require from it is the data below the tag that looks like:

#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0
/*Pixel format: Red: 5 bit, Green: 6 bit, Blue: 5 bit BUT the 2 bytes are swapped*/
0x00, 0x00, 0x00,...
...0x00, 0x00, 0x00, 
#endif

from this copy the Data from the array alone… I.e this part,

0x00, 0x00, 0x00,...
...0x00, 0x00, 0x00

(Make sure to not include the comma at the end or the #endif as the entire part is going to substitute a new array)

In clock.cpp, just below the header files and the Task creation part (I.e event_handler…),

static void event_handler(lv_obj_t * obj, lv_event_t event) {
Clock* screen = static_cast<Clock *>(obj->user_data);
screen->OnObjectEvent(obj, event);
}

create a name for the label with,

static lv_img_dsc_t <name>; // remember to replace <name> with the actual name you gave to your image while converting!

then below it create a array to hold the data with,

const uint8_t <name>_map[] = {}; // paste the array you copied from the conversion file we specified above...

so your array is something like,

const uint8_t <name>_map[] = {0x00,0x00,0x00...
...0x00,0x00,0x00};

so your Entire top region of declaration looks like,

#include <cstdio>
#include <libs/date/includes/date/date.h>
...
using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_extrabold_compressed;
extern lv_font_t jetbrains_mono_bold_20;
extern lv_style_t* LabelBigStyle;
   
static void event_handler(lv_obj_t * obj, lv_event_t event) {
 Clock* screen = static_cast<Clock *>(obj->user_data);
 screen->OnObjectEvent(obj, event);
}
   
//Declare the descriptor here
static lv_img_dsc_t <name>;
//place the Image data here
const uint8_t <name>_map[] = {0x00,0x00,0x00...
...0x00,0x00,0x00
};

note

Declaring variables outside a function like we did above is known as global scope declaration, this means the variable can be used by not just one function but the Entire code.

Then inside the

Clock::Clock(DisplayApp* app,...){...

region (the watchface function), you need to place a particular set of lines which LVGL uses to define the object to declare the array as an Icon/Image, You can place this set of lines above label_time.

 <name>.header.always_zero = 0; //Initialization
 <name>.header.w = <picture_X>;                     // Setting the Width (or) Horizontal length of the image (number of px)
 <name>.header.h = <picture_Y>;                     // Setting the Height (or) vertical length of the image (number of px)
 <name>.data_size = <Hr_length> * <Vr_length> * LV_COLOR_SIZE / 8; //Allocation of memory for the image
 <name>.header.cf = LV_IMG_CF_TRUE_COLOR; // Sets the color scheme for the image
 <name>.data = <name>_map;                // Maps the Image data to the Array
 lv_obj_t *img_src = lv_img_create(lv_scr_act(), NULL);  // Create an image object
 lv_img_set_src(img_src, &<name>);        // Set the created file as image (<name>)

again, make sure to replace with the name you gave it during conversion!

Now that we have bought in the image data, we need to set the position, you can place this just below the lines we wrote for bringing in the image, It can be done with either,

lv_obj_set_pos(img_src, <x_pos, <y_pos>); // <x_pos>, <y_pos> are the coordinates of the Cartesian plane

or,

lv_obj_align(img_src, lv_scr_act(), LV_ALIGN_<parameter>, <x_pos, <y_pos>); 

If done correctly, you will now have a beautiful little Icon/Image in your Watch face, make sure that your Watch face can accommodate the image by pushing the other labels farther away, creating space for it.

We have provided a small template you can use for adding even a large image comfortably

If you have succeeded with this, you have completed part 3 of the tutorial.

Creating an entirely new watchface

The instructions above describe how to modify the existing default watchface, if you would like to create a new watchface instead you will need to complete some additional steps. We will refer to the new watchface as WatchFaceName in these instructions.

Create the watchface files

The watchface is composed of 2 files, WatchFaceName.cpp and WatchFaceName.h. You can copy them from one of the existing watchfaces and give it a new name to provide a basic layout to start from. It is important to increment the ClockFace number near the top of WatchFaceName.cpp otherwise the wrong watchface will be displayed when leaving the menu.

settingsController.SetClockFace(0);

Add the watchface to Clock.cpp and Clock.h

Clock.cpp now provides the ability to switch between multiple watchfaces by long-pressing the screen. You will need to make 3 modifications in Clock.cpp and 2 modifications in Clock.h.

src/displayapp/screens/Clock.cpp

#include "WatchFaceDigital.h"
#include "WatchFaceAnalog.h"
#include "WatchFaceName.h"

               [this]() -> std::unique_ptr<Screen> { return WatchFaceDigitalScreen(); },
               [this]() -> std::unique_ptr<Screen> { return WatchFaceAnalogScreen(); },
               [this]() -> std::unique_ptr<Screen> { return WatchFaceNameScreen(); },

std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {  
  return std::make_unique<Screens::WatchFaceAnalog>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
}

std::unique_ptr<Screen> Clock::WatchFaceNameScreen() {  
  return std::make_unique<Screens::WatchFaceName>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController);
}

src/displayapp/screens/Clock.h

         ScreenList<3> screens;
         std::unique_ptr<Screen> WatchFaceDigitalScreen();
         std::unique_ptr<Screen> WatchFaceAnalogScreen();
         std::unique_ptr<Screen> WatchFaceNameScreen();

Be sure to increment the number of screens.

Add the watchface to CMakeLists.txt

src/CMakeLists.txt

       ## Watch faces
       displayapp/icons/bg_clock.c
       displayapp/screens/WatchFaceAnalog.cpp
       displayapp/screens/WatchFaceDigital.cpp
       displayapp/screens/WatchFaceName.cpp

Creating an entirely new watchface (Updated Alternative)

The previous method may not work with the current version of Infinitime as of (2023 Jan 28). Therefore, here I will tell you a method of creating watch faces on the current build.

Create the watch face files

The watch face is composed of 2 files, WatchFaceName.cpp and WatchFaceName.h. You can copy them from one of the existing watch faces and give it a new name to provide a basic layout to start from.

Important do not forget to rename the class names to reflect the new filenames.

Add the watchface to Clock.cpp and Clock.h

Clock.cpp now provides the ability to switch between multiple watchfaces by long-pressing the screen. You will need to make 3 modifications in Clock.cpp, 1 modification in Clock.h and two modifications in SettingsWatchFace.h which will allow us to select the newly created watch face.

src/displayapp/screens/Clock.cpp

#include "displayapp/screens/WatchFaceDigital.h"
#include "displayapp/screens/WatchFaceAnalog.h"
#include "displayapp/screens/WatchFaceName.h"



switch (settingsController.GetClockFace()) {
       case 0:
         return WatchFaceDigitalScreen();
         break;
       case 1:
         return WatchFaceAnalogScreen();
         break;
       case 2:
         return WatchFacePineTimeStyleScreen();
         break;
       case 3:
         return WatchFaceTerminalScreen();
         break;
       case 4:
         return WatchFaceInfineatScreen();
         break;
       case 5:
         return WatchFaceCasioStyleG7710();
         break;
       case 6:
         return WatchFaceNameScreen();
         break;
     }
     return WatchFaceDigitalScreen();
   }

std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {  
  return std::make_unique<Screens::WatchFaceAnalog>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
}

std::unique_ptr<Screen> Clock::WatchFaceNameScreen() {  
  return std::make_unique<Screens::WatchFaceName>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController);
}

src/displayapp/screens/Clock.h

       std::unique_ptr<Screen> screen;
       std::unique_ptr<Screen> WatchFaceDigitalScreen();
       std::unique_ptr<Screen> WatchFaceAnalogScreen();
       std::unique_ptr<Screen> WatchFaceNameScreen();

Since I have set WatchFaceName to case 6 in a switch statement beforehand it will take sixth position in a list.

src/displayapp/screens/settings/SettingWatchFace.h

         #include "displayapp/screens/WatchFaceInfineat.h"
         #include "displayapp/screens/WatchFaceCasioStyleG7710.h"
         #include "displayapp/screens/WatchFaceName.h"

          std::array<Screens::CheckboxList::Item, settingsPerScreen * nScreens> watchfaces {
         {{"Digital face", true},
          {"Analog face", true},
          {"PineTimeStyle", true},
          {"Terminal", true},
          {"Infineat face", Applications::Screens::WatchFaceInfineat::IsAvailable(filesystem)},
          {"Casio G7710", Applications::Screens::WatchFaceCasioStyleG7710::IsAvailable(filesystem)},
          {"Name Face", true},
          {"", false}}};
       ScreenList<nScreens> screens;

Add the watchface to CMakeLists.txt

src/CMakeLists.txt

       ## Watch faces
       displayapp/icons/bg_clock.c
       displayapp/screens/WatchFaceAnalog.cpp
       displayapp/screens/WatchFaceDigital.cpp
       displayapp/screens/WatchFaceName.cpp

Using git to work on the firmware

Cloning the repository

Instructions for cloning the repository are available on the Building and programming page on github.

Changing the code to add the image

Use the editor of your choice to modify the source files. Please read the coding conventions before you start.

Compiling the firmware

Information about how to compile the firmware is included on the Building and programming page on github.

Testing the firmware

Installing the new firmware

A holistic guide on how to install different firmware using various hardware programmers is available here: Reprogramming the PineTime.

If you would like to install the firmware by OTA/DFU, you can follow these steps:

cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=/path/to/gcc-arm-none-eabi-9-2020-q2-update -DNRF5_SDK_PATH=/path/to/nRF5_SDK_15.3.0_59ac345 -DUSE_OPENOCD=1 -DBUILD_DFU=1 ../
make -j pinetime-mcuboot-app

Be aware the paths for the cmake command must be absolute. The -DBUILD_DFU argument will generate a zip file which can be flashed using nRF Connect (not recommended) or Gadgetbridge on Android. You must have adafruit-nrfutil installed in your $PATH for this to work.

PineTimeStyle

PineTimeStyle is a watchface created for Infinitime and has been included since version 1.3 of Infinitime.

It is based on TimeStyle for Pebble by Dan Tilden with permission.

PineTimeStyle Watchface

Layout

The time is displayed on the left in either 12 or 24h format depending on the system setting. An indicator will appear in the bottom left corner if 12h is selected.

In the sidebar on the right there is a battery icon which displays the current charge level, and is replaced by a plug icon when charging. Below it is a bluetooth icon which appears only when bluetooth is connected, and a lower case i which indicates unread notifications are available, generally only if notifications are disabled via the quick settings screen.

The current date is displayed in the middle, and at the bottom there is a small gauge which displays the step count, the needle will rotate in a clockwise direction as the step count increases. The gauge scales automatically based on the configured step goal, and the outside of the gauge will turn white when the step goal is reached.

Customisation

Since version 1.4 of Infinitime, a color picker is available for PineTimeStyle. As of version 1.11, an addition page of customisation has been added which allows the user to customise the sidebar. These settings can be accessed by long pressing on the watchface which will display two buttons, one with a palette icon to access the color settings, one with a cog icon for the sidebar options.

PineTimeStyle Settings Button

The color picker

PineTimeStyle Color Settings Interface

There are 3 pairs of buttons with left and right arrows which scroll through 17 standard colors. The top pair changes the time text color, middle for the sidebar color and bottom for the time background color.

There is also a ‘Rnd’ button which randomises all colors, a ‘Rst’ button which resets the colors to standard teal and black, and a cross which closes the settings interface. It can also be closed using the physical button.

The sidebar settings

PineTimeStyle Sidebar Settings

As of Infinitime version 1.13, there are 2 options available in the sidebar settings page - Steps style and Weather.

By pressing the ‘Steps style’ button you can toggle between 3 options, the original full gauge, a half size gauge plus seconds display, or a numerical step count display. The second button, Weather, enables or disables this function. When no weather data is available a ban icon will be displayed with 2 dashes below, and when weather data is available the current temperature plus an icon showing conditions based on the cloud cover and precipitation amount.

To learn more about the sending weather data to the watch, see InfiniTime weather


FAQ

Does PineTime run Linux?

No. Please read this forum article for information about Linux on PineTime. Also check out the article “PineTime doesnā€™t run Linux… But thatā€™s OK!”

Why are there two versions of the PineTime in the store?

See the below question and answer.

Why can I only buy the closed version in a 3-pack, and the open version per one?

TL:DR: The unsealed PineTime is for development, the sealed one is for production use, because of firmware uploads. That is also why they were initially sold three in a pack.

In the current situation in development there are some reasons to want to be sure you only experiment with an open device. If you install the wrong firmware, your device could be bricked, until you find a way to open it, which will likely damage the device. The idea is that if you want to develop an application for the PineTime, you will be testing it out first, and only after you know for sure your new firmware is well tested, you will install it on sealed devices. If you are in the beta stage, having more than one PineTime is probably a good idea. So to prevent people from locking themselves out at the first test, it was decided to sell the closed version only as a pack of 3. Development can be done on an open device, so any issues can be easily handled.

How long does it take to ship my PineTime?

That depends on whether you chose for Standard or Express shipping. Standard shipping for the dev kit may take up to a few weeks.

How do I install new software on PineTime?

If you have a Sealed PineTime, flash only tested PineTime Firmware to your PineTime. If you flash unknown PineTime Firmware, your sealed PineTime may be bricked relatively permanently.

There is a lot of different software for flashing. Check out Development page for a list of companion software.

Also see this page to see various methods of reprogramming the devkit PineTime the wired way.

Remember to validate the firmware after flashing: Swipe up to show the menu, tap the checkmark icon, tap “Validate”.

My PineTime arrived, now what?

You should start by testing out all the features of the watch, to make sure everything works. Power it on and check the display.

PineTime is shipped with InfiniTime firmware. Press the watch button to show the clock, then swipe up on the touchscreen to reveal the menu.

You probably now wish to update the bootloader and firmware.

What’s the OS that’s preinstalled on the PineTime by default?

PineTime ships with the open source InfiniTime firmware.

To support firmware update and rollback, PineTime includes the open source MCUBoot Bootloader.

Can we use this OS or its source code?

Yes, InfiniTime and the MCUBoot Bootloader are open source.

Why is the back exposed? Is it supposed to snap on?

The back cover of the PineTime devkit is exposed so that you can flash and debug the device with the SWD pins. The main unit and cover does not snap (lock) together. If you want to attach the back cover anyway, you can use glue or tape.

What hardware should I use to flash code to the PineTime?

There are several ways you can do this, check out Reprogramming the PineTime

How do I connect the PineTime to a programmer?

Hereā€™s how: Devkit wiring

How do I set the time on PineTime?

In recent versions of the firmware, you can set it directly from settings.

You can also use either GadgetBridge or the closed-source nRF Connect and Da Fit apps. See “Set PineTime Date and Time with nRF Connect”

You can also set the time using your PinePhone or other Linux-based Bluetooth LE capable device with the Bluez software installed. Install the bluez package and make sure your PineTime is running and awake with InfiniTime 0.7.1 or later.

$ bluetoothctl
[ bluetooth ]# scan on
...
[NEW] Device D7:03:FB:6E:31:B2 Pinetime-JF
...
[bluetooth]# pair D7:03:FB:6E:31:B2
Attempting to pair with D7:03:FB:6E:31:B2
...
[NEW] Characteristic (Handle 0xfd80)
  /org/bluez/hci0/dev_D7_03_FB_6E_31_B2/service0015/char0016
  00002a2b-0000-1000-8000-00805f9b34fb
  Current Time
...
[Pinetime-JF]# menu gatt
...
[Pinetime-JF]# select-attribute /org/bluez/hci0/dev_D7_03_FB_6E_31_B2/service0015/char0016
[Pinetime-JF:/service0015/char0016]# read
Attempting to read /org/bluez/hci0/dev_D7_03_FB_6E_31_B2/service0015/char0016
[CHG] Attribute /org/bluez/hci0/dev_D7_03_FB_6E_31_B2/service0015/char0016 Value:
  b2 07 01 01 00 04 15 00 00                       .........    
  b2 07 01 01 00 04 15 00 00                       .........
[Pinetime-JF:/service0015/char0016]# write "0xe4 0x07 0x0c 0x1f 0x0e 0x02 0x00 0x00 0x00"

Use the list-attributes command to find the correct path. Look for the characteristic named ‘Current Time’

This is the format for the current time as hex bytes:

<lsb of year> <msb of year> <month (1-12)> <day (1-31)> <hour (0-23)> <minute (0-59)> <seconds (0-59)> <weekday (1-7 where Monday)> <fractions (1/256th of second)>

Is there a standard agreed method of pushing OTA updates so that one could seal the PineTime dev kit nicely?

InfiniTime supports firmware updates over Bluetooth LE with the nRF Connect mobile app. See “Download and Test Our PineTime Firmware”

My PineTime’s screen shows garbage, how do I fix it?

This is usually caused by unplugging the device after it has booted, it needs to be reinitialised. To do so just restart the watch by removing power to it.

I have experience developing on Arduino. How does the PineTime compare?

To learn programming on PineTime, check out this article

Arduino provides the Arduino IDE (or you use the avr-gcc and avrdude command-line tools) which you can use to compile and upload code to an Arduino board. The PineTime and its ARM processor doesnā€™t have this, so youā€™ll have to familiarize yourself with tools like GCC for ARM, and OpenOCD. Some experience with Arduino does translate over to the PineTime, especially if youā€™ve worked with LCDā€™s, or SPI. The PineTime is at least four times faster than an Arduino Uno (even faster at certain specific workloads due to hardware acceleration), and it has 32 times more RAM and 16 times more flash storage.

Lup Yuen Lee (just call him Lup, rhymes with “Up”) has written many articles on PineTime programming. Check out the articles here

Can I code firmware for PineTime without an actual PineTime?

Yes, you may code PineTime Watch Faces and preview them in a web browser (thanks to WebAssembly).

PineTime Simulator

Then flash your firmware remotely to a real PineTime via Telegram, and watch your firmware run in a live video stream.

Remote PineTime

What do I need for building PineTime firmware locally on my computer?

Most flavours of PineTime firmware (InfiniTime, Hypnos, Klok, wasp-os) will build fine on Linux (x64, Arm32, Arm64) and macOS. Just follow the instructions provided.

Download version 9-2020-q2-update of the Arm Embedded Toolchain arm-none-eabi-gcc. Other versions of gcc may have problems building the firmware correctly.

On Windows, install Windows Subsystem for Linux (WSL) and execute the build steps inside the WSL Terminal (instead of the Windows Command Prompt). USB Programmers (like ST-Link and JLink) are not supported in WSL, so use the Windows Command Prompt to flash your built firmware to PineTime.

pinetime-rust-mynewt firmware for PineTime supports building and flashing via the Windows Command Prompt (no need for MinGW and Docker).

Can I use Pinebook Pro for developing PineTime?

Yes, use version 9-2020-q2-update of the Arm Embedded Toolchain arm-none-eabi-gcc. Other versions of gcc may have problems building the firmware correctly.

What is ARM Semihosting?

We use the SWD (Single Wire Debug) protocol created by ARM for flashing and debugging PineTimeā€™s nRF52832 microcontroller, which contains an ARM CPU. (SWD is derived from standard JTAG, but with fewer wires) With ARM CPUs you can trigger a software breakpoint, and allow the debugger (OpenOCD) to do something really nifty: Display a message, read console input, dump out a file, even read a file. Thatā€™s called ARM Semihosting. More about ARM Semihosting

What is OpenOCD?

OpenOCD is Open On-Chip Debugger. Itā€™s the software that drives your microcontroller debugger/flasher. We need it for running any kind of flashing and debugging with Pi or ST-Link. gdb talks to OpenOCD for debugging firmware. gdb also works with VSCode for debugging firmware visually. More about OpenOCD

Please use xPack OpenOCD with PineTime. Other versions of OpenOCD seem to have problems with PineTime.

How do I remove flash protection?

PineTime watches shipped before 20 Sep 2020 have flash protection enabled.

The flash protection can be removed using multiple different methods. If you donā€™t have anything except the PineTime, not even a Raspberry Pi, then you have to order a programmer online: you can use a J-Link, CMSIS-DAP dongle and various other programmers. See this page to see various methods of reprogramming the PineTime.

If your PineTime was shipped after 20 Sep 2020, you donā€™t need to remove flash protection. They are shipped with flash protection disabled. You can flash and debug PineTime right away with ST-Link, JLink and Raspberry Pi.

Because ST-Link is a High Level Adapter. It doesnā€™t really implement all SWD functions, just a subset (probably to keep the price low). More details in the section “Why Visual Studio Code with ST-Link (instead of nRFgo Studio with J-LINK)” in the article “Coding nRF52 with Rust and Apache Mynewt on Visual Studio Code”.

Yes, Raspberry Pi works for flashing and debugging PineTime, even for removing flash protection. We have a special version of OpenOCD called OpenOCD SPI that talks to PineTimeā€™s SWD port over SPI (without bit-banging). See PineTime Updater

Is there a 3D model of PineTime available somewhere?

Not yet. Someone did design a cover you can snap on to keep the back shut. More details

Are there any alternatives to the wrist band provided with the PineTime?

No, but PineTime accepts standard 20mm wrist band that is widely available by a third party.

Note that some sellers have a different point of view on what standard is. So you should always check the fitting to make sure it looks like the one used by PineTime.

Troubleshooting PineTime’s Bluetooth (InfiniTime firmware)

Old (pre 1.4) InfiniTime firmware had an issue where Bluetooth connectivity sometimes gets lost/fails.

The fix was to soft-reset the watch, by holding the PineTime button for ~8 seconds until the PineCone logo appears.

Does PineTime have an audible beeper or speaker?

No, but in practice the vibration component is a substitute for use with alerts.


Development

Available firmware and projects

Project HomepageProject SourcePineTime Implementations
FreeRTOShttps://www.freertos.orghttps://sourceforge.net/projects/freertos/InfiniTime (GitHub: JF002/Pinetime)+ kaythe/pinetime-os
Zephyrhttps://www.zephyrproject.orghttps://github.com/zephyrproject-rtos/zephyrnajnesnaj/pinetime-zephyr
SuperPrower/pinetime_zephyr_sample_fw
Dejvino/pinetime-hermes-firmware
endian-albin/pinetime-hypnos
omegatime watchface
mynewthttps://mynewt.apache.org/about/https://github.com/apache/mynewt-corelupyuen/pinetime-rust-mynewt(Discontinued by Creator)
caspermeijn/klok
MbedOShttps://os.mbed.comhttps://github.com/ARMmbed/mbed-ossethitow/mbed-pinetime
geoffrey.vl/mbed-pinetime
RIOThttp://riot-os.org/https://github.com/RIOT-OS/RIOT/bosmoment/PineTime-apps
TinyGohttps://tinygo.orghttps://github.com/tinygo-org/tinygoaykevl/go-smartwatch
wasp-os (MicroPython)https://wasp-os.readthedocs.io/https://github.com/daniel-thompson/wasp-osWaspOS PineTime install guide
Rust + RTFMhttps://rtfm.rs/rtfm-rs/cortex-m-rtfmhttps://github.com/dbrgn/pinetime-rtfm/
Bare Metalhttps://github.com/xriss/timecake
https://github.com/Arc13/Pyrus
xriss/timecake
Arc13/Pyrus
AdaFruit bootloaderhttps://github.com/adafruit/Adafruit_nRF52_Bootloaderdaniel-thompson/wasp-bootloader
Useful drivershttps://github.com/sethitow/mbed-pinetimehttps://github.com/sethitow/mbed-pinetime
Project HomepageProject SourcePineTime Implementations
Gadgetbridge (Android companion app)https://gadgetbridge.org/https://codeberg.org/Freeyourgadget/GadgetbridgeInfiniTime support in mainline
Arduinohttps://youtu.be/4aFDjymXjOwhttps://github.com/atc1441/ATCwatchhttps://github.com/atc1441/ATCwatch
OTA Update Flasher / DaFlasherhttps://youtu.be/gUVEz-pxhgghttps://github.com/atc1441/DaFlasherFileshttps://github.com/atc1441/DaFlasherFiles
InfiniTime companion app for Linuxalexr4535/siglo
Wasp-os companion app for LinuxSiroj42/wasp-companion
UI design proposalarteeh/pinetime
Flashing app for Linuxarteeh/pinetime-flasher
Flashing app for WindowsZephyrLabs/PinetimeFlasher
ESP32 / ESP8266 SWD WebFlasherhttps://youtu.be/Iu6RoXRZxOkhttps://github.com/atc1441/ESP32_nRF52_SWD
InfiniTime companion daemon for LinuxElara6331/ITD

Compatibility

Compatibility with other projects

Different firmware running using different bootloaders and Bluetooth stacks on the nRF52832 have different requirements on how they should be initialised and what should be placed where in the internal flash.

To keep track of what, how and why things work like they do across the different projects, check out the PineTime SoftDevice and MCUBoot compatibility article.

Compatibility with companions apps and Bluetooth communication

There are a lot of different firmware running on the Pinetime that implement different BLE APIs (for example for time synchronization and notifications). Companion apps must be able to differentiate between different firmware and forks of the same firmware. See Bluetooth.

Hardware wishlist

This page contains a list of things people wish PineTime did differently

Hardware

  • Other display technology could be explored.
    • E-ink
      • Still images require no power to maintain **[//en.Wikipedia.org/wiki/Transflective_liquid-crystal_display A transflective LCD]
      • Increased readability in bright daylight **[//en.wikipedia.org/wiki/OLED OLED]
      • Self-emissive display (pixels emit their own light)
      • Allows for lower power usage with mostly black screens
      • Allows for low power visual notifications (imagine an always-on small red square in the corner to indicate a notification)
  • Touchscreen with configurable sensitivity
    • Ideal for gloved fingers and water droplet resistance
    • Preferably it should remain capacitive, as a resistive touchscreen would have too many trade-offs.
  • A slightly bigger 256Ɨ256 pixel display
    • This resolution is preferable for its binary alignment for low-level simplicity
    • It has the property that its X and Y coordinates are each addressable with a single byte, with no bounds checking
    • Its total number of pixels is a power of 2 (65536), and each pixel is addressable with exactly 2 bytes.
    • The IBNIZ (Ideally Bare Numeric Impression giZmo) virtual machine, designed for minimalist demoscene graphics, has chosen 256Ɨ256 for its virtual display specifically for code efficiency.
      • If PineTime also chose 256Ɨ256 then it would be a target platform for unclipped IBNIZ demoscene programmes, which would be really fun to play around with on oneā€™s wrist!
  • Full screen refresh is very slow
    • A full 16-bit redraw on the display takes at worst 120ms, which is 8Hz
    • Modest optimization is possible by adopting 12-bit color
    • A smooth scrolling/usage/animation experience would be 30Hz minimum, preferably 60hz
    • Display redraw is currently bottlenecked by the nRF52832 maximum SPI clock (8MHz).
    • The nRF528(33/40) has one high speed SPI master which supports 32MHz, still well below the ST7789 maximum
    • Parallel data transfer could be an option, but using more GPIOs (which donā€™t look available)
  • Some sort of scroll wheel (and possibly button combination) would be nice as an additional input method
  • Changed GPIO assignment so more functionality is available (i.e. NFC and VSYNC)
  • Wireless charging, or Qi Charging capability
  • Different MCU with more RAM and ROM, higher clock
    • nRF5840 update
      • 32MHz HS SPI, QuadSPI
      • CryptoCell + Secure Key Storage
      • More RAM, a coprocessor
      • The possibility to expose USB through power pins
    • Ox64/BL808
      • Open hardware RISC-V based MCU
      • Significant jump in performance
      • Significant jump in memory and storage, allowing for more features and better UIā€™s
    • Possibly a pre-certified MCU module with a ceramic antenna
  • Version without sensors but maybe bigger battery
  • Pins on the programmer connector to allow UART while developing (currently there is a TX test point on PCB). (Note: Thereā€™s ARM SemiHosting, ITM and Segger RTT that fulfil this purpose for most)
  • Connect SDO of ST7889 LCD controller to MCU
    • Allows MCU to execute READ commands
    • Possibility of leveraging ST7889 RAM to save MCU RAM?
  • LCD must be centered on case. Currently is not and watchfaces seems different when clock is put on the other wrist.
  • A NFC antenna around the case, connected to the NFC pins.
  • Used sensors should be NDA-free and preferably also blob-free for easier development
    • Possibly replace BMA421 accelerometer with a magnetometer + gyroscope + accelerometer combination
      • The BMA421 doesnā€™t have a public datasheet
      • Special attention should be paid to advanced features, such as step counting integration or flick detection.
  • PineTime SoC could support USB or have a FTDI chip with the relevant pins exposed
    • It could allow flashing a sealed device, just like Arduinos work.
    • Alternatively, an USB-C port could be added that provides these features.
  • A bigger pulldown resistor for the power button
    • 100k still leaks a noticeable amount of power when the button is always on.
  • Ceramic Bluetooth antenna for better signal reception
  • An external RTC circuit
    • Allows the main MCU go to deep-sleep while retaining time.
    • Allows time retention through MCU reset.
  • Ultra low quiescent current PMIC
    • In theory could provide a hard reset capability based on button press
    • Better deep sleep/shipping/storage/off lifetime
      • A nano-power system timer IC could in theory provide a RTC, MOSFET-controlled deep sleep, watchdog timer and button-controlled reset
    • Built-in “fuel gauge” for better estimation of battery capacity
  • Improved haptic or audible feedback
    • E.g. small Piezo buzzer
    • Use case would be for very short beeps (think old-school casio watch) as notification.
    • Of course developers can PWM other frequency to make it sing, but piezos tend to be shrill.
  • A built-in microphone
    • Would allow phone call functionality to be built into the watch.
    • Could potentially allow for speech recognition for text input.
    • Direct access to the external (flash) storage
    • Only a small jump in price

Reprogramming

Wire it up and then flash something.

Manuals

Write battery-friendly software

The key to save battery is to enable only what you need when you need it. nRF52832 has a lot of functionalities allowing you to draw as little current as possible. Here are some tips and tricks:

  • Disable / shutdown / put in sleep mode all devices around the MCU (display controller, touch controller, external memory).
  • Disable all peripheral inside the MCU when you donā€™t need them (SPI, TWI(IĀ²C)). The power management of the NRF52832 is very smart and will completely shut down (power off and disable the clock) the peripheral when the software disables it.
  • Put the MCU to sleep as soon and as often as possible. If you are not using a RTOS, this is done by calling WFE (wait for event) instruction. Most of the time, RTOS implement this functionality. For example, FreeRTOS calls it the tickless mode: it puts the CPU in sleep mode when no task is planned for execution for more than a specified time, and wakes up as soon as an event is detected or when a task is ready to run.
  • Do not use logging (JLink RTT, SWO, semihosting), it uses a lot of power.
  • Ensure that the debug circuitry of the MCU is not enabled when you measuring the battery life. The debug peripheral is enabled as soon as you connect a debugger to the device, and is not automatically disabled, even if you disconnect the debugger you will have to wait for the battery to go flat to disable to port. The software running in the NRF52832 cannot disable the debug peripheral. To disable the debug circuitry:
    • using nrfjprog --reset
    • using JLinkExe: issue the command writeDP 1 0
    • or with OpenOCD:
      • issue the command halt
      • issue the command flash fillw 0x10001208 0xFFFFFF00 0x01
      • issue the command reset
  • You can check if the debug port is enabled using the following code:
DWT->CYCCNT ? "NO":"YES"

Discussions

Bootloader improvements

Discussion by the user JF:

Request for comments

In this page, I explain the changes and improvements I would like to implement to the MCUBoot bootloader to make it more reliable, and to allow bootloader upgrade and switch (mcuboot <-> wasp-bootloader, for example). I try to explain why I made these choices too.

Do not hesitate to edit this page and add a new section if you want to add any comment to these points (if you agree, disagree, want to try something else).

Introduction

This pages describes the improvements I propose to make to the bootloader and OTA to make the whole process more reliable.

My goals are:

  • KISS: Keep It Stupid Simple. Simple code contains less bugs than complicated/complex code.
  • As reliable as possible. 100% reliability is not feasible with the current hardware (no physical reset button, no bootloader in ROM code), but letā€™s try to reach 99.9% reliability
  • Provide a read-only factory image weā€™ll be able to restore in case of unrecoverable errors during OTA
  • Provide a way to upgrade the bootloader
  • Provide a way to switch from MCUBoot to NRF bootloaders

Current memory map

Pinetime Devkit0 is using a nRF52832-QFAA MCU with 512kB flash and a XT25F32B SPI NOR external flash with 4096kB.

Internal flash (512Kb):

  • Page size is 4Kb.
  • Bootloader contains the MCUBoot bootloader.
  • Boot log is currently not used. Can be use to provide reboot logs to the application
  • App is the PRIMARY SLOT for MCUBoot: the firmware that is currently running.
  • Scratch is used by MCUBoot as temporary storage for swap operation

External flash (4096Kb):

  • Page size is 256 bytes.
  • Bootloader assets: boot logo
  • OTA: SECONDARY SLOT for MCUBoot. During OTA, this is where the new version of the firmware is stored.
  • FS: space available for the application firmware.

This memory map allows a strong boundary between the bootloader and the application :

  • The bootloader is ran by the MCU at reset time and loads its boot logo from the SPI flash
  • The bootloader (MCUBoot) swaps images from primary and secondary slots if necessary
  • The bootloader starts the watchdog
  • The bootloader jumps to 0x8000 to run the application

The application donā€™t know anything about the bootloader except that:

  • it must be linked @0x8000
  • it must refresh the watchdog periodically
  • it must store the image for OTA at a specific location in the external SPI flash memory

In this configuration, the boot logo is part of the bootloader, not of the application: the same logo will be displayed whatever application firmware will be loaded.

Changes

Factory image

The factory image is a ’light’ build of InfiniTime that contains the bare minimum code to provide OTA to the user: basic UI, BLE and OTA. No apps, no fancy functionalities.

This factory image will be stored in the “bootloader assets” area of the external flash memory.

This factory image will be restored either automatically, when the bootloader detect it cannot boot from primary slot (App in internal memory) and secondary slot (OTA from exeternal). The user can also request to restore this factory image if necessary (using the button?).

Bootloader assets

Graphics can be compressed using a simple RLE encoding. RLE encoding on 1 bits (2 colors images) is very efficient (115200KB -> 1-5KB).

In order to simplify the code, the graphics will be stored into the bootloader code instead of storing them into the external flash:

  • Less code.
  • Easier to document: 1 specific logo = bootloader mode
  • Do not have to worry about the dynamic size of the logo (the binary size of the graphics will vary with the content when they are RLE encoded).

Bootloader

  • Display the bootloader version on the screen and expose the version to the application firmware
  • Provide a way to revert the firmware to the last working version AND to the factory firmware
  • Display status and progression (progress bar similar to wasp-reloader, color code)
  • Test as much as possible

Bootloader workflow

Upgrade & Reloader

The reloader is a tool that allows to upgrade the current version of the bootloader AND to switch from MCUBoot to NRF and vice versa.

How does it work?

  • Upgrade
    • The reloader is first OTAed like any firmware upgrade.
    • When the system resets, MCUBoot swaps the application firmware with the reloader. The reloader upgrade the current bootloader and resets the system. It does NOT flag the image as validated. It resets the MCU.
    • The new version of MCUBoot notice that the last upgrade is not validated and reverts to the firmware that was running just before.
    • VoilĆ , youā€™re running your firmware and a new version of the bootloader
  • Switch bootloader
    • The reloader is first OTAed like any firmware upgrade.
    • When the system resets, MCUBoot swaps the application firmware with the reloader. The reloader overwrite the current bootloader with a new one and reset.
    • The new bootloader is running.
  • Switch
    • From InfiniTime to wasp-os: the reloader contains the NRF Bootloader and Softdevice. This bootloader provides the OTA mecanism out of the box. Wasp-os is downloaded when the NRF bootloader is running
    • From wasp-os to InfiniTime: the reloader contains the factory image (infinitime-factory). The complete version of InfiniTime will be OTAed when this factory image is running.

Discussions

Boot Logo: embedded into the bootloader binary vs stored in the external SPI flash

Embedding (and compressing) the boot logo inside the bootloader binary brings many advantages:

  • All the data are available in memory at runtime. No need to load them & check them, and no need to handle errors and invalid corrupted data.
  • The data is available and can be sent directly to the display controller
  • 1 unique logo for the bootloader: easier to document and explain to the user that this specific logo is the logo from the bootloader mode.

But it also has some disadvantages:

  • 1-Bit RLE encoding (very effective compression) allows only 2 colors (background/foreground)
  • The boot logo cannot be customized (unless you recompile and flash this new build of the bootloader)
  • The size of the boot logo is limited (depending on the compression ratio)

My (JF) point is that the bootloader must be as reliable as possible. I would like to remove all part of the code than can fail. If we read the boot logo from the SPI flash, we will write something like this:

 int ret;
boot_logo_info info;
ret = SpiNor_read(infoOffset, &info, sizeof(boot_logo_info));
if(ret != 0) {
  // Something went wrong while reading image info
  panic(); // ? reset ?
}

if(check_boot_logo_info(info) == false) {
  // image info are invalid (ex: size > 240*240), we cannot use them
  panic(); // ? reset ? Display nothing?
}

We could find invalid image info if a firmware did not respect the memory map and erased/overwrote the external memory map. In this case, the bootloader couldnā€™t run properly. Of course, we can implement something smart in panic() (retry, use failover values), but again, this adds complexity and bug probability.

All these ifā€™s that call panic() can be avoided by using hard-coded values at build-time.

If the image is hard-coded, you wonā€™t be able to easily (not that easy, actually) customize the boot logo. But remember that this logo is only display for a short time only when the device reset (manually or during an OTA).

Why not add OTA functionality to the bootloader?

This is exactly how the NRF bootloader/SoftDevice works: the bootloader is a standalone firmware that provides OTA functionality thanks to the SoftDevice. The downside is that the BLE stack needed to provide OTA is quite big and uses a lot of space in flash memory (~124kB according to the documentation). This is roughly 1/4 of the available space in the internal flash memory.

Firmware based on the NRF SoftDevice share the BLE stack with the bootloader, it is mutualised between both entities. The downside of this design is that firmwares developers are somewhat forced to use the NRF BLE stack. If they want to integrate another BLE stacak (NimBLE for example), these 120kB used by the SoftDevice would be wasted.

Thatā€™s why we decided to make the MCUBoot bootloader a simple bootloader without OTA functionality. Itā€™s very lighweight (less than 20kB) and leaves the developers the right to choose the BLE stack they want.

Fixed vs dynamic memory map

A dynamic memory map, using a partition stored in a fixed place (at the beginning of the external flash, for example) would allow different firmware to customize the partition table, image sizes and use the memory for efficiently.

But it has the downside to add complexity and code that could fail.

See External flash partitioning proposal.


Further information

Datasheets, schematics and certifications

Schematics

note

The part number for the SPI FLASH in the schematic diagram is not correct, the PineTime features a larger external FLASH device, see below.

Chip Datasheets

NORDIC nRF52832 information:

ARMv7-M information:

Component Datasheets

PMU (Power Management Unit) information:

SPI Flash information:

LCD Panel:

Touchpad information:

Sensor:

Certificates

Manuals

Hardware

Display

Note: The factory-default software on the PineTime does not auto-detect the display being disconnected when it has already booted. That can cause garbled output, to fix it just restart the PineTime.

The display is driven using the ST7789 display controller. Use the following pins to drive the screen:

PineTime pinST7789 pin

LCD_SCK (P0.02)

SPI clock

LCD_SDI (P0.03)

SPI MOSI

LCD_RS (P0.18)

Command/Data pin (CD)

LCD_CS (P0.25)

Chip select

LCD_RESET (P0.26)

Display reset

LCD_BACKLIGHT_{LOW,MID,HIGH}

Backlight (active low)

Notes:

  • Chip select must be held low while driving the display. It must be high when using other SPI devices on the same bus (such as external flash storage) so that the display controller wonā€™t respond to the wrong commands.

  • SPI must be used in mode 3. Mode 0 (the default) wonā€™t work.

  • LCD_BACKLIGHT_* is used to enable the backlight. Set at least one to low to see anything on the screen.

  • Use SPI at 8MHz (the fastest clock available on the nRF52832) because otherwise refreshing will be super slow.

References:

Battery measurement

Reading whether the PineTime has power attached is easy: simply read the charge indication pin (P0.12). When it is high it is running on battery, when it is low it is charging.

Reading the battery voltage is a bit harder. For that you can use the battery voltage pin on P0.31 (AIN7). The returned value is 12 bits, which means it is 0..4095. You can get the measured voltage with the following formula, assuming a reference voltage of 3.3V (this is configurable in the ADC):

adcVoltage = adcValue / (4095 / 3.3)

The measured voltage is actually half of the actual battery voltage, because the ADC is connected between a voltage divider where both resistors are 1MĪ©. This can be corrected by multiplying the value:

batteryVoltage = adcValue * 2 / (4095 / 3.3)

Itā€™s often better to avoid floating point values on embedded systems and in this case there is no reason to use float at all, we can just represent the value in millivolts. Therefore the formula can be simplified to:

batteryVoltage = adcValue * 2000 / (4095 / 3.3)
batteryVoltage = adcValue * 2000 / 1241

Converting this voltage to an estimated capacity in percent requires a more complicated algorithm, because Lithium-ion batteries have a non-linear discharge curve.

Button

The button on the side of the PineTime is disabled by default. To enable it, drive the button out pin (P0.15) high.

While enabled, the button in pin (P0.13) will be high when the button is pressed, and low when it is not pressed.

note

the button consumes around 34ĀµA when P0.15 is left high. To reduce current consumption, set it to low most of the time and only set it to high shortly before reading it. The button needs a short time to give good outputs though, setting P0.15 high at least four times in a row seems to result in enough delay that P0.13 has a stable output.

Touch panel

The touch panel is controlled by a Hynitron CST816S chips. Unfortunately, there is not much information about this chip on the internet apart from the datasheet below and a reference driver. This is enough to implement a basic driver, but crucial information needed to implement advanced functionalities are missing (IĀ²C protocol and registers, timings, power modes).

Pins

  • P0.10=== What do you need to know?

If you followed Dorianā€™s guide to get here and felt semi-comfortable, youā€™ll be fine. This is no more complicated than that. If you are intimidated, thatā€™s okay! Iā€™ll still encourage you to try. You will learn a lot, just be patient and donā€™t put any data on your PineNote that you wouldnā€™t be okay losing. If you run into trouble, ask for help in the Discord / Matrix. Please try to solve problems on your own first, and then ask for helpā€‰ā€”ā€‰if nobody replies, please be patient and ask again soon.

Reset

  • P0.28: Interrupt (signal to the CPU when a touch event is detected)

  • P0.06: IĀ²C SDA

  • P0.07: IĀ²C SCL

IĀ²C

  • Device address: 0x15

  • Frequency: from 10Khz to 400Khz

note

The controller goes to sleep when no event is detected. In sleep mode, the controller does not communicate on the IĀ²C bus (it appears disconnected). So, for the communication to work, you need to tap on the screen so that the chip wakes-up.

note

The IĀ²C bus, also known as TWI bus has known issues, make sure to write your TWI driver with timeouts.

Touch events

Touch information is available from the 63 first registers of the controller. Remember: the device is in sleep mode when no touch event is detected. It means that you can read the register only when the touch controller detected an event. You can use the Interrupt pin to detect such event in the software.

These 63 bytes contain up to 10 touch point (X, Y, event type, pressure,ā€¦ā€‹) :

ByteBit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0

0

?

1

GestureID: (Gesture code ,

0x00: no gesture,

0x01: Slide down,

0x02: Slide up,

0x03: Slide left,

0x04: Slide right,

0x05: Single click,

0x0B: Double click,

0x0C: Long press)

2

?

Number of touch points

3

Event (0 = Down, 1 = Up, 2 = Contact)

?

X (MSB) coordinate

4

X (LSB) coordinate

5

?

Touch ID

Y (MSB) coordinate

6

Y (LSB) coordinate

7

Pressure (?)

8

Miscellaneous (?)

Bytes 3 to 8 are repeated 10 times (10*6 + 3 = 63 bytes).

NOTES

  • The touch controller seems to report only 1 touch point

  • Fields X, Y, Number of touch points and touch ID are updated. The others are always 0.

Registers

The reference driver specifies some registers and value, but there is no information about them:

RegisterAddressDescription

HYN_REG_INT_CNT

0x8F

HYN_REG_FLOW_WORK_CNT

0x91

HYN_REG_WORKMODE

0x00

0 = WORK, 0x40 = FACTORY

HYN_REG_CHIP_ID

0xA3

HYN_REG_CHIP_ID2

0x9F

HYN_REG_POWER_MODE

0xA5

0x03 = SLEEP (reset the touchpanel using the reset pin before using this register: pin_low, delay 5ms, pin_high, delay 50ms then write 3 to register 0xA5)

HYN_REG_FW_VER

0xA6

HYN_REG_VENDOR_ID

0xA8

HYN_REG_LCD_BUSY_NUM

0xAB

HYN_REG_FACE_DEC_MODE_EN

0xB0

HYN_REG_GLOVE_MODE_EN

0xC0

HYN_REG_COVER_MODE_EN

0xC1

HYN_REG_CHARGER_MODE_EN

0x8B

HYN_REG_GESTURE_EN

0xD0

HYN_REG_GESTURE_OUTPUT_ADDRESS

0xD3

HYN_REG_ESD_SATURATE 0xED

0xED

Accelerometer

The on board accelerometer in devices shipped before July 2021 is a Bosch BMA421, connected to the I2C bus. Devices shipped after July 2021 use a Bosch BMA425 accelerometer.

Pins

  • P0.06: IĀ²C SDA

  • P0.07: IĀ²C SCL

  • P0.08: Interrupt

IĀ²C Device address: 0x18

Reducing power consumption

The PineTime appears to be able to sleep with a current consumption of only 66ĀµA.

To investigate current consumption, itā€™s a good idea to disable everything possible to get the lowest current consumption possible, and then re-enable things one by one. Here is one way to get a baseline current consumption of 0.60ĀµA, as measured from the 3.3V pin with the battery disconnected:

  • Enable the DC/DC regulator. This doesnā€™t affect the current consumption while sleeping, but almost halves the runtime current consumption.

  • Use the low-frequency (32.768kHz) oscillator.

  • Leave all pins in their default state, except for P0.05 (SPI CS) and P0.25 (LCD CS) which should be configured as an output and set to high.

  • Put the heart rate sensor in sleep mode by setting the PDRIVER (0x0C) register to 0, see the HRS3300 datasheet for details.

  • Put the SPI flash in deep power-down mode by setting flash CS high, then low, then writing the byte 0xb9 on the SPI bus, and then setting flash CS high again.

  • Sleep in a loop, using WFE or WFI (if youā€™re using the Nordic SoftDevice, call sd_app_evt_wait instead).

Here are some current consumption statistics (current consumed in addition to the baseline power), roughly ordered from large to small:

SourceCurrentNotes

SWD

3.05mA

Power cycle the chip after programming to avoid this, it can hide other inefficiencies.

LCD

5.61mA

Set the LCD to sleep mode when not used, using SLPIN.

Backlight high

12.27mA

Backlight mid

5.51mA

Backlight low

1.83mA

ADC left enabled

1.3mA

Stopping SAADC brings the current back to the baseline. It seems that it doesnā€™t need to be disabled entirely.

Edge triggered pin interrupts

0-0.47mA?

It appears that under some configurations, edge triggered interrupts result in a large power drain. One way to avoid this is by using the pin sense mechanism.

BUTTON_OUT left high

0.04mA

See Button for how to avoid this.

SPI flash sleep mode

0.014mA

Sleep mode still consumes power. Put it in deep power down mode to avoid this.

SPI, I2C

(negligible)

SPI and I2C appear to consume very little power when idle, around 1ĀµA or less.

PineTime Devkit Wiring

This article will help you get up to scratch about how to connect your PineTime to your hardware debugger and what to keep in mind.

The devkit comes with a set of wires you can use for connecting your programmer to the SWD pins. Most people use friction to make contact with the programming cable. Soldering the wires to the pinetime is not recommended, especially if you donā€™t have a temperature-controlled iron and good confidence that you can do it - the thin PCB is fragile and easy to break.

Current amount of dead PineTimes (or ruined bundled programming connectors) due to attempted soldering is 5 (update this number when suitable).

Read this about the battery

You have three choices:

a) If you have a soldering iron and youā€™re confident with using it, it is recommended that you remove the battery until you actually need it. Doing so avoids unnecessary charge cycles and strain on it. It can also potentially prevent issues with your watch not resetting properly or backfeeding power into your debugger-programmer. Thereā€™s also the option that you just connect a microswitch between the batteryā€™s positive side and the PineTime, just make sure to isolate your connections so it doesnā€™t short out against anything.

b) If you do not have a soldering iron or youā€™re not confident with using it, donā€™t disconnect the battery if you ever plan on using it. Donā€™t bend the wires too much as theyā€™re thin, you wonā€™t be able to reconnect it. Keep in mind that keeping it connected during development will probably reduce the lifetime of the battery. Small load on the 3.3V pin is probably fine, but it will drain the battery empty. Having the battery connected when itā€™s not empty will also very likely backfeed power into your 3.3V pin - Donā€™t cause short circuits! Donā€™t leave the wire dangling!

c) If you donā€™t disconnect the battery, also donā€™t connect the 3.3V pin from the SWD cable when debugging or updating firmware. You only need to use the GND, SWDIO and SWDCLK pins. In that case the watch will run exclusively on battery, but there is no danger of backfeeding power.

SWD Pinout

The devkits have exposed SWD pins for flashing and debugging.

The pinout is:

Pogo Pins connection

The PineTime Pogo Pins are spring-loaded pins with diamond-shaped tips.

The Pogo Pins are meant to be connected temporarily to PineTimeā€™s SWD port for firmware flashing and simple firmware debugging.

The other end of the Pogo Pins connects to ST-Link v2 or JLink for flashing and debugging. (ST-Link v2 is shown in the background)

To connect PineTime Pogo Pins to PineTimeā€™s SWD Port:

  1. To orientate the pins, stick a piece of Sticky Tape to the Pogo Pins as shown above. The Sticky Tape should point away from PineTimeā€™s Battery. Orientation is important. You may damage PineTime with the incorrect orientation!
  2. With the Battery at left and Sticky Tape pointing right, the SWD Pins will be arranged left to right as: SWDIO, SWDCLK, 3.3V, GND
  3. Connect the other end of the Pogo Pins to the Jumper Cable thatā€™s bundled with PineTime. Connect the Jumper Cable to ST-Link v2 or JLink: SWDIO, SWDCLK, 3.3V, GND. See Reprogramming the PineTime
  4. With the Sticky Tape pointing right (away from the Battery), tap and hold the Pogo Pins firmly on PineTimeā€™s SWD Port. But not too hard because the PCB or screen may break. Stabilise the Pogo Pins with your pinky finger as shown above.
  5. PineTime should light up and reboot when the Pogo Pins are connected. You may flash and debug PineTime now. See Reprogramming the PineTime

The tips of the Pogo Pins will partially penetrate the SWD holes like this. Donā€™t force them in!

Soldered wires example

Before attempting this, make sure you have a good soldering iron, some magnification and you havenā€™t set your iron too high. If you havenā€™t ever before soldered things this small, you really really do not want to start out on something this dense, small and thus fragile.

Raspberry Pi connection

See PineTime Updater

PineTime Equivalents

This page contains a list of PineTime hardware equivalents or very close clones. Primarily to keep track of which devices could possibly be “jailbroken” and run PineTime firmware.

Please add devices that use the same chipset to this list.

Kalakate

Seems to be PineTime hardware, interesting GUI, seems DaFit-y

Colmi P8

A few firmware already support both the P8 and PineTime, the physical differences are minor - only a few MCU pins are mapped differently. There are a few open issues on InfiniTimeā€™s GitHub page to add support for this device.

Specifications

  • Dimensions: 37.5 x 40 x 11mm
  • Weight: 38 grams
  • IP Rating: IP67 (waterproof to 1 meter (sealed edition))
  • Display:
    • Size: 1.3 inches (33mm) diagonal
    • Type: IPS capacitive touchscreen, RGB 65K colors
    • Display Controller: ST7789
    • Resolution: 240x240 pixels
  • System on Chip: Nordic Semiconductor nRF52832
  • Flash: 512KB with additional 4MB SPI NOR
  • RAM: 64KB
  • Bluetooth: 5.0 (including Bluetooth Low Energy)
  • Sensors: Accelerometer, Heart rate sensor
  • Feedback: Vibration motor
  • Battery: 170-180mAh 3.8V LiPo