Skip to content

Latest commit

 

History

History
702 lines (459 loc) · 27.6 KB

README.md

File metadata and controls

702 lines (459 loc) · 27.6 KB

CHRZ Watch Firmware

Custom firmware for the NRF52 based smartwatch I6HRC using the ARM Mbed RTOS. Supported by a custom version of the Gadgetbridge Android app.

Photos

Wrist

Watch

Heartrate

Speed

Steps

Distance

Settings

System

Android Screenshots

General

Activity

Activity2

Settings

Sleep

State of the project

Bluetooth
  • Complete BLE Stack
  • Battery GATT profile
  • Heartrate GATT profile
  • Current time GATT profile
  • Steps & cadence GATT profile
  • Immediate alert GATT profile
  • Text message GATT profile
  • Health activity / sleep monitor GATT profile
  • User configuration GATT profile
  • Proper bonding support
Hardware interfacing
  • LC Display
  • Vibration
  • Charging detection
  • Capacitive touch buttons
  • Battery voltage sensor
  • Acceleration sensor
  • Heartrate sensor
Power saving
  • Deep sleep in idle thread
  • Energy saving display
  • Energy saving touch input
  • Energy saving heartrate sensor
  • Energy saving acceleration sensor
  • Endurance tests & verification
  • Power saving BLE stack
  • Use even better BLE stack (Requires ARMmbed/mbed-os#10669)
Other
  • Timekeeping
  • Graceful reboot on error
  • Step detection algorithm
  • Wake-up on wrist turn
  • Heartrate detection algorithm
  • User configurable settings
  • High-speed LCD drawing
  • Sleep / rest detection algorithm
  • Basic UI
  • Fancy UI
  • Optional SEGGER J-Link RTT serial interface
  • Expose flash for external interfacing
Android app

Source code: https://codeberg.org/StarGate01/Gadgetbridge/src/branch/chrzwatch

  • Basic device support
  • Time synchronization
  • Sample database
  • Heartrate logging
  • Step logging
  • Sleep state logging
  • Immediate alert on messages
  • User settings editor

Hardware

The I6HRC smartwatch uses these components. You can also purchase then separately (on breakout boards) and connect them to a NRF52-DK.

CPU: NRF52832 with 512K ROM, 64K RAM, supports deep sleep
Display: 0.96 inch, 80x160 pixel LCD with ST7735 driver IC
Acceleration sensor: KX023
Heart rate sensor: AFE4404
Font Flash: GT24L24A2Y, 2MB storage, top 4KB user writeable
Ambient light sensor: Some photodiode combined with a LED
  • Driver: N/A, currently unused
Battery voltage sensor: Down-scaled battery voltage (ca. 0.34V - 0.4V)
  • Driver: Mbed ADC
Vibration motor: Small axial vibration motor
  • Driver: Mbed GPIO
Buttons: Two capacitive buttons below display
  • Driver: Mbed GPIO Interrupts
Charging detection: Down-scaled charging voltage (0V | 5V)
  • Driver: Mbed GPIO

This project configures the NRF52832 IC and the Mbed OS as follows.

SPI and I2C interfaces

The NRF52832 IC has 2 I2C and 3 SPI instances, which partly overlap. To avoid contention, the interfaces are assigned as follows:

Peripheral / Instance Pin Mapping Instance 0 Instance 1 Instance 2
LC Display 15, NC, 14 SPI2
Acceleration Sensor 3, 2 TWI0
Heartrate Sensor 20, 21 TWI1
Font Flash 8, 5, 7 SPI2

The reason for this particular configuration is a follows: Both the acceleration and heartrate sensor need to be able to access the I2C bus upon receiving an interrupt signal (to read a motion event upon occurrence and to read the ADC when it signals readiness, respectively). This means UnsafeI2C, - an I2C implementation without bus access locking - has to be used, because the mutex used for locking cant be used in an interrupt context.

Because these I2C access events may occur at any moment, a dedicated instance is allocated to each of them. The remaining instance does only support SPI anyways. To ensure no contention occurs between the LCD and the flash, the standard bus locking SPI functions are used. Due to this locking, parallel access to the LCD and font flash is impossible, but also not needed by the app logic. Locking also means that all functions using these devices cant be called from an interrupt context. Specifically, this concerns the Screen::render function. This function is intended to never be called directly, instead DisplayService::render has to be called, which defers the rendering call via the main EventQueue to the main thread.

At the time of writing, the font flash is unused. The interface mapping still stands, for future-proofing.

GPIO pins and interrupt memory

The NRF52832 IC has a few special functions which can be assigned to specific pins, eg. NFC and Reset. To use these pins as standard GPIO pins, the following macros are configured in mbed_app.json, in order to configure how Mbed is built: CONFIG_GPIO_AS_PINRESET is removed (enables p0.21 as GPIO) and CONFIG_NFCT_PINS_AS_GPIOS is added (enables p0.9 and p0.10 as GPIO). Also, the ITM debug trace module, which uses the SWO pin, is removed (enables p0.18 as GPIO). The ITM module is already removed in the board configuration this project inherits from (NRF52_DK).

To support more than one interrupt event on the GPIO pins (using the GPIOTE peripheral), more memory is allocated to the peripheral by defining NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS=8. This supports a maximum of 8 separate interrupt events.

To conserve space, the ITM, SERIAL, SERIAL_ASYNC and SERIAL_FC devices are removed, as the serial connection is unavailable anyway (for more info, see below).

Mbed configuration

No proprietary softdevice by Nordic is used. Instead, Mbed supplies APIs like Bluetooth CORDIO (https://os.mbed.com/docs/mbed-cordio/19.02/introduction/index.html). This enables the usage of all software interrupts, by removing the SWI_DISABLE0 macro in mbed_app.json.

The Bluetooth API is modified in the custom Mbed OS fork, to enable deep sleep and reduce power usage. This modification might lead to some problems, but none have occurred so far.

Mbed also provides various RTOS features, e.g. Threads. (Enabled by defining PIO_FRAMEWORK_MBED_RTOS_PRESENT in platformio.ini)

Tho conserve energy, low-power timers are used. These are enabled by setting "events.use-lowpower-timer-ticker": true in mbed_app.json. The clock source for the low power clock is defined to be the external crystal oscillator by setting "target.lf_clock_src": "NRF_LF_SRC_XTAL".

Crash behavior

The system is configured to automatically reboot when a hard fault occurs ("platform.fatal-error-auto-reboot-enabled": true,). Although the number of these subsequent reboots is configured to be 3 ("platform.error-reboot-max": 3), this counter is set to 0 on each boot (mbed_reset_reboot_count()). This leads to an unlimited amount of reboots.

The crash dump retention feature of Mbed is enabled, and on the next boot after a crash the stored error information is printed out (in mbed_error_reboot_callback). The storage of this crash information requires a seperate memory region (.crash_data_ram), which is configured in the custom linker script patch/mbed/linker.ld.

If you have a RTT reader attached and compiled with RTT support, you can receive the message printed at reboot.

Development setup

It is recommended to use Linux, however Windows should work as well, provided all the command line tools, compilers and interface drivers are installed.

Modifying an I6HRC watch

Disassemble the watch and solder the SWCLK and SWDIO testpoints to the unused inner two USB data lines like shown in this video: https://www.youtube.com/watch?v=0Fu-VSuKHEg . Verify that the PCB actually contains a NRF52832 IC, and not an off-brand HS6620D IC.

You might want to fabricate a custom SWD via USB to ISP adapter, link shown in the schematic below (KiCAD schematic is available in doc/schematics/swd-usb-adapter). You can still charge the watch using any USB A compliant charger or port.

Dumping the stock firmware

The watch does not use read-back-protection. If it ever does, you might be able to use dirty hacks like https://github.com/StarGate01/nRF51822-siphon (untested, also probably incompatible).

Using a J-Link

To dump the stock ROM and RAM, use any J-Link adapter and the official Nordic dev tools (nrfjprog). Then, disassemble the watch to expose the internal testpoints. Connect the VDD, SWDIO and SWCLK testpoints and also the ground terminal. Then, dump the data like this:

$ nrfjprog -f nrf52 --readcode i6hrc_code.bin

You can also dump the contents of the RAM:

$ nrfjprog -f nrf52 --readram i6hrc_ram.bin
Using a CMSIS-DAP

For more info on the CMSIS-DAP, see below.

OpenOCD is able to to read the flash data as well, e.g. using a CMSIS-DAP adapter:

$ openocd -f interface/cmsis-dap.cfg -c "transport select swd" -f target/nrf52.cfg -c "init; reset halt; flash read_bank 0 i6hrc_code.bin; reset; exit"

Using other adapters should work as well, but is untested.

The original firmware dumps can also be found at doc/dump (armv7le) for decompiling (e.g. using Ghidra: https://ghidra-sre.org/) or restoring the watch (untested).

Unlocking the flash memory

The flash memory has to be reset in oder for the chip to accept new firmware and debug instructions. This requires lower level SWD access (SWD), and thus cant be performed by the cheap ST-Link V2 clones because these only provide high level access (SWD_HLA).

Using a J-Link

Buy a J-Link adapter and connect it like in the paragraph above. This is annoying, since the internal VDD testpoint has to be tapped, requiring the watch to be disassembled. Hence, one of the other methods is recommended.

$ nrfjprog -f nrf52 --eraseall
Using a CMSIS-DAP

Either buy any CMSIS-DAP capable adapter, or buy a cheap ST-Link V2 clone (the little USB adapter one), and open it. Then use any SWD high level capable programmer, like for example another ST-Link V2 to reprogram the first one with the firmware for CMSIS-DAP functionality (https://raw.githubusercontent.com/x893/CMSIS-DAP/master/Firmware/STM32/hex/CMSIS-DAP-STLINK21.hex). You can use any ST-compatible flash tool, like for example the ST-Link utility or OpenOCD.

Then, connect the watch to the CMSIS-DAP, and the adapter to your PC via USB. Then connect to it using OpenOCD.

$ openocd -f interface/cmsis-dap.cfg -c "transport select swd" -f target/nrf52.cfg -c "init; reset halt; nrf5 mass_erase; reset; exit"
Using a Black Magic Probe

Buy or assemble - Bluepill boards (https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill.html) are available for a few bucks - a Black Magic Probe (https://github.com/blacksphere/blackmagic/wiki). Connect the SWD interface to the watch, and the probe via USB to your PC. Then connect to the probe using GDB.

$ arm-none-eabi-gdb
(gdb) target extended-remote /dev/ttyACM0
(gdb) set non-stop on 
(gdb) mon swdp_scan
(gdb) mon erase_mass

Uploading new firmware

Flashing the unlocked chip should then work with any basic SWD capable programmer, like the ST-Link V2 (the cheap clones work too). The CMSIS-DAP or the Black Magic Probe can be used as well.

You can use OpenOCD to connect to the chip and manage its flash memory ("..." denote subsequent commands or arguments):

High level SWD access using a ST-Link V2 clone
$ openocd -f interface/stlink-v2.cfg -c "transport select hla_swd" -f target/nrf52.cfg -c "init; reset halt; ...; reset; exit"
Lower level SWD access using a CMSIS-DAP (recommended)
$ openocd -f interface/cmsis-dap.cfg -c "transport select swd" -f target/nrf52.cfg -c "init; reset halt; ...; reset; exit"
Lower level SWD access using a J-Link (not recommended)
$ nrfjprog -f nrf52 ...

The J-Link tools are able to communicate with the CMIS-DAP interface as well.

The PlatformIO IDE is set up to use OpenOCD via some hardware adapter (default: CMSIS-DAP) to program the chip. This can be changed in the platformio.ini file. For the watch, use the i6hrc configuration.

Debugging

The PlatformIO IDE is able to use either a CMSIS-DAP or J-Link adapter for interactive debugging. The i6hrc_debug and nrf52_dk_debug configurations provide debugging support. Do note that the watchdog timer might kill your debugging session. The host may use the J-Link debugger software (which is able to connect to a CMSIS-DAP as well) or the CMSIS_DAP debugger software by PlatformIO (default).

Since the RX, TX and SWO pins are used for other peripherals, UART and SWO communication is not possible. The firmware thus implements a SEGGER J-Link RTT interface (https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/), which communicates via the SWD debug interface. To access this interface, either the SEGGER RTT tools or J-Link tools are required. Use JLinkRTTViewer or the other tools to open a socket, then connect to it via a terminal at socket://localhost:19021 (default port).

$ JLinkExe -autoconnect 1 -device NRF52832_XXAA -if SWD -speed 4000 -RTTTelnetPort 19021 &
$ telnet localhost 19021

Add the compiler definition SEGGER_RTT to enable this feature. The task "Start Monitor Server" automates the connection.

Troubleshooting

Unfortunately, the SWD RESET line is not easily accessible. However, shorting SWDCLK to VCC triggers a debug init halt as well.

If the watch refuses to flash, hangs in low power mode or is stuck in a bootloop, try connecting to it using OpenOCD while spamming the reset button on your adapter - hoping the debugger attaches before the error state occures. This should not be needed during normal flashing and execution. Sometimes, this reset condition occurs randomly when the SWDCLK pin is left floating due to EMI and long wires. This is also the reason for the pulldown resistor in the schematic above.

On a successful connection OpenOCD displays something like "Info : nrf52.cpu: hardware has 6 breakpoints, 4 watchpoints". This means the chip was reset, caught and is now in debug mode.

The script tools/reset.sh or the task "Reset Target" automates this spamming, waiting for you to press the reset button.

Optionally, append -c "reset halt" to the OpenOCD command. The chip then halts at the first instruction, which may be good for debugging.

Accessing the Font ROM (Work in progress)

The font rom is internally connected to the main CPU via SPI and implements a fairly standard flash command interface.

Unfortunately, neither the NRF52832 nor the flash used support or use the QSPI interface, which would allow mapping the flash address range and accessing it via the MMU. This would enable remote reading and writing of the flash via the SWD connection.

It is possible to write a RamCode (https://wiki.segger.com/Programming_External_SPI_Flashes) using the SEGGER Open Flashloader framework (https://wiki.segger.com/Open_Flashloader). Then, a tool like J-Flash would be able to access the external flash via the RamCode on the CPU. This is however quite trick to do. Instead, the OpenOCD "on chip flash loader" protocol by Pavel Chromy (http://openocd.org/doc/html/Flash-Commands.html) could be implemented.

Some python scripts are used to communicate with the chip using the RTT interface. However, a proper SPI flash driver is still missing and has yet to be fully implemented (#1).

Connecting to a phone

A fork of the Android app Gadgetbridge (https://gadgetbridge.org/) with support for this firmware is available at Codeberg: https://codeberg.org/StarGate01/Gadgetbridge/src/branch/chrzwatch .

You can use the Android app "nRF Connect" (https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp) to test the Bluetooth functions. Sometimes, connecting takes multiple tries.

Various other apps like e.g. FitoTrack (https://play.google.com/store/apps/details?id=de.tadris.fitness) are able to read the heartrate from this sensor. Update: The device has not to be bonded in the recent version.

Thanks

Thanks to Aaron Christophel for providing instructions on how to modify the hardware, mapping out the pins and providing some demo Arduino code.

Licensing

Please note that while most code in this repository is licensed under the terms of the GPLv3 license, this explicitly does not apply to the libraries contained in /lib, fonts in /res/fonts, images in /res/img, patches in /patch and the file /doc/pinout.png. All these documents have their own license attached.

Fonts

Fonts can be found in /res/fonts.

Standard ascii 5x7
Roboto Mono Bold [24pt, 36pt, 48pt]

Images

Images can be found in /res/img.

MDI heart-pulse [36px]
MDI map-marker-distance [36px]
MDI shoe-print [36px]
MDI speedometer [36px]

Patches

Patches to Mbed can be found in /patch.

ARM Mbed RTOS and API

Libraries

All modified libraries have been or will be published to https://platformio.org , their source code can be found in /lib.

CurrentTimeService
Adafruit_GFX
Adafruit_ST7735_Mini
UnsafeI2C
GT24L24A2Y
  • URL: (Unpublished, unfinished)
  • License: GPLv3
Heartrate3_AFE4404
kionix-kx123-driver
RegisterWriter
JLink_RTT

Other

File /doc/pinout.png