Merge branch 'develop' of JF/PineTime into master

This commit is contained in:
JF 2020-09-26 17:09:24 +02:00 committed by Gitea
commit b6a910e52e
90 changed files with 2318 additions and 7776 deletions

202
.github/workflows/c-cpp.yml vendored Normal file
View File

@ -0,0 +1,202 @@
# GitHub Action to build FreeRTOS Firmware for PineTime Smart Watch
# Based on https://github.com/lupyuen/pinetime-lab/blob/master/doc/buildAndProgram.md
name: C/C++ CI
# Run this workflow on every push and pull request on "master" branch
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Steps to be run for the workflow
jobs:
build:
# Run these steps on Ubuntu
runs-on: ubuntu-latest
steps:
- name: Install cmake
uses: lukka/get-cmake@v3.18.0
- name: Check cache for Embedded Arm Toolchain arm-none-eabi-gcc
id: cache-toolchain
uses: actions/cache@v2
env:
cache-name: cache-toolchain
with:
path: ${{ runner.temp }}/arm-none-eabi
key: ${{ runner.os }}-build-${{ env.cache-name }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}
- name: Install Embedded Arm Toolchain arm-none-eabi-gcc
if: steps.cache-toolchain.outputs.cache-hit != 'true' # Install toolchain if not found in cache
uses: fiam/arm-none-eabi-gcc@v1.0.2
with:
# GNU Embedded Toolchain for Arm release name, in the V-YYYY-qZ format (e.g. "9-2019-q4")
release: 8-2019-q3
# Directory to unpack GCC to. Defaults to a temporary directory.
directory: ${{ runner.temp }}/arm-none-eabi
- name: Check cache for nRF5 SDK
id: cache-nrf5sdk
uses: actions/cache@v2
env:
cache-name: cache-nrf5sdk
with:
path: ${{ runner.temp }}/nrf5_sdk
key: ${{ runner.os }}-build-${{ env.cache-name }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}
- name: Install nRF5 SDK
if: steps.cache-nrf5sdk.outputs.cache-hit != 'true' # Install SDK if not found in cache
run: cd ${{ runner.temp }} && curl https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip -o nrf5_sdk.zip && unzip nrf5_sdk.zip && mv nRF5_SDK_15.3.0_59ac345 nrf5_sdk
- name: Checkout source files
uses: actions/checkout@v2
- name: Show files
run: set ; pwd ; ls -l
- name: CMake
run: mkdir -p build && cd build && cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=${{ runner.temp }}/arm-none-eabi -DNRF5_SDK_PATH=${{ runner.temp }}/nrf5_sdk -DUSE_OPENOCD=1 ../
- name: Make
# For debugging builds, remove option "-j" for clearer output. Add "--trace" to see details.
run: cd build && make -j pinetime-app
- name: Find output
run: find . -name pinetime-app.out
- name: Upload built firmware
uses: actions/upload-artifact@v2
with:
# Artifact name (optional)
name: pinetime-app.out
# A file, directory or wildcard pattern that describes what to upload
path: build/src/pinetime-app.out
# Embedded Arm Toolchain and nRF5 SDK will only be cached if the build succeeds.
# So make sure that the first build always succeeds, e.g. comment out the "Make" step.
# Sample compile command:
# [100%] Building CXX object src/CMakeFiles/pinetime-app.dir/drivers/TwiMaster.cpp.o
# cd /home/runner/work/pinetime-lab/pinetime-lab/build/src && /home/runner/work/_temp/arm-none-eabi/bin/arm-none-eabi-c++ --sysroot=/home/runner/work/_temp/arm-none-eabi/bin \
# -DBOARD_PCA10040 \
# -DCONFIG_GPIO_AS_PINRESET \
# -DDEBUG \
# -DDEBUG_NRF_USER \
# -DFREERTOS \
# -DNIMBLE_CFG_CONTROLLER \
# -DNRF52 \
# -DNRF52832 \
# -DNRF52832_XXAA \
# -DNRF52_PAN_12 \
# -DNRF52_PAN_15 \
# -DNRF52_PAN_20 \
# -DNRF52_PAN_31 \
# -DNRF52_PAN_36 \
# -DNRF52_PAN_51 \
# -DNRF52_PAN_54 \
# -DNRF52_PAN_55 \
# -DNRF52_PAN_58 \
# -DNRF52_PAN_64 \
# -DNRF52_PAN_74 \
# -DOS_CPUTIME_FREQ \
# -D__HEAP_SIZE=8192 \
# -D__STACK_SIZE=8192 \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/. \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/.. \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/FreeRTOS \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/date/includes \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/porting/npl/freertos/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/porting/nimble/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/host/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/controller/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/transport/ram/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/drivers/nrf52/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/ext/tinycrypt/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/host/services/gap/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/host/services/gatt/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/host/util/include \
# -I/home/runner/work/pinetime-lab/pinetime-lab/src/libs/mynewt-nimble/nimble/host/store/ram/include \
# -I/home/runner/work/_temp/nrf5_sdk/components/drivers_nrf/nrf_soc_nosd \
# -I/home/runner/work/_temp/nrf5_sdk/components \
# -I/home/runner/work/_temp/nrf5_sdk/components/boards \
# -I/home/runner/work/_temp/nrf5_sdk/components/softdevice/common \
# -I/home/runner/work/_temp/nrf5_sdk/integration/nrfx \
# -I/home/runner/work/_temp/nrf5_sdk/integration/nrfx/legacy \
# -I/home/runner/work/_temp/nrf5_sdk/modules/nrfx \
# -I/home/runner/work/_temp/nrf5_sdk/modules/nrfx/drivers/include \
# -I/home/runner/work/_temp/nrf5_sdk/modules/nrfx/hal \
# -I/home/runner/work/_temp/nrf5_sdk/modules/nrfx/mdk \
# -I/home/runner/work/_temp/nrf5_sdk/external/freertos/source/include \
# -I/home/runner/work/_temp/nrf5_sdk/components/toolchain/cmsis/include \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/atomic \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/atomic_fifo \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/atomic_flags \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/balloc \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/bootloader/ble_dfu \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/cli \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/crc16 \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/crc32 \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/crypto \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/csense \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/csense_drv \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/delay \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/ecc \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/experimental_section_vars \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/experimental_task_manager \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/fds \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/fstorage \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/gfx \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/gpiote \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/hardfault \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/hci \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/led_softblink \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/log \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/log/src \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/low_power_pwm \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/mem_manager \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/memobj \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/mpu \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/mutex \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/pwm \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/pwr_mgmt \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/queue \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/ringbuf \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/scheduler \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/sdcard \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/slip \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/sortlist \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/spi_mngr \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/stack_guard \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/strerror \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/svc \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/timer \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd/class/audio \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd/class/cdc \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd/class/cdc/acm \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd/class/hid \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd/class/hid/generic \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd/class/hid/kbd \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd/class/hid/mouse \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/usbd/class/msc \
# -I/home/runner/work/_temp/nrf5_sdk/components/libraries/util \
# -I/home/runner/work/_temp/nrf5_sdk/external/segger_rtt \
# -I/home/runner/work/_temp/nrf5_sdk/external/fprintf \
# -I/home/runner/work/_temp/nrf5_sdk/external/thedotfactory_fonts -O3 \
# -DNDEBUG -MP -MD -mthumb -mabi=aapcs -Wall -g3 \
# -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin \
# --short-enums -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 \
# -Wreturn-type -Werror=return-type -O3 -std=gnu++11 \
# -o CMakeFiles/pinetime-app.dir/drivers/TwiMaster.cpp.o \
# -c /home/runner/work/pinetime-lab/pinetime-lab/src/drivers/TwiMaster.cpp
# /home/runner/work/pinetime-lab/pinetime-lab/src/drivers/TwiMaster.cpp:1:10: fatal error: sdk/integration/nrfx/nrfx_log.h: No such file or directory
# #include <sdk/integration/nrfx/nrfx_log.h>

18
.gitignore vendored
View File

@ -1,5 +1,19 @@
.idea
cmake-build-*
.idea/
cmake-build-*/
**/CMakeCache.txt
CMakeFiles/
core
sdk
src/Version.h
docker/post_build.sh
# Linux
**/.directory
**/*.swp
# OSX/MacOS
**/.DS_Store
# Windows
**/thumbs.db

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(pinetime VERSION 0.7.1 LANGUAGES C CXX ASM)
project(pinetime VERSION 0.8.2 LANGUAGES C CXX ASM)
set(NRF_TARGET "nrf52")
@ -65,5 +65,7 @@ endif()
set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docker/post_build.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/docker/post_build.sh)
add_subdirectory(src)

View File

@ -6,7 +6,7 @@
*https://www.pine64.org/pinetime/*
The **Pinetime** smartwatch is built around the NRF52832 MCU (512KB Flash, 64KB RAM), a 240*240 LCD display driven by the ST7789 controller, an accelerometer, a heartrate sensor and a vibrator.
The **Pinetime** smartwatch is built around the NRF52832 MCU (512KB Flash, 64KB RAM), a 240*240 LCD display driven by the ST7789 controller, an accelerometer, a heart rate sensor, and a vibration motor.
# InfiniTime
![InfiniTime logo](images/infinitime-logo.jpg "InfiniTime Logo")
@ -19,7 +19,7 @@ The goal of this project is to design an open-source firmware for the Pinetime s
- Using **[LittleVGL/LVGL 6.1.2](https://lvgl.io/)** as UI library...
- ... and **[NimBLE 1.3.0](https://github.com/apache/mynewt-nimble)** as BLE stack.
##Overview
## Overview
![Pinetime screens](images/0.7.0/montage.jpg "PinetimeScreens")
@ -27,29 +27,40 @@ As of now, here is the list of achievements of this project:
- Fast and optimized LCD driver
- BLE communication
- Rich user interface via display, touchpanel and push button
- Time synchronisation via BLE
- Rich user interface via display, touchscreen and pushbutton
- Time synchronization via BLE
- Notification via BLE
- Multiple 'apps' :
* Clock (displays the date, time, battery level, ble connection status, heart rate and step count)
* Clock (displays the date, time, battery level, BLE connection status, heart rate and step count)
* Heart rate
* Motion
* System info (displays various info : BLE MAC, build date/time, uptime, version,...)
* Brightess (allows the user to configure the brightness of the display)
- Supported by 2 companion apps (developpments ongoing):
* [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/branch/pinetime-jf) (on Android)
* System info (displays various info : BLE MAC, build date/time, uptime, version, ...)
* Brightness (allows the user to configure the brightness of the display)
- Supported by 2 companion apps (development is in progress):
* [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android)
* [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS)
- **[Experimental]** OTA (Over-the-air) update via BLE
- **[Experimental]** Bootloader based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/)
## Documentation
### Develop
- [Generate the fonts and symbols](src/DisplayApp/Fonts/Readme.md)
### Build, flash and debug
- [Project branches](doc/branches.md)
- [Versioning](doc/versioning.md)
- [Files included in the release notes](doc/filesInReleaseNotes.md)
- [Build the project](doc/buildAndProgram.md)
- [Build the project with Docker](doc/buildWithDocker.md)
- [Bootloader, OTA and DFU](./bootloader/README.md)
- [Stub using NRF52-DK](./doc/PinetimeStubWithNrf52DK.md)
- Logging with JLink RTT.
- Using files from the releases
### Contribute
- [How to contribute ?](doc/contribute.md)
### API
- [BLE implementation and API](./doc/ble.md)
@ -57,27 +68,26 @@ As of now, here is the list of achievements of this project:
- [Memory analysis](./doc/MemoryAnalysis.md)
### Using the firmware
- Integration with Gadgetbridge
- Integration with AmazFish
- Integration with NRFConnect
- Firmware update, OTA
- [Integration with Gadgetbridge](doc/CompanionApps/Gadgetbridge.md)
- [Integration with AmazFish](doc/CompanionApps/Amazfish.md)
- [Firmware update, OTA](doc/CompanionApps/NrfconnectOTA.md)
## TODO - contribute
This project is far from beeing finished, and there are still a lot of things to do for this project to become a firmware usable by the general public.
This project is far from being finished, and there are still a lot of things to do for this project to become a firmware usable by the general public.
Here a quick list out of my head of things to do for this project:
- Improve BLE communication stability and reliability
- Improve OTA and MCUBoot bootloader
- Add more functionalities : Alarm, chrono, configuration, activities, heart rate logging, games,...
- Add more functionalities : Alarm, chronometer, configuration, activities, heart rate logging, games,...
- Add more BLE functionalities : call notifications, agenda, configuration, data logging,...
- Measure power consumption and improve battery life
- Improve documentation, take better pictures and video than mine
- Improve the UI
- Create companion app for multiple OSes (Linux, Android, IoS) and platforms (desktop, ARM, mobile). Do not forget the other devices from Pine64 like [the Pinephone](https://www.pine64.org/pinephone/) and the [Pinebook Pro](https://www.pine64.org/pinebook-pro/).
- Design a simple CI (preferably selfhosted and easy to reproduce).
- Create companion app for multiple OSes (Linux, Android, iOS) and platforms (desktop, ARM, mobile). Do not forget the other devices from Pine64 like [the Pinephone](https://www.pine64.org/pinephone/) and the [Pinebook Pro](https://www.pine64.org/pinebook-pro/).
- Design a simple CI (preferably self-hosted and easy to reproduce).
Do not hesitate to clone/fork the code, hack it and create pull-requests. I'll do my best to review and merge them :)
@ -96,7 +106,7 @@ Im not working alone on this project. First, many people create PR for this p
Here are some people I would like to highlight:
- [Atc1441](https://github.com/atc1441/) : He works on an Arduino based firmware for the Pinetime and many other smartwatches based on similar hardware. He was of great help when I was implementing support for the BMA421 motion sensor and I²C driver.
- [Koen](https://github.com/bosmoment) : Hes working on a firmware based on RiotOS. He integrated similar libs than me : NimBLE, LittleVGL,… His help was invaluable too!
- [Lup Yuen Lee](https://github.com/lupyuen) : He is everywhere : he works on a Rust firmware, buils a MCUBoot based bootloader for the Pinetime, design a Flutter based companion app for smartphones and write a lot of articles about the Pinetime!
- [Koen](https://github.com/bosmoment) : Hes working on a firmware based on RiotOS. He integrated similar libs as me : NimBLE, LittleVGL,… His help was invaluable too!
- [Lup Yuen Lee](https://github.com/lupyuen) : He is everywhere: he works on a Rust firmware, builds a MCUBoot based bootloader for the Pinetime, designs a Flutter based companion app for smartphones and writes a lot of articles about the Pinetime!
*If you feel like you should appear on this list, just get in touch with me or submit a PR :)*

View File

@ -1,4 +1,53 @@
# Bootloader
# About this bootloader
The [bootloader](https://github.com/lupyuen/pinetime-rust-mynewt/tree/master/libs/pinetime_boot/src) is mostly developed by [Lup Yuen](https://github.com/lupyuen). It is based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/) and [Mynewt](https://mynewt.apache.org/).
The goal of this project is to provide a common bootloader for multiple (all?) Pinetime projects. It allows to upgrade the current bootloader and even replace the current application by another one that supports the same bootloader.
As we wanted this bootloader to be as universal as possible, we decided that it should **not** integrate a BLE stack and provide OTA capabilities.
Integrating a BLE stack for the OTA functionality would have used to much memory space and/or forced all the firmware developers to use the same BLE stack as the bootloader.
When it is run, this bootloader looks in the SPI flash memory if a new firmware is available. It there is one, it *swaps* the current firmware with the new one (the new one is copied in the main flash memory, and the current one is copied in the SPI flash memory) and run the new one. If the new one fails to run properly, the bootloader is able to revert to the old one and mark the new one as not working.
As this bootloader does not provide any OTA capability, it is not able to actually download a new version of the application. Providing OTA functionality is thus the responsability of the application firmware.
# About MCUBoot
MCUBoot is run at boot time. In normal operation, it just jumps to the reset handler of the application firmware to run it. Once the application firmware is running, MCUBoot does not run at all.
![MCUBoot boot sequence diagram](../doc/bootloader/boot.png "MCUBoot boot sequence diagram")
But MCUBoot does much more than that : it can upgrade the firmware that is currently running by a new one, and it is also able to revert to the previous version of the firmware in case the new one does not run propertly.
To do this, it uses 2 memory 'slots' :
- **The primary slot** : it contains the current firmware, the one that will be executed by MCUBoot
- **The secondary slot** : it is used to store the upgraded version of the firmware, when available.
At boot time, MCUBoot detects that a new version of the firmware is available in the secondary slot and swaps them : the current version of the firmware is copied from the primary to the secondary slot and vice-versa.
When the swapping is done, it runs the new version of the firmware from the primary slot.
![MCUBoot update sequence diagram](../doc/bootloader/upgrade.png "MCUBoot update sequence diagram")
The next time MCUBoot will be run (after a MCU reset), MCUBoot will check if the new firmware ran correctly (it must set a flag in memory). If it is not the case, it'll revert to the previous version of the firmware by copying it from the secondary to the primary.
![MCUBoot update sequence diagram](../doc/bootloader/recover.png "MCUBoot update sequence diagram")
Note than MCUBoot **does not** provide any means to download and store the new version of the firmware into the secondary slot. This must be implemented by the application firmware.
# Degraded cases
This chapter describes degraded cases that are handled by our bootloader and those that are not supported.
Case | Current bootloader | Solution
-----|--------------------|----------------------------------------------
Data got corrupted during file transfert | [OK] Application firmware does a CRC check before applying the update, and does not proceed if it fails. | N/A
New firmware does not run at all (bad file) (1) | [NOK] MCU executes unknown instructions and will most likely end up in an infinite loop or freeze in an error handler. The bootloader does not run, it can do nothing, the MCU is stucked until next reset | [OK] The bootloader starts the watchdog just before running the new firmware. This way, the watchdog will reset the MCU after ~7s because the firmware does not refresh it. Bootloader reverts to the previous version of the firmware during the reset.
New firmware runs, does not set the valid bit and does not refresh the watchdog | [NOK] The new firmware runs until the next reset. The bootloader will be able to revert to the previous firmware only during the next reset. If the new firmware does not run properly and does not reset, the bootloader can do nothing until the next reset | [OK] The bootloader starts the watchdog just before running the new firmware. This way, the watchdog will reset the MCU after ~7s because the firmware does not refresh it. Bootloader reverts to the previous version of the firmware during the reset.
New firmware does not run properly, does not set the valid bit but refreshes the watchdog | [NOK] The bootloader will be able to revert to the previous firmware only during the next reset. If the new firmware does not run properly and does not reset, the bootloader can do nothing until the next reset | [~] Wait for the battery to drain. The CPU will reset the next time the device is charged and will be able to rollback to the previous version.
New firmware does not run properly but sets the valid bit and refreshes the watchdog | [NOK] The bootloader won't revert to the previous version because the valid flag is set | [~] Wait for the battery to drain. The CPU will reset the next time the device is charged. Then, the bootloader must provide a way for the user to force the rollback to the previous version
*(1) I've observed this when I tried to run a firmware built to run from offset 0 while it was flashed at offset 0x8000 in memory (bad vector table).*
# Using the bootloader
## Bootloader graphic
The bootloader loads a graphic (Pinetime logo) from the SPI Flash memory. If this graphic is not loaded in the memory, the LCD will display garbage (the content of the SPI flash memory).
@ -18,14 +67,14 @@ program pinetime-graphics.bin 0
- Let it run for ~10s (it does nothing for 5 seconds, then write the logo into the SPI memory, then (slowly) displays it on the LCD).
## Bootloader binary
The binary comes from https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7
The binary comes from https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4
It must be flash at address **0x00** in the internal flash memory.
Using OpenOCD:
`
program mynewt_nosemi.elf_4.1.7.bin 0
program bootloader-5.0.4.bin 0
`
## Application firmware image
@ -64,4 +113,25 @@ Use NRFConnect or dfu.py (in <project root>/bootloader/ota-dfu-python) to upload
sudo dfu.py -z /home/jf/nrf52/bootloader/dfu.zip -a <pinetime MAC address> --legacy
`
**Note** : dfu.py is a slightly modified version of [this repo](https://github.com/daniel-thompson/ota-dfu-python).
**Note** : dfu.py is a slightly modified version of [this repo](https://github.com/daniel-thompson/ota-dfu-python).
See [this page](../doc/CompanionApps/NrfconnectOTA.md) for more info about OTA with NRFConect
### Firmware validation
Once the OTA is done, InfiniTime will reset the watch to apply the update. When the watch reboots, the new firmware is running.
One last step is needed to finalize the upgrade : the new firmware must be manually validated. If the watch resets while the image is not validated, the bootloader will automatically revert to the previous version of the firmware.
If the new firmware is working correctly, open the application menu and tap on the 'check' app. This apps displays the version of the firmware that is currently running, and allows you to validate the firmware, or reset the device to rollback to the previous version.
Firmware validation application in the menu:
![Firmware Validation App](../doc/CompanionApps/firmwareValidationApp.jpg "Firmware Validation App")
The firmware is not validated yet. Tap 'Validate' to validate it, or 'Reset' to rollback to the previous version.
![Firmware Not Validated](../doc/CompanionApps/firmwareNoValidated.jpg "Firmware Not Validated")
The firmware is validated!
![Firmware Validated](../doc/CompanionApps/firmwareValidated.jpg "Firmware Validated")

View File

@ -19,7 +19,7 @@ echo ""
# Flashing Bootloader
echo "Flashing bootloader..."
program ./mynewt_nosemi_4.1.7.elf verify 0x00000000
program ./bootloader-5.0.4.elf verify 0x00000000
# Flashing Application
echo "Flashing application..."

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
# Amazfish
[Amazfish](https://openrepos.net/content/piggz/amazfish) is a companion app that supports many smartwatches and activity trackers running on [SailfishOS](https://sailfishos.org/).
## Features
The following features are implemented:
- Scanning & detection of Pinetime-JF / InfiniTime
- Connection / disconnection
- Time synchronization
- Notifications
- Music control
## Demo
[This video](https://seafile.codingfield.com/f/21c5d023452740279e36/) shows how to connect to the Pinetime and control the playback of the music on the phone.
Amazfish and Sailfish OS are running on the [Pinephone](https://www.pine64.org/pinephone/), another awesome device from Pine64.

View File

@ -0,0 +1,13 @@
# Integration with Gadgetbridge
[Gadgetbridge](https://gadgetbridge.org/) is an Android application that supports many smartwatches and fitness trackers.
The integration of InfiniTime (previously Pinetime-JF) is now merged into the master branch (https://codeberg.org/Freeyourgadget/Gadgetbridge/).
## Features
The following features are implemented:
- Scanning & detection of Pinetime-JF / InfiniTime
- Connection / disconnection
- Notifications
## Demo
[This video](https://seafile.codingfield.com/f/0a2920b9d765462385e4/) shows how to scan, connect, send notification (using the debug screen) and disconnect from the Pinetime.

View File

@ -0,0 +1,12 @@
# OTA using NRFConnect
[NRFConnect](https://www.nordicsemi.com/Software-and-tools/Development-Tools/nRF-Connect-for-mobile) is a powerful application (running on Android and iOS) which allows to scan and connect to BLE devices.
## Features
- Scanning, connect, disconnect
- Time synchronization
- OTA
InfiniTime implements the Nordic DFU protocol for the OTA functionality. NRFConnect also supports this protocol.
# Demo
[This video](https://seafile.codingfield.com/f/a52b69683a05472a90c7/) shows how to use NRFConnect to update the firmware running on the Pinetime.

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

View File

@ -24,17 +24,17 @@ As I said in the introduction, the datasheet will be your bedside book during yo
The schematic of your board (the Pinetime schematics in this case) will also be very important, as you'll need to know how the LCD controller is physically connected to the MCU.
How to read the datasheet? I recommand to read it from the beginning to the end (no joke) at least once. You certainly do not need to read everything in details, but it's good to know what information is available and where in the document. It'll be very useful during the developpment phase.
How to read the datasheet? I recommend to read it from the beginning to the end (no joke) at least once. You certainly do not need to read everything in details, but it's good to know what information is available and where in the document. It'll be very useful during the development phase.
You'll want to read some part with more attention :
- Data color coding in 4-Line Serial Interface : how to send the pixel to be display to the controller
- Display Data Ram : how is the memory organised
- Display Data Ram : how is the memory organized
- Power On/Off sequence
- System function commands : all the commands you can send to the controller.
## One Pixel at a time
## Bulk transfert
## Bulk transfers
## DMA

BIN
doc/bootloader/boot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

19
doc/bootloader/boot.puml Normal file
View File

@ -0,0 +1,19 @@
@startuml
MCU -> Bootloader: reset
activate Bootloader
Bootloader -> Bootloader: Recover? (no)
Bootloader -> Bootloader: New version? (no)
Bootloader -> Application: Jump to primary slot
deactivate Bootloader
activate Application
note right: This is the current version of the firmware
Application -> Application: OTA procedure
note right: Download a new firmware version and\n store it in secondary slot
Application -> MCU: Reset
deactivate Application
@enduml

BIN
doc/bootloader/recover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,17 @@
@startuml
MCU -> Bootloader: reset
activate Bootloader
Bootloader -> Bootloader: Recover? (yes)
Bootloader -> Bootloader: Restore previous firmware
note left: Copy the previous firmware from secondary to primary slot
Bootloader -> Application: Jump to primary slot
deactivate Bootloader
activate Application
note right: This is the previous version\nof the firmware
Application -> Application: Normal Operation
Application -> MCU: Reset
deactivate Application
@enduml

BIN
doc/bootloader/upgrade.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,21 @@
@startuml
MCU -> Bootloader: reset
activate Bootloader
Bootloader -> Bootloader: Recover? (no)
Bootloader -> Bootloader: New version? (yes)
Bootloader -> Bootloader: Swap firmwares
note left: Copy current firmware from primary to secondary\nand copy the new firmware from secondary to primary
Bootloader -> Application: Jump to primary slot
deactivate Bootloader
activate Application
note right: This is the new version of the firmware
Application -> Application: Write the valid bit in flash memory
note right: The application should provide a way to\ncheck that it is running correctly\n(selftest, user confirmation,...)\nbefore setting the valid bit.
Application -> Application: Normal operations...
Application -> MCU: Reset
deactivate Application
@enduml

12
doc/branches.md Normal file
View File

@ -0,0 +1,12 @@
# Branches
The branching model of this project is based on the workflow named [Git flow](https://nvie.com/posts/a-successful-git-branching-model/).
It is based on 2 main branches:
- **master** : this branch is always ready to be reployed. It means that at any time, we should be able to build the branch and release a new version of the application.
- **develop** : this branch contains the latest development that will be integrated in the next release once it's considered as stable.
New features should be implemented in **feature branches** created from **develop**. When the feature is ready, a pull-request is created and it'll be merge into **develop** when it is succesfully reviewed and accepted.
To release a new version of the application, when develop is considered stable, a **release** branch is created from **develop**. This can be considered as a *release candidate* branch. When everything is OK, this release branch is merged into **master** and the release is generated (a tag is applied to git, the release note is finalized, binaries are built,...) from **master**.
Git flow also supports the creation of **hotfix** branches when a bug is discovered in a released version. The **hotfix** branch is created from **master** and will be used only to implement a fix to this bug. Multiple hotfix branches can be created for the same release if more than one bugs are discovered.

View File

@ -1,19 +1,19 @@
# Build
##Dependencies
## Dependencies
To build this project, you'll need:
- A cross-compiler : [gcc-arm-none-eabi-8-2019-q3-update](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads/8-2019q3-update)
- The NRF52 SDK 15.3.0 : [nRF5_SDK_15.3.0_59ac345](https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip)
- A reasonably recent version of CMake (I use 3.16.5)
##Build steps
###Clone the repo
## Build steps
### Clone the repo
```
git clone https://github.com/JF002/Pinetime.git
cd Pinetime
mkdir build
cd build
```
###Project generation using CMake
### Project generation using CMake
CMake configures the project according to variables you specify the command line. The variables are:
Variable | Description | Example|
@ -27,22 +27,22 @@ CMake configures the project according to variables you specify the command line
**GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0`
####CMake command line for JLink
#### CMake command line for JLink
```
cmake -DCMAKE_BUILD_TYPE=Debug -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../
```
####CMake command line for GDB Client (Black Magic Probe)
#### CMake command line for GDB Client (Black Magic Probe)
```
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_GDB_CLIENT=1 -DGDB_CLIENT_BIN_PATH=... -DGDB_CLIENT_TARGET_REMOTE=... ../
```
####CMake command line for OpenOCD
#### CMake command line for OpenOCD
```
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_OPENOCD=1 -DGDB_CLIENT_BIN_PATH=[optional] ../
```
###Build the project
### Build the project
During the project generation, CMake created the following targets:
- FLASH_ERASE : mass erase the flash memory of the NRF52.
- FLASH_pinetime-app : flash the firmware into the NRF52.
@ -50,7 +50,7 @@ During the project generation, CMake created the following targets:
- pinetime-mcuboot-app : build the firmware with the support of the bootloader (based on MCUBoot).
- pinetime-graphics : small firmware that writes the boot graphics into the SPI flash.
If you just want to build the project and run it on the Pinetime, using *pinetime-app* is recommanded. See ???? for more info about bootloader support.
If you just want to build the project and run it on the Pinetime, using *pinetime-app* is recommanded. See [this page](../bootloader/README.md) for more info about bootloader support.
Build:
```
@ -66,8 +66,8 @@ Binary files are generated into the folder `src`:
- **pinetime-graphics.bin, .hex and .out** : firmware for the boot graphic in bin, hex and out formats.
- **pinetime-graphics.map** : map file
###Program and run
####Using CMake targets
### Program and run
#### Using CMake targets
These target have been configured during the project generation by CMake according to the parameters you provided to the command line.
Mass erase:
@ -80,7 +80,7 @@ Flash the application:
make FLASH_pinetime-app
```
###Using JLink
### Using JLink
Start JLinkExe:
```
$ /opt/SEGGER/JLink/JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1
@ -146,7 +146,7 @@ Reset: Reset device via AIRCR.SYSRESETREQ.
J-Link>g
```
####JLink RTT
#### JLink RTT
RTT is a feature from Segger's JLink devices that allows bidirectionnal communication between the debugger and the target. This feature can be used to get the logs from the embedded software on the development computer.
- Program the MCU with the code (see above)
@ -161,7 +161,7 @@ Start JLinkRTTClient
$ JLinkRTTClient
```
###Using GDB and Black Magic Probe (BMP)
### Using GDB and Black Magic Probe (BMP)
Enter the following command into GDB:
```

33
doc/buildWithDocker.md Normal file
View File

@ -0,0 +1,33 @@
# Build the project using Docker
A [Docker image (Dockerfile)](../docker) containing all the build environment is available for X86_64 and AMD64 architectures. This image makes the build of the firmware and the generation of the DFU file for OTA.
## Build the image
The image is not (yet) available on DockerHub, you need to build it yourself, which is quite easy. The following commands must be run from the root of the project.
If you are running on a x86_64 computer :
```
docker image build -t infinitime-build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) docker/x86_64/
```
And if your are running on an ARM64 device (tested on RaspberryPi4 and Pine64 PineBookPro):
```
docker image build -t infinitime-build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) docker/arm64/
```
This operation will take some time. It builds a Docker image based on Ubuntu, install some packages, download the ARM toolchain, the NRF SDK, MCUBoot and adafruit-nrfutil.
When this is done, a new image named *infinitime-build* is available.
## Run a container to build the project:
```
docker run --rm -v <project_root>:/sources infinitime-build
```
Replace *<project_root>* by the path of the root of the project on your computer. For example:
```
docker run --rm -v /home/jf/git/PineTime:/sources infinitime-build
```
This will start a container, build the firmware and generate the MCUBoot image and the DFU file. The output of the build is stored in **<project_root>/built/output**.

24
doc/contribute.md Normal file
View File

@ -0,0 +1,24 @@
# How to contribute?
## Report bugs
You use your Pinetime and find a bug in the firmware? [Create an issue on Github](https://github.com/JF002/Pinetime/issues) explaining the bug, how to reproduce it, the version of the firmware you use...
## Write and improve documentation
Documentation might be incomplete, or not clear enough, and it is always possible to improve it with better wording, pictures, photo, video,...
As the documentation is part of the source code, you can submit your improvements to the documentation by submitting a pull request (see below).
## Fix bugs, add functionalities and improve the code
You want to fix a bug, add a cool new functionality or improve the code? See *How to submit a pull request below*.
## Spread the word
Pinetime is a cool open source project that deserves to be know. Talk about it around you, on social networks, on your blog,... and let people know that we are working on an open-source firmware for a smartwatch!
# How to submit a pull request ?
Your contribution is more than welcome!
If you want to fix a bug, add a functionality or improve the code, you'll first need to create a branch from the **develop** branch (see [this page about the branching model](./branches.md)). This branch is called a feature branch, and you should choose a name that explains what you are working on (ex: "add-doc-about-contributions"). In this branch, try to focus on only one topic, bug or feature. For example, if you created this branch to work on the UI of a specific application, do not commit modifications about the SPI driver. If you want to work on multiple topics, create one branch per topic.
When your feature branch is ready, make sure it actually works and do not forget to write documentation about it if necessary.
Then, you can submit a pull-request for review. Try to describe your pull request as much as possible: what did you do in this branch, how does it work, how is it designed, are there any limitations,... This will help the contributors to understand and review your code easily.
Other contributors can post comments about the pull request, maybe ask for more info or adjustements in the code.
Once the pull request is reviewed an accepted, it'll be merge in **develop** and will be released in the next release version of the firmware.

View File

@ -0,0 +1,56 @@
# Using the releases
For each new *stable* version of Pinetime, a [release note](https://github.com/JF002/Pinetime/releases) is created. It contains a description of the main changes in the release and some files you can use to flash the firmware in your Pinetime.
This page describes the files from the release notes and how to use them.
**NOTE :** the files included in different could be different. This page describes the release note of [version 0.7.1](https://github.com/JF002/Pinetime/releases/tag/0.7.1), which is the version that'll probably be pre-programmed at the factory for the next batch of Pinetime devkits.
## Files included in the release note
### Standalone firmware
This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flash at offset 0, meaning it will erase any bootloader that might be present in memory.
- **pinetime-app.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB.
- **pinetime-app.hex** : Firmware in Intel HEX file format. Easier to use because it contains the offset in memory where it must be flashed, you don't need to specify it.
- **pintime-app.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed.
- **pinetime-app.map** : Map file containing all the symbols, addresses in memory,...
**This firmware must be flashed at address 0x00 in the main flash memory**
### Bootloader
The bootloader is maintained by [lupyuen](https://github.com/lupyuen) and is a binary version of [this release](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4).
- **bootloader.hex** : Firmware in Intel HEX file format.
**This firmware must be flashed at address 0x00 in the main flash memory**
### Graphics firmware
This firmware is a small utility firmware that writes the boot graphic in the external SPI flash memory. You need it if you want to use the [bootloader](../bootloader/README.md).
- **pinetime-graphics.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB.
- **pinetime-graphics.hex** : Firmware in Intel HEX file format. Easier to use because it contains the offset in memory where it must be flashed, you don't need to specify it.
- **pintime-graphics.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed.
- **pinetime-graphics.map** : Map file containing all the symbols, addresses in memory,...
**This firmware must be flashed at address 0x00 in the main flash memory**
### Firmware with bootloader
This firmware is intended to be used with our [MCUBoot-based bootloader](../bootloader/README.md).
- **pinetime-mcuboot-app-image.hex** : Firmware wrapped into an MCUBoot image. This is **the** file that must be flashed **@ 0x8000** into flash memory. If the [bootloader](../bootloader/README.md) has been successfully programmed, it should run this firmware after the next reset.
The following files are not directly usable by the bootloader:
- **pinetime-mcuboot-app.bin** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB.
- **pinetime-mcuboot-app.hex** : Firmware in Intel HEX file format.
- **pinetime-mcuboot-app.bin** : Firmware in binary format.
- **pinetime-mcuboot-app.map** : Map file containing all the symbols, addresses in memory,...
### OTA (Update the firmware Over-The-Air)
Once the bootloader and application firmware are running, it is possible to update the current firmware or even replace it with another firmware **that uses the same bootloader based on MCUBoot**.
**NOTE :** Use this file **only** if you programmed our [MCUBoot-based bootloader](../bootloader/README.md). This file is not compatible with other bootloaders like the one that is based on the closed source NRF SoftDevice !
- **pinetime-app-dfu.zip** : This is the file you must provide toNRFConnect to update the firmware over BLE.

6
doc/versioning.md Normal file
View File

@ -0,0 +1,6 @@
# Versioning
The versioning of this project is based on [Semantic versionning](https://semver.org/) :
- The **patch** is incremented when we fix a bug on a **released** version (most of the time using a **hotfix** branch).
- The **minor** is incremented when we release a new version with new features. It corresponds to a merge of **develop** into **master**.
- The **major** should be incremented when a breaking change is made to the application. We still have to define what is a breaking change in the context of this project. For now, I suggest that it stays **0** until we have a fully functionning firmware suited for the final user.

2
docker/README.md Normal file
View File

@ -0,0 +1,2 @@
Docker images and build script for building the project using Docker.
See [this page for more info](../doc/buildWithDocker.md).

17
docker/arm64/Dockerfile Normal file
View File

@ -0,0 +1,17 @@
FROM ubuntu:18.04
ARG USER_ID
ARG GROUP_ID
RUN addgroup --gid $GROUP_ID user
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user
RUN apt-get update -qq && apt-get install -y wget unzip cmake make build-essential git python3 python3-pip libffi-dev libssl-dev python3-dev
RUN wget -q https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-aarch64-linux.tar.bz2 -O - | tar -xj -C /opt/
RUN wget -q https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip && unzip -q nRF5_SDK_15.3.0_59ac345.zip -d /opt/ && rm nRF5_SDK_15.3.0_59ac345.zip
RUN git clone https://github.com/JuulLabs-OSS/mcuboot.git /opt/mcuboot && pip3 install -r /opt/mcuboot/scripts/requirements.txt
RUN pip3 install adafruit-nrfutil
USER user
CMD ["/sources/docker/build.sh"]

12
docker/build.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
set -x
mkdir /sources/build
cd /sources/build
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=/opt/gcc-arm-none-eabi-9-2020-q2-update -DNRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345 -DUSE_OPENOCD=1 ../
make -j$(nproc)
sh /sources/docker/post_build.sh

16
docker/post_build.sh.in Executable file
View File

@ -0,0 +1,16 @@
#!/bin/sh
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
set -x
mkdir -p /sources/build/output
/opt/mcuboot/scripts/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header /sources/build/src/pinetime-mcuboot-app-@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.bin /sources/build/output/image-@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.bin
adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application /sources/build/output/image-@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.bin /sources/build/output/dfu-@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.zip
cp /sources/build/src/*.bin /sources/build/output/
cp /sources/build/src/*.hex /sources/build/output/
cp /sources/build/src/*.out /sources/build/output/
cp /sources/build/src/*.map /sources/build/output/
cp /sources/bootloader/bootloader-5.0.4.bin /sources/build/output/bootloader.bin

17
docker/x86_64/Dockerfile Normal file
View File

@ -0,0 +1,17 @@
FROM ubuntu:18.04
ARG USER_ID
ARG GROUP_ID
RUN addgroup --gid $GROUP_ID user
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user
RUN apt-get update -qq && apt-get install -y wget unzip cmake make build-essential git python3 python3-pip
RUN wget -q https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz -O - | tar -xj -C /opt/
RUN wget -q https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip && unzip -q nRF5_SDK_15.3.0_59ac345.zip -d /opt/ && rm nRF5_SDK_15.3.0_59ac345.zip
RUN git clone https://github.com/JuulLabs-OSS/mcuboot.git /opt/mcuboot && pip3 install -r /opt/mcuboot/scripts/requirements.txt
RUN pip3 install adafruit-nrfutil
USER user
CMD ["/sources/docker/build.sh"]

View File

@ -1,25 +0,0 @@
#include "BlinkApp.h"
#include <FreeRTOS.h>
#include <task.h>
#include <libraries/log/nrf_log.h>
#include <boards.h>
using namespace Pinetime::Applications;
void BlinkApp::Start() {
if (pdPASS != xTaskCreate(BlinkApp::Process, "BlinkApp", 256, this, 0, &taskHandle))
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
void BlinkApp::Process(void *instance) {
auto* app = static_cast<BlinkApp*>(instance);
NRF_LOG_INFO("BlinkApp task started!");
while (1) {
// NRF_LOG_INFO("BlinkApp task running!");
// nrf_gpio_pin_toggle(22);
// nrf_gpio_pin_toggle(23);
// nrf_gpio_pin_toggle(14);
vTaskDelay(1000);
}
}

View File

@ -1,15 +0,0 @@
#pragma once
#include <FreeRTOS.h>
#include <task.h>
namespace Pinetime {
namespace Applications {
class BlinkApp {
public:
void Start();
private:
TaskHandle_t taskHandle;
static void Process(void* instance);
};
}
}

View File

@ -287,6 +287,8 @@ set(LVGL_SRC
libs/lvgl/src/lv_objx/lv_bar.h
libs/lvgl/src/lv_objx/lv_slider.h
libs/lvgl/src/lv_objx/lv_slider.c
libs/lvgl/src/lv_objx/lv_ddlist.c
libs/lvgl/src/lv_objx/lv_ddlist.h
)
list(APPEND IMAGE_FILES
@ -321,21 +323,24 @@ list(APPEND IMAGE_FILES
list(APPEND SOURCE_FILES
Logging/NrfLogger.cpp
BlinkApp/BlinkApp.cpp
DisplayApp/DisplayApp.cpp
DisplayApp/Screens/Screen.cpp
DisplayApp/Screens/Clock.cpp
DisplayApp/Screens/Message.cpp
DisplayApp/Screens/Tile.cpp
DisplayApp/Screens/Meter.cpp
DisplayApp/Screens/Gauge.cpp
DisplayApp/Screens/InfiniPaint.cpp
DisplayApp/Screens/DropDownDemo.cpp
DisplayApp/Screens/Modal.cpp
DisplayApp/Screens/BatteryIcon.cpp
DisplayApp/Screens/BleIcon.cpp
DisplayApp/Screens/Brightness.cpp
DisplayApp/Screens/ScreenList.cpp
DisplayApp/Screens/SystemInfo.cpp
DisplayApp/Screens/Label.cpp
DisplayApp/Screens/FirmwareUpdate.cpp
DisplayApp/Screens/Music.cpp
DisplayApp/Screens/FirmwareValidation.cpp
DisplayApp/Screens/ApplicationList.cpp
main.cpp
drivers/St7789.cpp
drivers/SpiNorFlash.cpp
@ -356,15 +361,12 @@ list(APPEND SOURCE_FILES
Components/Ble/DfuService.cpp
Components/Ble/CurrentTimeService.cpp
Components/Ble/AlertNotificationService.cpp
Components/Ble/MusicService.cpp
Components/FirmwareValidator/FirmwareValidator.cpp
drivers/Cst816s.cpp
FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c
FreeRTOS/port_cmsis.c
${TINYCRYPT_SRC}
${NIMBLE_SRC}
${LVGL_SRC}
#${IMAGE_FILES}
${SDK_SOURCE_FILES}
DisplayApp/LittleVgl.cpp
DisplayApp/Fonts/jetbrains_mono_extrabold_compressed.c
@ -375,8 +377,6 @@ list(APPEND SOURCE_FILES
)
list(APPEND GRAPHICS_SOURCE_FILES
${SDK_SOURCE_FILES}
# FreeRTOS
FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c
@ -397,22 +397,26 @@ list(APPEND GRAPHICS_SOURCE_FILES
set(INCLUDE_FILES
Logging/Logger.h
Logging/NrfLogger.h
BlinkApp/BlinkApp.h
DisplayApp/DisplayApp.h
DisplayApp/TouchEvents.h
DisplayApp/Screens/Screen.h
DisplayApp/Screens/Clock.h
DisplayApp/Screens/Message.h
DisplayApp/Screens/Tile.h
DisplayApp/Screens/Meter.h
DisplayApp/Screens/Gauge.h
DisplayApp/Screens/InfiniPaint.h
DisplayApp/Screens/DropDownDemo.h
DisplayApp/Screens/Modal.h
DisplayApp/Screens/BatteryIcon.h
DisplayApp/Screens/BleIcon.cpp
DisplayApp/Screens/Brightness.h
DisplayApp/Screens/SystemInfo.h
DisplayApp/Screens/ScreenList.h
DisplayApp/Screens/Label.h
DisplayApp/Screens/FirmwareUpdate.h
DisplayApp/Screens/FirmwareValidation.h
DisplayApp/Screens/ApplicationList.h
DisplayApp/Apps.h
drivers/St7789.h
drivers/SpiNorFlash.h
drivers/SpiMaster.h
@ -430,7 +434,8 @@ set(INCLUDE_FILES
Components/Ble/CurrentTimeClient.h
Components/Ble/AlertNotificationClient.h
Components/Ble/DfuService.h
drivers/Cst816s.h
Components/FirmwareValidator/FirmwareValidator.h
drivers/Cst816s.h
FreeRTOS/portmacro.h
FreeRTOS/portmacro_cmsis.h
libs/date/includes/date/tz.h
@ -558,10 +563,49 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
# NRF SDK
add_library(nrf-sdk STATIC ${SDK_SOURCE_FILES})
target_include_directories(nrf-sdk SYSTEM PUBLIC . ../)
target_include_directories(nrf-sdk SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
target_compile_options(nrf-sdk PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -std=c99 -x assembler-with-cpp>
)
# NimBLE
add_library(nimble STATIC ${NIMBLE_SRC} ${TINYCRYPT_SRC})
target_include_directories(nimble SYSTEM PUBLIC . ../)
target_include_directories(nimble SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
target_compile_options(nimble PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -std=c99 -x assembler-with-cpp>
)
# lvgl
add_library(lvgl STATIC ${LVGL_SRC})
target_include_directories(lvgl SYSTEM PUBLIC . ../)
target_include_directories(lvgl SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
target_compile_options(lvgl PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -std=c99 -x assembler-with-cpp>
)
# Build autonomous binary (without support for bootloader)
set(EXECUTABLE_NAME "pinetime-app")
set(EXECUTABLE_FILE_NAME ${EXECUTABLE_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld")
add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES})
set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME})
target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl)
target_compile_options(${EXECUTABLE_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
@ -572,24 +616,28 @@ target_compile_options(${EXECUTABLE_NAME} PUBLIC
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
SUFFIX ".out"
LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_NAME}.map"
LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_FILE_NAME}.map"
CXX_STANDARD 11
C_STANDARD 99
)
add_custom_command(TARGET ${EXECUTABLE_NAME}
POST_BUILD
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_NAME}.out "${EXECUTABLE_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_NAME}.out "${EXECUTABLE_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_NAME}")
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_FILE_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_FILE_NAME}.out "${EXECUTABLE_FILE_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_FILE_NAME}.out "${EXECUTABLE_FILE_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_FILE_NAME}")
# Build binary intended to be used by bootloader
set(EXECUTABLE_MCUBOOT_NAME "pinetime-mcuboot-app")
set(EXECUTABLE_MCUBOOT_WITH_BOOTLOADER_NAME "pinetime-mcuboot-app-wth-bootloader")
set(EXECUTABLE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
set(IMAGE_MCUBOOT_FILE_NAME image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.bin)
set(DFU_FILE_NAME dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld")
add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES})
target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl)
set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME})
target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
@ -600,22 +648,25 @@ target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC
set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES
SUFFIX ".out"
LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT_MCUBOOT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_MCUBOOT_NAME}.map"
LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT_MCUBOOT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_MCUBOOT_FILE_NAME}.map"
CXX_STANDARD 11
C_STANDARD 99
)
add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME}
POST_BUILD
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_MCUBOOT_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_MCUBOOT_NAME}.out "${EXECUTABLE_MCUBOOT_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_MCUBOOT_NAME}.out "${EXECUTABLE_MCUBOOT_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_NAME}"
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_MCUBOOT_FILE_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_FILE_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_FILE_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_FILE_NAME}"
)
# Build binary that writes the graphic assets for the bootloader
set(EXECUTABLE_GRAPHICS_NAME "pinetime-graphics")
set(EXECUTABLE_GRAPHICS_FILE_NAME ${EXECUTABLE_GRAPHICS_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
add_executable(${EXECUTABLE_GRAPHICS_NAME} ${GRAPHICS_SOURCE_FILES})
target_link_libraries(${EXECUTABLE_GRAPHICS_NAME} nrf-sdk)
set_target_properties(${EXECUTABLE_GRAPHICS_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_GRAPHICS_FILE_NAME})
target_compile_options(${EXECUTABLE_GRAPHICS_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
@ -626,17 +677,17 @@ target_compile_options(${EXECUTABLE_GRAPHICS_NAME} PUBLIC
set_target_properties(${EXECUTABLE_GRAPHICS_NAME} PROPERTIES
SUFFIX ".out"
LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_GRAPHICS_NAME}.map"
LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_GRAPHICS_FILE_NAME}.map"
CXX_STANDARD 11
C_STANDARD 99
)
add_custom_command(TARGET ${EXECUTABLE_GRAPHICS_NAME}
POST_BUILD
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_GRAPHICS_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_GRAPHICS_NAME}.out "${EXECUTABLE_GRAPHICS_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_GRAPHICS_NAME}.out "${EXECUTABLE_GRAPHICS_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_GRAPHICS_NAME}"
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_GRAPHICS_FILE_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_GRAPHICS_FILE_NAME}.out "${EXECUTABLE_GRAPHICS_FILE_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_GRAPHICS_FILE_NAME}.out "${EXECUTABLE_GRAPHICS_FILE_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_GRAPHICS_FILE_NAME}"
)
# FLASH
@ -675,3 +726,4 @@ elseif(USE_OPENOCD)
)
endif()

View File

@ -116,7 +116,7 @@ void AlertNotificationClient::OnNotification(ble_gap_event *event) {
char *s = (char *) &data[3];
auto messageSize = min(maxMessageSize, (bufferSize-3));
for (int i = 0; i < messageSize-1; i++) {
for (uint i = 0; i < messageSize-1; i++) {
if (s[i] == 0x00) {
s[i] = 0x0A;
}

View File

@ -26,8 +26,8 @@ void AlertNotificationService::Init() {
ASSERT(res == 0);
}
AlertNotificationService::AlertNotificationService ( Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager ) : m_systemTask{systemTask}, m_notificationManager{notificationManager},
characteristicDefinition{
AlertNotificationService::AlertNotificationService ( System::SystemTask& systemTask, NotificationManager& notificationManager )
: characteristicDefinition{
{
.uuid = (ble_uuid_t *) &ansCharUuid,
.access_cb = AlertNotificationCallback,
@ -48,8 +48,7 @@ AlertNotificationService::AlertNotificationService ( Pinetime::System::SystemTas
{
0
},
}
{
}, m_systemTask{systemTask}, m_notificationManager{notificationManager} {
}
int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle,
@ -67,7 +66,7 @@ int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle
char *s = (char *) &data[3];
auto messageSize = min(maxMessageSize, (bufferSize-3));
for (int i = 0; i < messageSize-1; i++) {
for (uint i = 0; i < messageSize-1; i++) {
if (s[i] == 0x00) {
s[i] = 0x0A;
}

View File

@ -57,7 +57,7 @@ int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handl
return 0;
}
CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) : m_dateTimeController{dateTimeController},
CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) :
characteristicDefinition{
{
.uuid = (ble_uuid_t *) &ctChrUuid,
@ -80,8 +80,7 @@ CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) : m_dateTim
{
0
},
}
{
}, m_dateTimeController{dateTimeController} {
}

View File

@ -8,6 +8,8 @@ constexpr ble_uuid16_t DeviceInformationService::serialNumberUuid;
constexpr ble_uuid16_t DeviceInformationService::fwRevisionUuid;
constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid;
constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid;
constexpr ble_uuid16_t DeviceInformationService::swRevisionUuid;
int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
auto deviceInformationService = static_cast<DeviceInformationService*>(arg);
@ -44,6 +46,9 @@ int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16
case hwRevisionId:
str = hwRevision;
break;
case swRevisionId:
str = swRevision;
break;
default:
return BLE_ATT_ERR_UNLIKELY;
}
@ -84,6 +89,12 @@ DeviceInformationService::DeviceInformationService() :
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
},
{
.uuid = (ble_uuid_t *) &swRevisionUuid,
.access_cb = DeviceInformationCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
},
{
0
}

View File

@ -3,6 +3,7 @@
#include <array>
#include <host/ble_gap.h>
#include <Version.h>
namespace Pinetime {
namespace Controllers {
@ -21,12 +22,15 @@ namespace Pinetime {
static constexpr uint16_t serialNumberId {0x2a25};
static constexpr uint16_t fwRevisionId {0x2a26};
static constexpr uint16_t hwRevisionId {0x2a27};
static constexpr uint16_t swRevisionId {0x2a28};
static constexpr const char* manufacturerName = "PINE64";
static constexpr const char* modelNumber = "PineTime";
static constexpr const char* hwRevision = "1.0.0";
static constexpr const char* serialNumber = "0";
static constexpr const char* fwRevision = Version::VersionString();
static constexpr const char* swRevision = "InfiniTime";
static constexpr char* manufacturerName = "Codingfield";
static constexpr char* modelNumber = "1";
static constexpr char* serialNumber = "9.8.7.6.5.4";
static constexpr char* fwRevision = "0.7.0";
static constexpr char* hwRevision = "1.0.0";
static constexpr ble_uuid16_t deviceInfoUuid {
.u { .type = BLE_UUID_TYPE_16 },
@ -58,7 +62,12 @@ namespace Pinetime {
.value = hwRevisionId
};
struct ble_gatt_chr_def characteristicDefinition[6];
static constexpr ble_uuid16_t swRevisionUuid {
.u {.type = BLE_UUID_TYPE_16},
.value = swRevisionId
};
struct ble_gatt_chr_def characteristicDefinition[7];
struct ble_gatt_svc_def serviceDefinition[2];

View File

@ -394,14 +394,14 @@ void DfuService::DfuImage::WriteMagicNumber() {
}
void DfuService::DfuImage::Erase() {
for (int erased = 0; erased < maxSize; erased += 0x1000) {
for (size_t erased = 0; erased < maxSize; erased += 0x1000) {
spiNorFlash.SectorErase(writeOffset + erased);
}
}
bool DfuService::DfuImage::Validate() {
uint32_t chunkSize = 200;
int currentOffset = 0;
size_t currentOffset = 0;
uint16_t crc = 0;
bool first = true;

View File

@ -0,0 +1,129 @@
#include <SystemTask/SystemTask.h>
#include "MusicService.h"
int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
auto musicService = static_cast<Pinetime::Controllers::MusicService*>(arg);
return musicService->OnCommand(conn_handle, attr_handle, ctxt);
}
Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask &system) : m_system(system)
{
msUuid.value[11] = msId[0];
msUuid.value[12] = msId[1];
msEventCharUuid.value[11] = msEventCharId[0];
msEventCharUuid.value[12] = msEventCharId[1];
msStatusCharUuid.value[11] = msStatusCharId[0];
msStatusCharUuid.value[12] = msStatusCharId[1];
msTrackCharUuid.value[11] = msTrackCharId[0];
msTrackCharUuid.value[12] = msTrackCharId[1];
msArtistCharUuid.value[11] = msArtistCharId[0];
msArtistCharUuid.value[12] = msArtistCharId[1];
msAlbumCharUuid.value[11] = msAlbumCharId[0];
msAlbumCharUuid.value[12] = msAlbumCharId[1];
characteristicDefinition[0] = { .uuid = (ble_uuid_t*)(&msEventCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY,
.val_handle = &m_eventHandle
};
characteristicDefinition[1] = { .uuid = (ble_uuid_t*)(&msStatusCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[2] = { .uuid = (ble_uuid_t*)(&msTrackCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[3] = { .uuid = (ble_uuid_t*)(&msArtistCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[4] = { .uuid = (ble_uuid_t*)(&msAlbumCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[5] = {0};
serviceDefinition[0] = {
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &msUuid,
.characteristics = characteristicDefinition
};
serviceDefinition[1] = {0};
m_artist = "Waiting for";
m_album = "";
m_track = "track information...";
}
void Pinetime::Controllers::MusicService::Init()
{
int res = 0;
res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0);
res = ble_gatts_add_svcs(serviceDefinition);
ASSERT(res == 0);
}
int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt) {
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
uint8_t data[notifSize + 1];
data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data);
char *s = (char *) &data[0];
NRF_LOG_INFO("DATA : %s", s);
if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msArtistCharUuid) == 0) {
m_artist = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msTrackCharUuid) == 0) {
m_track = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msAlbumCharUuid) == 0) {
m_album = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msStatusCharUuid) == 0) {
m_status = s[0];
}
}
return 0;
}
std::string Pinetime::Controllers::MusicService::album()
{
return m_album;
}
std::string Pinetime::Controllers::MusicService::artist()
{
return m_artist;
}
std::string Pinetime::Controllers::MusicService::track()
{
return m_track;
}
unsigned char Pinetime::Controllers::MusicService::status()
{
return m_status;
}
void Pinetime::Controllers::MusicService::event(char event)
{
auto *om = ble_hs_mbuf_from_flat(&event, 1);
uint16_t connectionHandle = m_system.nimble().connHandle();
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return;
}
ble_gattc_notify_custom(connectionHandle, m_eventHandle, om);
}

View File

@ -0,0 +1,92 @@
#pragma once
#include <cstdint>
#include <array>
#include <host/ble_gap.h>
#include <host/ble_uuid.h>
#include <string>
//c7e50000-78fc-48fe-8e23-43b37a1942d0
#define MUSIC_SERVICE_UUID_BASE {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0xe5, 0xc7}
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class MusicService {
public:
MusicService(Pinetime::System::SystemTask &system);
void Init();
int OnCommand(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt);
std::string artist();
std::string track();
std::string album();
unsigned char status();
void event(char event);
static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00;
static const char EVENT_MUSIC_PAUSE = 0x01;
static const char EVENT_MUSIC_NEXT = 0x03;
static const char EVENT_MUSIC_PREV = 0x04;
static const char EVENT_MUSIC_VOLUP = 0x05;
static const char EVENT_MUSIC_VOLDOWN = 0x06;
static const char STATUS_MUSIC_PAUSED = 0x00;
static const char STATUS_MUSIC_PLAYING = 0x01;
private:
static constexpr uint8_t msId[2] = {0x00, 0x01};
static constexpr uint8_t msEventCharId[2] = {0x00, 0x02};
static constexpr uint8_t msStatusCharId[2] = {0x00, 0x03};
static constexpr uint8_t msArtistCharId[2] = {0x00, 0x04};
static constexpr uint8_t msTrackCharId[2] = {0x00, 0x05};
static constexpr uint8_t msAlbumCharId[2] = {0x00, 0x06};
ble_uuid128_t msUuid {
.u = { .type = BLE_UUID_TYPE_128 },
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msEventCharUuid {
.u = { .type = BLE_UUID_TYPE_128 },
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msStatusCharUuid {
.u = { .type = BLE_UUID_TYPE_128 },
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msArtistCharUuid {
.u = { .type = BLE_UUID_TYPE_128 },
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msTrackCharUuid {
.u = { .type = BLE_UUID_TYPE_128 },
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msAlbumCharUuid {
.u = { .type = BLE_UUID_TYPE_128 },
.value = MUSIC_SERVICE_UUID_BASE
};
struct ble_gatt_chr_def characteristicDefinition[6];
struct ble_gatt_svc_def serviceDefinition[2];
uint16_t m_eventHandle;
std::string m_artist;
std::string m_album;
std::string m_track;
unsigned char m_status;
Pinetime::System::SystemTask& m_system;
};
}
}

View File

@ -6,6 +6,7 @@
#include <hal/nrf_rtc.h>
#include "NimbleController.h"
#include "MusicService.h"
#include <services/gatt/ble_svc_gatt.h>
#include <services/gap/ble_svc_gap.h>
#include <host/util/util.h>
@ -35,7 +36,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
currentTimeClient{dateTimeController},
anService{systemTask, notificationManager},
alertNotificationClient{systemTask, notificationManager},
currentTimeService{dateTimeController} {
currentTimeService{dateTimeController},
musicService{systemTask} {
}
@ -80,6 +82,7 @@ void NimbleController::Init() {
deviceInformationService.Init();
currentTimeClient.Init();
currentTimeService.Init();
musicService.Init();
anService.Init();
@ -104,7 +107,7 @@ void NimbleController::Init() {
void NimbleController::StartAdvertising() {
if(ble_gap_adv_active()) return;
ble_svc_gap_device_name_set("Pinetime-JF");
ble_svc_gap_device_name_set(deviceName);
/* set adv parameters */
struct ble_gap_adv_params adv_params;
@ -132,18 +135,17 @@ void NimbleController::StartAdvertising() {
fields.uuids128_is_complete = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
rsp_fields.name = (uint8_t *)"Pinetime-JF";
rsp_fields.name_len = strlen("Pinetime-JF");
rsp_fields.name = (uint8_t *)deviceName;
rsp_fields.name_len = strlen(deviceName);
rsp_fields.name_is_complete = 1;
int res;
res = ble_gap_adv_set_fields(&fields);
ble_gap_adv_set_fields(&fields);
// ASSERT(res == 0); // TODO this one sometimes fails with error 22 (notsync)
res = ble_gap_adv_rsp_set_fields(&rsp_fields);
ble_gap_adv_rsp_set_fields(&rsp_fields);
// ASSERT(res == 0);
res = ble_gap_adv_start(addrType, NULL, 180000,
ble_gap_adv_start(addrType, NULL, 180000,
&adv_params, GAPEventCallback, this);
// ASSERT(res == 0);// TODO I've disabled these ASSERT as they sometime asserts and reset the mcu.
// For now, the advertising is restarted as soon as it ends. There may be a race condition
@ -326,5 +328,7 @@ void NimbleController::StartDiscovery() {
}
uint16_t NimbleController::connHandle() {
return connectionHandle;
}

View File

@ -7,6 +7,7 @@
#include "CurrentTimeClient.h"
#include "DfuService.h"
#include "CurrentTimeService.h"
#include "MusicService.h"
#include <host/ble_gap.h>
namespace Pinetime {
@ -15,6 +16,7 @@ namespace Pinetime {
}
namespace Controllers {
class DateTime;
class NimbleController {
public:
@ -35,8 +37,13 @@ namespace Pinetime {
uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
void StartDiscovery();
Pinetime::Controllers::MusicService& music() {return musicService;};
uint16_t connHandle();
private:
static constexpr char* deviceName = "Pinetime-JF";
static constexpr const char* deviceName = "InfiniTime";
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::Ble& bleController;
DateTime& dateTimeController;
@ -49,9 +56,10 @@ namespace Pinetime {
AlertNotificationService anService;
AlertNotificationClient alertNotificationClient;
CurrentTimeService currentTimeService;
MusicService musicService;
uint8_t addrType; // 1 = Random, 0 = PUBLIC
uint16_t connectionHandle;
uint16_t connectionHandle = 0;
ble_uuid128_t dfuServiceUuid {
.u { .type = BLE_UUID_TYPE_128},

View File

@ -0,0 +1,20 @@
#include <drivers/InternalFlash.h>
#include <hal/nrf_rtc.h>
#include "FirmwareValidator.h"
using namespace Pinetime::Controllers;
bool FirmwareValidator::IsValidated() const {
auto* imageOkPtr = reinterpret_cast<uint32_t *>(validBitAdress);
return (*imageOkPtr) == validBitValue;
}
void FirmwareValidator::Validate() {
if(!IsValidated())
Pinetime::Drivers::InternalFlash::WriteWord(validBitAdress, validBitValue);
}
void FirmwareValidator::Reset() {
NVIC_SystemReset();
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Controllers {
class FirmwareValidator {
public:
void Validate();
bool IsValidated() const;
void Reset();
private:
static constexpr uint32_t validBitAdress {0x7BFE8};
static constexpr uint32_t validBitValue {1};
};
}
}

7
src/DisplayApp/Apps.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
namespace Pinetime {
namespace Applications {
enum class Apps {None, Launcher, Clock, SysInfo, Meter, Gauge, Brightness, Music, FirmwareValidation, Paint};
}
}

View File

@ -2,21 +2,22 @@
#include <FreeRTOS.h>
#include <task.h>
#include <libraries/log/nrf_log.h>
#include <boards.h>
#include <nrf_font.h>
#include <queue.h>
#include <Components/DateTime/DateTimeController.h>
#include <drivers/Cst816s.h>
#include <string>
#include <lvgl/lvgl.h>
#include <DisplayApp/Screens/Tile.h>
#include <DisplayApp/Screens/Message.h>
#include <DisplayApp/Screens/Meter.h>
#include <DisplayApp/Screens/Gauge.h>
#include <DisplayApp/Screens/Brightness.h>
#include <DisplayApp/Screens/ScreenList.h>
#include <DisplayApp/Screens/SystemInfo.h>
#include <DisplayApp/Screens/Music.h>
#include <Components/Ble/NotificationManager.h>
#include <DisplayApp/Screens/FirmwareUpdate.h>
#include <DisplayApp/Screens/ApplicationList.h>
#include <DisplayApp/Screens/FirmwareValidation.h>
#include <DisplayApp/Screens/InfiniPaint.h>
#include "../SystemTask/SystemTask.h"
using namespace Pinetime::Applications;
@ -79,6 +80,9 @@ void DisplayApp::Refresh() {
RunningState();
queueTimeout = 20;
break;
default:
queueTimeout = portMAX_DELAY;
break;
}
Messages msg;
@ -91,14 +95,10 @@ void DisplayApp::Refresh() {
vTaskDelay(100);
}
lcd.DisplayOff();
lcd.Sleep();
touchPanel.Sleep();
systemTask.PushMessage(System::SystemTask::Messages::OnDisplayTaskSleeping);
state = States::Idle;
break;
case Messages::GoToRunning:
lcd.Wakeup();
touchPanel.Wakeup();
lcd.DisplayOn();
brightnessController.Restore();
state = States::Running;
@ -168,6 +168,15 @@ void DisplayApp::Refresh() {
break;
}
}
if(state != States::Idle && touchMode == TouchModes::Polling) {
auto info = touchPanel.GetTouchInfo();
if(info.action == 2) {// 2 = contact
if(!currentScreen->OnTouchEvent(info.x, info.y)) {
lvgl.SetNewTapEvent(info.x, info.y);
}
}
}
}
void DisplayApp::RunningState() {
@ -179,16 +188,19 @@ void DisplayApp::RunningState() {
onClockApp = false;
switch(nextApp) {
case Apps::None:
case Apps::Launcher: currentScreen.reset(new Screens::Tile(this)); break;
case Apps::Launcher: currentScreen.reset(new Screens::ApplicationList(this)); break;
case Apps::Clock:
currentScreen.reset(new Screens::Clock(this, dateTimeController, batteryController, bleController));
onClockApp = true;
break;
// case Apps::Test: currentScreen.reset(new Screens::Message(this)); break;
case Apps::SysInfo: currentScreen.reset(new Screens::ScreenList(this, dateTimeController, batteryController, brightnessController, bleController, watchdog)); break;
case Apps::SysInfo: currentScreen.reset(new Screens::SystemInfo(this, dateTimeController, batteryController, brightnessController, bleController, watchdog)); break;
case Apps::Meter: currentScreen.reset(new Screens::Meter(this)); break;
case Apps::Gauge: currentScreen.reset(new Screens::Gauge(this)); break;
case Apps::Paint: currentScreen.reset(new Screens::InfiniPaint(this, lvgl)); break;
case Apps::Brightness : currentScreen.reset(new Screens::Brightness(this, brightnessController)); break;
case Apps::Music : currentScreen.reset(new Screens::Music(this, systemTask.nimble().music())); break;
case Apps::FirmwareValidation: currentScreen.reset(new Screens::FirmwareValidation(this, validator)); break;
}
nextApp = Apps::None;
}
@ -214,7 +226,8 @@ TouchEvents DisplayApp::OnTouchEvent() {
if(info.isTouch) {
switch(info.gesture) {
case Pinetime::Drivers::Cst816S::Gestures::SingleTap:
lvgl.SetNewTapEvent(info.x, info.y);
if(touchMode == TouchModes::Gestures)
lvgl.SetNewTapEvent(info.x, info.y);
return TouchEvents::Tap;
case Pinetime::Drivers::Cst816S::Gestures::LongPress:
return TouchEvents::LongTap;
@ -236,7 +249,7 @@ TouchEvents DisplayApp::OnTouchEvent() {
return TouchEvents::None;
}
void DisplayApp::StartApp(DisplayApp::Apps app) {
void DisplayApp::StartApp(Apps app) {
nextApp = app;
}
@ -252,3 +265,7 @@ void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) {
}
}
void DisplayApp::SetTouchMode(DisplayApp::TouchModes mode) {
touchMode = mode;
}

View File

@ -17,7 +17,9 @@
#include <drivers/Watchdog.h>
#include <DisplayApp/Screens/Modal.h>
#include <Components/Ble/NotificationManager.h>
#include <Components/FirmwareValidator/FirmwareValidator.h>
#include "TouchEvents.h"
#include "Apps.h"
namespace Pinetime {
@ -28,11 +30,11 @@ namespace Pinetime {
class DisplayApp {
public:
enum class States {Idle, Running};
enum class Messages : uint8_t {GoToSleep, GoToRunning, UpdateDateTime, UpdateBleConnection, UpdateBatteryLevel, TouchEvent, SwitchScreen,ButtonPushed,
NewNotification, BleFirmwareUpdateStarted, BleFirmwareUpdateFinished
};
enum class FullRefreshDirections { None, Up, Down };
enum class Messages : uint8_t {GoToSleep, GoToRunning, UpdateDateTime, UpdateBleConnection, UpdateBatteryLevel, TouchEvent, ButtonPushed,
NewNotification, BleFirmwareUpdateStarted };
enum class FullRefreshDirections { None, Up, Down };
enum class TouchModes { Gestures, Polling };
DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &,
Controllers::Battery &batteryController, Controllers::Ble &bleController,
@ -42,10 +44,11 @@ namespace Pinetime {
void Start();
void PushMessage(Messages msg);
enum class Apps {None, Launcher, Clock, SysInfo, Meter, Gauge, Brightness};
void StartApp(Apps app);
void SetFullRefresh(FullRefreshDirections direction);
void SetTouchMode(TouchModes mode);
private:
TaskHandle_t taskHandle;
static void Process(void* instance);
@ -80,6 +83,8 @@ namespace Pinetime {
Controllers::BrightnessController brightnessController;
std::unique_ptr<Screens::Modal> modal;
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::FirmwareValidator validator;
TouchModes touchMode = TouchModes::Gestures;
};
}
}

View File

@ -10,7 +10,7 @@
* Bpp : 1 bit-per-pixel
* Do not enable font compression and horizontal subpixel hinting
* Load the file `JetBrainsMono-Bold.woff` and specify the following range : `0x20-0x7f`
* Add a 2nd font, load the file `FontAwesome5-Solid+Brands+Regular.woff` and specify the following range : `0xf293, 0xf294, 0xf244, 0xf240, 0xf242, 0xf243, 0xf241, 0xf54b, 0xf21e, 0xf1e6, 0xf54b, 0xf017, 0xf129, 0xf03a, 0xf185`
* Add a 2nd font, load the file `FontAwesome5-Solid+Brands+Regular.woff` and specify the following range : `0xf293, 0xf294, 0xf244, 0xf240, 0xf242, 0xf243, 0xf241, 0xf54b, 0xf21e, 0xf1e6, 0xf54b, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf069, 0xf1fc`
* Click on Convert, and download the file `jetbrains_mono_bold_20.c` and copy it in `src/DisplayApp/Fonts`
Add new symbols:

View File

@ -433,6 +433,15 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
/* U+7E "~" */
0x78, 0xff, 0x3c, 0xff, 0x1e,
/* U+F001 "" */
0x0, 0x0, 0x70, 0x0, 0x7f, 0x0, 0x3f, 0xf0,
0x1f, 0xff, 0x7, 0xff, 0xf0, 0x7f, 0xff, 0x7,
0xfc, 0x70, 0x7e, 0x7, 0x7, 0x0, 0x70, 0x70,
0x7, 0x7, 0x0, 0x70, 0x70, 0x7, 0x7, 0x0,
0x70, 0x70, 0x7f, 0x7, 0xf, 0xf7, 0xf0, 0xff,
0xff, 0x7, 0xef, 0xf0, 0x0, 0xff, 0x0, 0x3,
0xc0, 0x0,
/* U+F017 "" */
0x3, 0xf8, 0x1, 0xff, 0xc0, 0x7f, 0xfc, 0x1f,
0xff, 0xc7, 0xf1, 0xfc, 0xfe, 0x3f, 0x9f, 0xc7,
@ -449,6 +458,14 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0xf, 0x0, 0x0, 0xf3, 0xff, 0xff, 0x3f, 0xff,
0xf0, 0x0, 0x0,
/* U+F069 "" */
0x0, 0xe0, 0x0, 0x1c, 0x0, 0x3, 0x80, 0x0,
0x70, 0x6, 0xe, 0xc, 0xf1, 0xc7, 0x9f, 0xbb,
0xf1, 0xff, 0xfc, 0xf, 0xfe, 0x0, 0x7f, 0x0,
0xf, 0xe0, 0x7, 0xff, 0x3, 0xff, 0xf8, 0xfd,
0xdf, 0x9e, 0x38, 0xf3, 0x7, 0x6, 0x0, 0xe0,
0x0, 0x1c, 0x0, 0x3, 0x80, 0x0, 0x70, 0x0,
/* U+F129 "" */
0x3c, 0x7e, 0x7e, 0x7e, 0x3c, 0x0, 0x0, 0xfc,
0xfc, 0xfc, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
@ -470,6 +487,14 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0x83, 0xfe, 0x3, 0xf8, 0x1, 0xc0, 0x3, 0x80,
0x7, 0x0, 0xe, 0x0,
/* U+F1FC "" */
0x0, 0x0, 0xf0, 0x0, 0x1f, 0x0, 0x3, 0xf0,
0x0, 0x7e, 0x0, 0xf, 0xe0, 0x3, 0xfc, 0x0,
0x7f, 0xc0, 0xf, 0xf8, 0x0, 0xff, 0x80, 0x1f,
0xf0, 0x0, 0xfe, 0x0, 0xf, 0xe0, 0xe, 0x7c,
0x1, 0xf8, 0x0, 0x9f, 0xc0, 0xf, 0xfc, 0x0,
0x7f, 0xc0, 0x7, 0xf8, 0x0, 0x1f, 0x0, 0x0,
/* U+F21E "" */
0x1e, 0x7, 0x83, 0xf9, 0xfe, 0x7f, 0xff, 0xef,
0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xfc,
@ -526,6 +551,15 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0x81, 0xf8, 0x6d, 0x99, 0x9a, 0x36, 0x7, 0x80,
0xe0, 0x18, 0x2, 0x0, 0x0,
/* U+F3FD "" */
0x0, 0xfe, 0x0, 0x7, 0xff, 0x0, 0x3f, 0xbf,
0x80, 0xfe, 0x2f, 0x83, 0xfe, 0xcf, 0x8f, 0x3f,
0x27, 0x9e, 0x7e, 0x4f, 0x3f, 0xfc, 0xfe, 0xff,
0xf3, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xcf, 0xff,
0xfe, 0x3f, 0xfe, 0x78, 0x3c, 0xff, 0xf0, 0x7f,
0xdf, 0xe0, 0xff, 0x3f, 0xff, 0xfe, 0x3f, 0xff,
0xf8,
/* U+F54B "" */
0x0, 0xf, 0xf8, 0x1, 0xdf, 0xff, 0x1, 0xef,
0xff, 0xc0, 0xf7, 0xff, 0xf0, 0x7b, 0xff, 0xf8,
@ -534,7 +568,16 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0x0, 0x0, 0x0, 0x7, 0xf8, 0x0, 0xf, 0xfe,
0x3, 0xbf, 0xff, 0x83, 0xdf, 0xff, 0xc1, 0xef,
0xff, 0xe0, 0xf7, 0xff, 0xe0, 0x3b, 0xff, 0xe0,
0x0, 0x7f, 0xc0, 0x0
0x0, 0x7f, 0xc0, 0x0,
/* U+F560 "" */
0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0xf, 0x0,
0x1, 0xf0, 0x8, 0x3e, 0x1, 0xc7, 0xc4, 0x1e,
0xf8, 0xe1, 0xff, 0x1f, 0xf, 0xe3, 0xf0, 0x7c,
0x7e, 0x23, 0x8f, 0xc7, 0x11, 0xf8, 0xf8, 0x3f,
0xf, 0xc7, 0xe0, 0x7e, 0xfc, 0x3, 0xff, 0x80,
0x1f, 0xf0, 0x0, 0xfe, 0x0, 0x7, 0xc0, 0x0,
0x38, 0x0, 0x1, 0x0, 0x0
};
@ -639,20 +682,25 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 1423, .adv_w = 192, .box_w = 3, .box_h = 18, .ofs_x = 5, .ofs_y = -2},
{.bitmap_index = 1430, .adv_w = 192, .box_w = 10, .box_h = 18, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 1453, .adv_w = 192, .box_w = 10, .box_h = 4, .ofs_x = 1, .ofs_y = 5},
{.bitmap_index = 1458, .adv_w = 320, .box_w = 19, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 1506, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 1549, .adv_w = 120, .box_w = 8, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1568, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 1618, .adv_w = 240, .box_w = 15, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1654, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 1697, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1735, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1773, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1811, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1849, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1887, .adv_w = 280, .box_w = 15, .box_h = 20, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 1925, .adv_w = 200, .box_w = 11, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 1954, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2}
{.bitmap_index = 1458, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 1508, .adv_w = 320, .box_w = 19, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 1556, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 1599, .adv_w = 320, .box_w = 19, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 1647, .adv_w = 120, .box_w = 8, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1666, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 1716, .adv_w = 240, .box_w = 15, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1752, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 1800, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 1843, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1881, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1919, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1957, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 1995, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 2033, .adv_w = 280, .box_w = 15, .box_h = 20, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 2071, .adv_w = 200, .box_w = 11, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 2100, .adv_w = 360, .box_w = 23, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 2149, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 2209, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3}
};
/*---------------------
@ -660,8 +708,9 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
*--------------------*/
static const uint16_t unicode_list_1[] = {
0x0, 0x23, 0x112, 0x16e, 0x1cf, 0x207, 0x229, 0x22a,
0x22b, 0x22c, 0x22d, 0x27c, 0x27d, 0x534
0x0, 0x16, 0x39, 0x68, 0x128, 0x184, 0x1e5, 0x1fb,
0x21d, 0x23f, 0x240, 0x241, 0x242, 0x243, 0x292, 0x293,
0x3fc, 0x54a, 0x55f
};
/*Collect the unicode lists and glyph_id offsets*/
@ -672,8 +721,8 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
},
{
.range_start = 61463, .range_length = 1333, .glyph_id_start = 96,
.unicode_list = unicode_list_1, .glyph_id_ofs_list = NULL, .list_length = 14, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.range_start = 61441, .range_length = 1376, .glyph_id_start = 96,
.unicode_list = unicode_list_1, .glyph_id_ofs_list = NULL, .list_length = 19, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};

View File

@ -6,10 +6,6 @@
#include <drivers/St7789.h>
#include <drivers/Cst816s.h>
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
namespace Pinetime {
namespace Components {
class LittleVgl {

View File

@ -0,0 +1,82 @@
#include <libs/lvgl/lvgl.h>
#include <DisplayApp/DisplayApp.h>
#include <functional>
#include "ApplicationList.h"
#include "Tile.h"
#include "Symbols.h"
using namespace Pinetime::Applications::Screens;
ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp *app) :
Screen(app),
screens{app, {
[this]() -> std::unique_ptr<Screen> { return CreateScreen1(); },
[this]() -> std::unique_ptr<Screen> { return CreateScreen2(); },
//[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
}
} {}
ApplicationList::~ApplicationList() {
lv_obj_clean(lv_scr_act());
}
bool ApplicationList::Refresh() {
if(running)
running = screens.Refresh();
return running;
}
bool ApplicationList::OnButtonPushed() {
running = false;
app->StartApp(Apps::Clock);
return true;
}
bool ApplicationList::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return screens.OnTouchEvent(event);
}
std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
std::array<Screens::Tile::Applications, 6> applications {
{{Symbols::clock, Apps::Clock},
{Symbols::music, Apps::Music},
{Symbols::sun, Apps::Brightness},
{Symbols::list, Apps::SysInfo},
{Symbols::check, Apps::FirmwareValidation},
{Symbols::none, Apps::None}
}
};
return std::unique_ptr<Screen>(new Screens::Tile(app, applications));
}
std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
std::array<Screens::Tile::Applications, 6> applications {
{{Symbols::tachometer, Apps::Gauge},
{Symbols::asterisk, Apps::Meter},
{Symbols::paintbrush, Apps::Paint},
{Symbols::none, Apps::None},
{Symbols::none, Apps::None},
{Symbols::none, Apps::None}
}
};
return std::unique_ptr<Screen>(new Screens::Tile(app, applications));
}
std::unique_ptr<Screen> ApplicationList::CreateScreen3() {
std::array<Screens::Tile::Applications, 6> applications {
{{"A", Apps::Meter},
{"B", Apps::Gauge},
{"C", Apps::Clock},
{"D", Apps::Music},
{"E", Apps::SysInfo},
{"F", Apps::Brightness}
}
};
return std::unique_ptr<Screen>(new Screens::Tile(app, applications));
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <vector>
#include <Components/Ble/NimbleController.h>
#include "Screen.h"
#include "Label.h"
#include "ScreenList.h"
#include "Gauge.h"
#include "Meter.h"
#include <functional>
namespace Pinetime {
namespace Applications {
namespace Screens {
class ApplicationList : public Screen {
public:
explicit ApplicationList(DisplayApp* app);
~ApplicationList() override;
bool Refresh() override;
bool OnButtonPushed() override;
bool OnTouchEvent(TouchEvents event) override;
private:
bool running = true;
ScreenList<2> screens;
std::unique_ptr<Screen> CreateScreen1();
std::unique_ptr<Screen> CreateScreen2();
std::unique_ptr<Screen> CreateScreen3();
};
}
}
}

View File

@ -121,13 +121,12 @@ bool Clock::Refresh() {
auto hour = time.hours().count();
auto minute = time.minutes().count();
auto second = time.seconds().count();
char minutesChar[3];
sprintf(minutesChar, "%02d", minute);
sprintf(minutesChar, "%02d", static_cast<int>(minute));
char hoursChar[3];
sprintf(hoursChar, "%02d", hour);
sprintf(hoursChar, "%02d", static_cast<int>(hour));
char timeStr[6];
sprintf(timeStr, "%c%c:%c%c", hoursChar[0],hoursChar[1],minutesChar[0], minutesChar[1]);

View File

@ -57,7 +57,7 @@ namespace Pinetime {
Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
uint8_t currentDay = 0;
DirtyValue<uint8_t> batteryPercentRemaining {0};
DirtyValue<float> batteryPercentRemaining {0};
DirtyValue<bool> bleState {false};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
DirtyValue<uint32_t> stepCount {0};

View File

@ -0,0 +1,64 @@
#include <libs/lvgl/lvgl.h>
#include <libraries/log/nrf_log.h>
#include "DropDownDemo.h"
#include "../DisplayApp.h"
using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_extrabold_compressed;
extern lv_font_t jetbrains_mono_bold_20;
DropDownDemo::DropDownDemo(Pinetime::Applications::DisplayApp *app) : Screen(app) {
// Create the dropdown object, with many item, and fix its height
ddlist = lv_ddlist_create(lv_scr_act(), NULL);
lv_ddlist_set_options(ddlist, "Apple\n"
"Banana\n"
"Orange\n"
"Melon\n"
"Grape\n"
"Raspberry\n"
"A\n"
"B\n"
"C\n"
"D\n"
"E");
lv_ddlist_set_fix_width(ddlist, 150);
lv_ddlist_set_draw_arrow(ddlist, true);
lv_ddlist_set_fix_height(ddlist, 150);
lv_obj_align(ddlist, NULL, LV_ALIGN_IN_TOP_MID, 0, 20);
}
DropDownDemo::~DropDownDemo() {
// Reset the touchmode
app->SetTouchMode(DisplayApp::TouchModes::Gestures);
lv_obj_clean(lv_scr_act());
}
bool DropDownDemo::Refresh() {
auto* list = static_cast<lv_ddlist_ext_t *>(ddlist->ext_attr);
// Switch touchmode to Polling if the dropdown is opened. This will allow to scroll inside the
// dropdown while it is opened.
// Disable the polling mode when the dropdown is closed to be able to handle the gestures.
if(list->opened)
app->SetTouchMode(DisplayApp::TouchModes::Polling);
else
app->SetTouchMode(DisplayApp::TouchModes::Gestures);
return running;
}
bool DropDownDemo::OnButtonPushed() {
running = false;
return true;
}
bool DropDownDemo::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
// If the dropdown is opened, notify Display app that it doesn't need to handle the event
// (this will prevent displayApp from going back to the menu or clock scree).
auto* list = static_cast<lv_ddlist_ext_t *>(ddlist->ext_attr);
if(list->opened) {
return true;
} else {
return false;
}
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <cstdint>
#include "Screen.h"
#include <bits/unique_ptr.h>
#include <libs/lvgl/src/lv_core/lv_style.h>
#include <libs/lvgl/src/lv_core/lv_obj.h>
namespace Pinetime {
namespace Applications {
namespace Screens {
class DropDownDemo : public Screen{
public:
DropDownDemo(DisplayApp* app);
~DropDownDemo() override;
bool Refresh() override;
bool OnButtonPushed() override;
bool OnTouchEvent(TouchEvents event) override;
private:
lv_obj_t * ddlist;
bool running = true;
bool isDropDownOpened = false;
};
}
}
}

View File

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

View File

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

View File

@ -19,6 +19,7 @@ Gauge::Gauge(Pinetime::Applications::DisplayApp *app) : Screen(app) {
style.text.color = LV_COLOR_WHITE;
style.line.color = LV_COLOR_RED; /*Line color after the critical value*/
/*Describe the color for the needles*/
needle_colors[0] = LV_COLOR_ORANGE;

View File

@ -0,0 +1,44 @@
#include <libs/lvgl/lvgl.h>
#include <libraries/log/nrf_log.h>
#include "InfiniPaint.h"
#include "../DisplayApp.h"
using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_extrabold_compressed;
extern lv_font_t jetbrains_mono_bold_20;
InfiniPaint::InfiniPaint(Pinetime::Applications::DisplayApp *app, Pinetime::Components::LittleVgl& lvgl) : Screen(app), lvgl{lvgl} {
app->SetTouchMode(DisplayApp::TouchModes::Polling);
std::fill(b, b+bufferSize, LV_COLOR_WHITE);
}
InfiniPaint::~InfiniPaint() {
// Reset the touchmode
app->SetTouchMode(DisplayApp::TouchModes::Gestures);
lv_obj_clean(lv_scr_act());
}
bool InfiniPaint::Refresh() {
return running;
}
bool InfiniPaint::OnButtonPushed() {
running = false;
return true;
}
bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return true;
}
bool InfiniPaint::OnTouchEvent(uint16_t x, uint16_t y) {
lv_area_t area;
area.x1 = x-(width/2);
area.y1 = y-(height/2);
area.x2 = x+(width/2)-1;
area.y2 = y+(height/2)-1;
lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None);
lvgl.FlushDisplay(&area, b);
return true;
}

View File

@ -0,0 +1,35 @@
#pragma once
#include <cstdint>
#include "Screen.h"
#include <bits/unique_ptr.h>
#include <libs/lvgl/src/lv_core/lv_style.h>
#include <libs/lvgl/src/lv_core/lv_obj.h>
#include <drivers/St7789.h>
#include <DisplayApp/LittleVgl.h>
namespace Pinetime {
namespace Applications {
namespace Screens {
class InfiniPaint : public Screen{
public:
InfiniPaint(DisplayApp* app, Pinetime::Components::LittleVgl& lvgl);
~InfiniPaint() override;
bool Refresh() override;
bool OnButtonPushed() override;
bool OnTouchEvent(TouchEvents event) override;
bool OnTouchEvent(uint16_t x, uint16_t y) override;
private:
Pinetime::Components::LittleVgl& lvgl;
static constexpr uint16_t width = 10;
static constexpr uint16_t height = 10;
static constexpr uint16_t bufferSize = width*height;
lv_color_t b[bufferSize];
bool running = true;
};
}
}
}

View File

@ -3,26 +3,13 @@
using namespace Pinetime::Applications::Screens;
Label::Label(const char* text) : text{text} {
}
Label::~Label() {
}
void Label::Refresh() {
}
void Label::Show() {
Label::Label(Pinetime::Applications::DisplayApp *app, const char *text) : Screen(app), text{text} {
label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_align(label, LV_LABEL_ALIGN_LEFT);
lv_obj_set_size(label, 240, 240);
lv_label_set_text(label, text);
}
void Label::Hide() {
Label::~Label() {
lv_obj_clean(lv_scr_act());
}

View File

@ -2,19 +2,18 @@
#include <vector>
#include "Screen.h"
#include <lvgl/lvgl.h>
namespace Pinetime {
namespace Applications {
namespace Screens {
class Label {
public:
Label() = default;
explicit Label(const char* text);
~Label();
void Refresh();
void Hide();
void Show();
class Label : public Screen {
public:
Label(DisplayApp* app, const char* text);
~Label() override;
bool Refresh() override {return false;}
private:
lv_obj_t * label = nullptr;
const char* text = nullptr;

View File

@ -1,81 +0,0 @@
#include <cstdio>
#include <libs/date/includes/date/date.h>
#include <Components/DateTime/DateTimeController.h>
#include <Version.h>
#include <libs/lvgl/src/lv_core/lv_obj.h>
#include <libs/lvgl/src/lv_font/lv_font.h>
#include <libs/lvgl/lvgl.h>
#include <libraries/log/nrf_log.h>
#include "Message.h"
#include <DisplayApp/DisplayApp.h>
using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_bold_20;
static void event_handler(lv_obj_t * obj, lv_event_t event) {
Message* screen = static_cast<Message *>(obj->user_data);
screen->OnObjectEvent(obj, event);
}
Message::Message(DisplayApp* app) : Screen(app) {
backgroundLabel = lv_label_create(lv_scr_act(), NULL);
backgroundLabel->user_data = this;
lv_obj_set_click(backgroundLabel, true);
lv_obj_set_event_cb(backgroundLabel, event_handler);
lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundLabel, 240, 240);
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text(backgroundLabel, "");
button = lv_btn_create(lv_scr_act(), NULL);
lv_obj_set_event_cb(button, event_handler);
lv_obj_align(button, NULL, LV_ALIGN_CENTER, 0, -40);
button->user_data = this;
label = lv_label_create(button, NULL);
lv_label_set_text(label, "Hello!");
labelClick = lv_label_create(lv_scr_act(), NULL);
lv_obj_align(labelClick, button, LV_ALIGN_OUT_BOTTOM_MID, 0, 30);
lv_label_set_text(labelClick, "0");
}
Message::~Message() {
lv_obj_clean(lv_scr_act());
}
bool Message::Refresh() {
if(previousClickCount != clickCount) {
lv_label_set_text_fmt(labelClick, "%d", clickCount);
previousClickCount = clickCount;
}
return running;
}
void Message::OnObjectEvent(lv_obj_t *obj, lv_event_t event) {
if(obj == backgroundLabel) {
if(event == LV_EVENT_CLICKED) {
app->PushMessage(DisplayApp::Messages::SwitchScreen);
NRF_LOG_INFO("SCREEN");
}
return ;
}
if(event == LV_EVENT_CLICKED) {
NRF_LOG_INFO("Clicked");
clickCount++;
}
else if(event == LV_EVENT_VALUE_CHANGED) {
NRF_LOG_INFO("Toggled");
}
}
bool Message::OnButtonPushed() {
running = false;
return true;
}

View File

@ -1,31 +0,0 @@
#pragma once
#include <cstdint>
#include "Screen.h"
#include <bits/unique_ptr.h>
#include <lvgl/src/lv_core/lv_style.h>
namespace Pinetime {
namespace Applications {
namespace Screens {
class Message : public Screen{
public:
explicit Message(DisplayApp* app);
~Message() override;
bool Refresh() override;
bool OnButtonPushed();
void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
private:
lv_obj_t * label;
lv_obj_t* backgroundLabel;
lv_obj_t * button;
lv_obj_t * labelClick;
uint32_t clickCount = 0 ;
uint32_t previousClickCount = 0;
bool running = true;
};
}
}
}

View File

@ -0,0 +1,125 @@
#include <libs/lvgl/lvgl.h>
#include "Music.h"
using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_extrabold_compressed;
extern lv_font_t jetbrains_mono_bold_20;
static void event_handler(lv_obj_t * obj, lv_event_t event)
{
Music* screen = static_cast<Music *>(obj->user_data);
screen->OnObjectEvent(obj, event);
}
Music::Music(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::MusicService &music) : Screen(app), musicService(music) {
lv_obj_t * label;
btnVolDown = lv_btn_create(lv_scr_act(), NULL);
btnVolDown->user_data = this;
lv_obj_set_event_cb(btnVolDown, event_handler);
lv_obj_align(btnVolDown, NULL, LV_ALIGN_IN_TOP_LEFT, 10, 10);
label = lv_label_create(btnVolDown, NULL);
lv_label_set_text(label, "v-");
btnVolUp = lv_btn_create(lv_scr_act(), NULL);
btnVolUp->user_data = this;
lv_obj_set_event_cb(btnVolUp, event_handler);
lv_obj_align(btnVolUp, NULL, LV_ALIGN_IN_TOP_RIGHT, -10, 10);
label = lv_label_create(btnVolUp, NULL);
lv_label_set_text(label, "v+");
btnPrev = lv_btn_create(lv_scr_act(), NULL);
btnPrev->user_data = this;
lv_obj_set_event_cb(btnPrev, event_handler);
lv_obj_set_size(btnPrev, LV_HOR_RES / 4, LV_VER_RES / 4);
lv_obj_align(btnPrev, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 10,-10);
label = lv_label_create(btnPrev, NULL);
lv_label_set_text(label, "<<");
btnPlayPause = lv_btn_create(lv_scr_act(), NULL);
btnPlayPause->user_data = this;
lv_obj_set_event_cb(btnPlayPause, event_handler);
lv_obj_set_size(btnPlayPause, LV_HOR_RES / 4, LV_VER_RES / 4);
lv_obj_align(btnPlayPause, NULL, LV_ALIGN_IN_BOTTOM_MID, 0,-10);
txtPlayPause = lv_label_create(btnPlayPause, NULL);
lv_label_set_text(txtPlayPause, ">");
btnNext = lv_btn_create(lv_scr_act(), NULL);
btnNext->user_data = this;
lv_obj_set_event_cb(btnNext, event_handler);
lv_obj_set_size(btnNext, LV_HOR_RES / 4, LV_VER_RES / 4);
lv_obj_align(btnNext, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -10,-10);
label = lv_label_create(btnNext, NULL);
lv_label_set_text(label, ">>");
txtArtist = lv_label_create(lv_scr_act(), NULL);
lv_label_set_long_mode(txtArtist, LV_LABEL_LONG_SROLL);
lv_obj_align(txtArtist, NULL, LV_ALIGN_IN_LEFT_MID, 0,-20);
lv_label_set_text(txtArtist, "Artist Name");
lv_label_set_align(txtArtist, LV_LABEL_ALIGN_CENTER);
lv_obj_set_width(txtArtist, LV_HOR_RES);
txtTrack = lv_label_create(lv_scr_act(), NULL);
lv_label_set_long_mode(txtTrack, LV_LABEL_LONG_DOT);
lv_obj_align(txtTrack, NULL, LV_ALIGN_IN_LEFT_MID, 0,20);
lv_label_set_text(txtTrack, "This is a very long track name");
lv_label_set_align(txtTrack, LV_LABEL_ALIGN_CENTER);
lv_obj_set_width(txtTrack, LV_HOR_RES);
musicService.event(Controllers::MusicService::EVENT_MUSIC_OPEN);
}
Music::~Music() {
lv_obj_clean(lv_scr_act());
}
bool Music::OnButtonPushed() {
running = false;
return true;
}
bool Music::Refresh() {
if (m_artist != musicService.artist()) {
m_artist = musicService.artist();
lv_label_set_text(txtArtist, m_artist.data());
}
if (m_track != musicService.track()) {
m_track = musicService.track();
lv_label_set_text(txtTrack, m_track.data());
}
if (m_album != musicService.album()) {
m_album = musicService.album();
}
if (m_status != musicService.status()) {
m_status = musicService.status();
}
if (m_status == Pinetime::Controllers::MusicService::STATUS_MUSIC_PLAYING) {
lv_label_set_text(txtPlayPause, "||");
} else {
lv_label_set_text(txtPlayPause, ">");
}
return running;
}
void Music::OnObjectEvent(lv_obj_t* obj, lv_event_t event)
{
if (event == LV_EVENT_CLICKED) {
if (obj == btnVolDown) {
musicService.event(Controllers::MusicService::EVENT_MUSIC_VOLDOWN);
} else if (obj == btnVolUp) {
musicService.event(Controllers::MusicService::EVENT_MUSIC_VOLUP);
} else if (obj == btnPrev) {
musicService.event(Controllers::MusicService::EVENT_MUSIC_PREV);
} else if (obj == btnPlayPause) {
if (m_status == Pinetime::Controllers::MusicService::STATUS_MUSIC_PLAYING) {
musicService.event(Controllers::MusicService::EVENT_MUSIC_PAUSE);
} else {
musicService.event(Controllers::MusicService::EVENT_MUSIC_PLAY);
}
} else if (obj == btnNext) {
musicService.event(Controllers::MusicService::EVENT_MUSIC_NEXT);
}
}
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <cstdint>
#include <chrono>
#include <Components/Gfx/Gfx.h>
#include "Screen.h"
#include <bits/unique_ptr.h>
#include <libs/lvgl/src/lv_core/lv_style.h>
#include <libs/lvgl/src/lv_core/lv_obj.h>
#include <Components/Battery/BatteryController.h>
#include <Components/Ble/BleController.h>
#include "../../Version.h"
#include <Components/Ble/MusicService.h>
#include <string>
namespace Pinetime {
namespace Applications {
namespace Screens {
class Music : public Screen{
public:
Music(DisplayApp* app, Pinetime::Controllers::MusicService &music);
~Music() override;
bool Refresh() override;
bool OnButtonPushed() override;
void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
private:
lv_obj_t * btnPrev;
lv_obj_t * btnPlayPause;
lv_obj_t * btnNext;
lv_obj_t * btnVolDown;
lv_obj_t * btnVolUp;
lv_obj_t * txtArtist;
lv_obj_t * txtTrack;
lv_obj_t * txtPlayPause;
bool running = true;
Pinetime::Controllers::MusicService &musicService;
std::string m_artist;
std::string m_album;
std::string m_track;
unsigned char m_status;
};
}
}
}

View File

@ -1,4 +1,6 @@
#pragma once
#include <cstdint>
#include "../TouchEvents.h"
namespace Pinetime {
@ -18,6 +20,7 @@ namespace Pinetime {
// Return false if the event hasn't been handled by the app, true if it has been handled
virtual bool OnTouchEvent(TouchEvents event) { return false; }
virtual bool OnTouchEvent(uint16_t x, uint16_t y) { return false; }
protected:
DisplayApp* app;

View File

@ -2,40 +2,64 @@
#include <vector>
#include <Components/Ble/NimbleController.h>
#include <functional>
#include "Screen.h"
#include "Label.h"
namespace Pinetime {
namespace Applications {
namespace Screens {
template <size_t N>
class ScreenList : public Screen {
public:
explicit ScreenList(DisplayApp* app,
Pinetime::Controllers::DateTime& dateTimeController,
Pinetime::Controllers::Battery& batteryController,
Pinetime::Controllers::BrightnessController& brightnessController,
Pinetime::Controllers::Ble& bleController,
Pinetime::Drivers::WatchdogView& watchdog);
~ScreenList() override;
bool Refresh() override;
bool OnButtonPushed() override;
bool OnTouchEvent(TouchEvents event) override;
ScreenList(DisplayApp* app, std::array<std::function<std::unique_ptr<Screen>()>, N>&& screens)
: Screen(app), screens{std::move(screens)}, current{this->screens[0]()} {
}
~ScreenList() override {
}
bool Refresh() override {
running = current->Refresh();
return running;
}
bool OnButtonPushed() override {
running = false;
return true;
}
bool OnTouchEvent(TouchEvents event) override {
switch (event) {
case TouchEvents::SwipeDown:
if (screenIndex > 0) {
current.reset(nullptr);
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down);
screenIndex--;
current = screens[screenIndex]();
}
return true;
case TouchEvents::SwipeUp:
if (screenIndex < screens.size() - 1) {
current.reset(nullptr);
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up);
screenIndex++;
current = screens[screenIndex]();
}
return true;
default:
return false;
}
return false;
}
private:
bool running = true;
uint8_t screenIndex = 0;
// TODO choose another container without dynamic alloc
std::vector<Screens::Label> screens;
Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Controllers::Battery& batteryController;
Pinetime::Controllers::BrightnessController& brightnessController;
Pinetime::Controllers::Ble& bleController;
Pinetime::Drivers::WatchdogView& watchdog;
char t1[200];
char t2[200];
char t3[30];
std::array<std::function<std::unique_ptr<Screen>()>, N> screens;
std::unique_ptr<Screen> current;
};
}
}

View File

@ -4,20 +4,26 @@ namespace Pinetime {
namespace Applications {
namespace Screens {
namespace Symbols {
static constexpr char* batteryFull = "\xEF\x89\x80";
static constexpr char* batteryEmpty = "\xEF\x89\x84";
static constexpr char* batteryThreeQuarter = "\xEF\x89\x81";
static constexpr char* batteryHalf = "\xEF\x89\x82";
static constexpr char* batteryOneQuarter = "\xEF\x89\x83";
static constexpr char* heartBeat = "\xEF\x88\x9E";
static constexpr char* bluetoothFull = "\xEF\x8A\x93";
static constexpr char* bluetooth = "\xEF\x8A\x94";
static constexpr char* plug = "\xEF\x87\xA6";
static constexpr char* shoe = "\xEF\x95\x8B";
static constexpr char* clock = "\xEF\x80\x97";
static constexpr char* info = "\xEF\x84\xA9";
static constexpr char* list = "\xEF\x80\xBA";
static constexpr char* sun = "\xEF\x86\x85";
static constexpr const char* none = "";
static constexpr const char* batteryFull = "\xEF\x89\x80";
static constexpr const char* batteryEmpty = "\xEF\x89\x84";
static constexpr const char* batteryThreeQuarter = "\xEF\x89\x81";
static constexpr const char* batteryHalf = "\xEF\x89\x82";
static constexpr const char* batteryOneQuarter = "\xEF\x89\x83";
static constexpr const char* heartBeat = "\xEF\x88\x9E";
static constexpr const char* bluetoothFull = "\xEF\x8A\x93";
static constexpr const char* bluetooth = "\xEF\x8A\x94";
static constexpr const char* plug = "\xEF\x87\xA6";
static constexpr const char* shoe = "\xEF\x95\x8B";
static constexpr const char* clock = "\xEF\x80\x97";
static constexpr const char* info = "\xEF\x84\xA9";
static constexpr const char* list = "\xEF\x80\xBA";
static constexpr const char* sun = "\xEF\x86\x85";
static constexpr const char* check = "\xEF\x95\xA0";
static constexpr const char* music = "\xEF\x80\x81";
static constexpr const char* tachometer = "\xEF\x8F\xBD";
static constexpr const char* asterisk = "\xEF\x81\xA9";
static constexpr const char* paintbrush = "\xEF\x87\xBC";
}
}
}

View File

@ -1,40 +1,61 @@
#include <libs/lvgl/lvgl.h>
#include <DisplayApp/DisplayApp.h>
#include "ScreenList.h"
#include <functional>
#include "SystemInfo.h"
#include "../../Version.h"
#include "Tile.h"
using namespace Pinetime::Applications::Screens;
// TODO this class must be improved to receive the list of "sub screens" via pointer or
// move operation.
// It should accept many type of "sub screen" (it only supports Label for now).
// The number of sub screen it supports must be dynamic.
ScreenList::ScreenList(Pinetime::Applications::DisplayApp *app,
Pinetime::Controllers::DateTime &dateTimeController,
Pinetime::Controllers::Battery& batteryController,
Pinetime::Controllers::BrightnessController& brightnessController,
SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp *app,
Pinetime::Controllers::DateTime &dateTimeController,
Pinetime::Controllers::Battery& batteryController,
Pinetime::Controllers::BrightnessController& brightnessController,
Pinetime::Controllers::Ble& bleController,
Pinetime::Drivers::WatchdogView& watchdog) :
Pinetime::Drivers::WatchdogView& watchdog) :
Screen(app),
dateTimeController{dateTimeController}, batteryController{batteryController},
brightnessController{brightnessController}, bleController{bleController}, watchdog{watchdog} {
screens.reserve(3);
// TODO all of this is far too heavy (string processing). This should be improved.
// TODO the info (battery, time,...) should be updated in the Refresh method.
brightnessController{brightnessController}, bleController{bleController}, watchdog{watchdog},
screens{app, {
[this]() -> std::unique_ptr<Screen> { return CreateScreen1(); },
[this]() -> std::unique_ptr<Screen> { return CreateScreen2(); },
[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
}
} {}
auto batteryPercent = static_cast<int16_t>(batteryController.PercentRemaining());
if(batteryPercent > 100) batteryPercent = 100;
else if(batteryPercent < 0) batteryPercent = 0;
SystemInfo::~SystemInfo() {
lv_obj_clean(lv_scr_act());
}
bool SystemInfo::Refresh() {
screens.Refresh();
return running;
}
bool SystemInfo::OnButtonPushed() {
running = false;
return true;
}
bool SystemInfo::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return screens.OnTouchEvent(event);
}
std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
auto batteryPercentF = batteryController.PercentRemaining();
uint16_t batteryPercent = 0;
if(batteryPercentF > 100.0f) batteryPercent = 100;
else if(batteryPercentF < 0.0f) batteryPercent = 0;
uint8_t brightness = 0;
switch(brightnessController.Level()) {
case Controllers::BrightnessController::Levels::Off: brightness = 0; break;
case Controllers::BrightnessController::Levels::Low: brightness = 1; break;
case Controllers::BrightnessController::Levels::Medium: brightness = 2; break;
case Controllers::BrightnessController::Levels::High: brightness = 3; break;
}
auto resetReason = [&watchdog]() {
auto resetReason = [this]() {
switch (watchdog.ResetReason()) {
case Drivers::Watchdog::ResetReasons::Watchdog: return "wtdg";
case Drivers::Watchdog::ResetReasons::HardReset: return "hardr";
@ -63,7 +84,7 @@ ScreenList::ScreenList(Pinetime::Applications::DisplayApp *app,
// TODO handle more than 100 days of uptime
sprintf(t1, "Pinetime\n"
"Version:%d.%d.%d\n"
"Version:%ld.%ld.%ld\n"
"Build: %s\n"
" %s\n"
"Date: %02d/%02d/%04d\n"
@ -72,68 +93,24 @@ ScreenList::ScreenList(Pinetime::Applications::DisplayApp *app,
"Battery: %d%%\n"
"Backlight: %d/3\n"
"Last reset: %s\n",
Version::Major(), Version::Minor(), Version::Patch(),
__DATE__, __TIME__,
dateTimeController.Day(), dateTimeController.Month(), dateTimeController.Year(),
dateTimeController.Hours(), dateTimeController.Minutes(), dateTimeController.Seconds(),
uptimeDays, uptimeHours, uptimeMinutes, uptimeSeconds,
batteryPercent, brightness, resetReason);
Version::Major(), Version::Minor(), Version::Patch(),
__DATE__, __TIME__,
dateTimeController.Day(), static_cast<uint8_t>(dateTimeController.Month()), dateTimeController.Year(),
dateTimeController.Hours(), dateTimeController.Minutes(), dateTimeController.Seconds(),
uptimeDays, uptimeHours, uptimeMinutes, uptimeSeconds,
batteryPercent, brightness, resetReason);
screens.emplace_back(t1);
return std::unique_ptr<Screen>(new Screens::Label(app, t1));
}
std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
auto& bleAddr = bleController.Address();
sprintf(t2, "BLE MAC: \n %2x:%2x:%2x:%2x:%2x:%2x",
bleAddr[5], bleAddr[4], bleAddr[3], bleAddr[2], bleAddr[1], bleAddr[0]);
screens.emplace_back(t2);
return std::unique_ptr<Screen>(new Screens::Label(app, t2));
}
std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
strncpy(t3, "Hello from\nthe developper!", 27);
screens.emplace_back(t3);
auto &screen = screens[screenIndex];
screen.Show();
}
ScreenList::~ScreenList() {
lv_obj_clean(lv_scr_act());
}
bool ScreenList::Refresh() {
auto &screen = screens[screenIndex];
screen.Refresh();
return running;
}
bool ScreenList::OnButtonPushed() {
running = false;
return true;
}
bool ScreenList::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
switch (event) {
case TouchEvents::SwipeDown:
if (screenIndex > 0) {
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down);
auto &oldScreen = screens[screenIndex];
oldScreen.Hide();
screenIndex--;
auto &newScreen = screens[screenIndex];
newScreen.Show();
}
return true;
case TouchEvents::SwipeUp:
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up);
if (screenIndex < screens.size() - 1) {
auto &oldScreen = screens[screenIndex];
oldScreen.Hide();
screenIndex++;
auto &newScreen = screens[screenIndex];
newScreen.Show();
}
return true;
default:
return false;
}
return false;
return std::unique_ptr<Screen>(new Screens::Label(app, t3));
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <vector>
#include <Components/Ble/NimbleController.h>
#include "Screen.h"
#include "Label.h"
#include "ScreenList.h"
#include "Gauge.h"
#include "Meter.h"
#include <functional>
namespace Pinetime {
namespace Applications {
namespace Screens {
class SystemInfo : public Screen {
public:
explicit SystemInfo(DisplayApp* app,
Pinetime::Controllers::DateTime& dateTimeController,
Pinetime::Controllers::Battery& batteryController,
Pinetime::Controllers::BrightnessController& brightnessController,
Pinetime::Controllers::Ble& bleController,
Pinetime::Drivers::WatchdogView& watchdog);
~SystemInfo() override;
bool Refresh() override;
bool OnButtonPushed() override;
bool OnTouchEvent(TouchEvents event) override;
private:
bool running = true;
Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Controllers::Battery& batteryController;
Pinetime::Controllers::BrightnessController& brightnessController;
Pinetime::Controllers::Ble& bleController;
Pinetime::Drivers::WatchdogView& watchdog;
char t1[200];
char t2[200];
char t3[30];
ScreenList<3> screens;
std::unique_ptr<Screen> CreateScreen1();
std::unique_ptr<Screen> CreateScreen2();
std::unique_ptr<Screen> CreateScreen3();
};
}
}
}

View File

@ -17,9 +17,16 @@ static void event_handler(lv_obj_t * obj, lv_event_t event) {
screen->OnObjectEvent(obj, event, eventData);
}
static const char * btnm_map1[] = {Symbols::heartBeat, Symbols::shoe, Symbols::clock, "\n", Symbols::info, Symbols::list, Symbols::sun, ""};
Tile::Tile(DisplayApp* app) : Screen(app) {
Tile::Tile(DisplayApp* app, std::array<Applications, 6>& applications) : Screen(app) {
for(int i = 0, appIndex = 0; i < 8; i++) {
if(i == 3) btnm_map1[i] = "\n";
else if(i == 7) btnm_map1[i] = "";
else {
btnm_map1[i] = applications[appIndex].icon;
apps[appIndex] = applications[appIndex].application;
appIndex++;
}
}
modal.reset(new Modal(app));
btnm1 = lv_btnm_create(lv_scr_act(), NULL);
@ -39,63 +46,16 @@ bool Tile::Refresh() {
}
void Tile::OnObjectEvent(lv_obj_t *obj, lv_event_t event, uint32_t buttonId) {
auto* tile = static_cast<Tile*>(obj->user_data);
if(event == LV_EVENT_VALUE_CHANGED) {
switch(buttonId) {
case 0:
tile->StartMeterApp();
break;
case 1:
tile->StartGaugeApp();
break;
case 2:
tile->StartClockApp();
break;
case 3:
char versionStr[20];
sprintf(versionStr, "VERSION: %d.%d.%d", Version::Major(), Version::Minor(), Version::Patch());
modal->Show(versionStr);
break;
case 4:
tile->StartSysInfoApp();
break;
case 5:
tile->StartBrightnessApp();
break;
}
clickCount++;
app->StartApp(apps[buttonId]);
running = false;
}
}
bool Tile::OnButtonPushed() {
app->StartApp(DisplayApp::Apps::Clock);
app->StartApp(Apps::Clock);
running = false;
return true;
}
void Tile::StartClockApp() {
app->StartApp(DisplayApp::Apps::Clock);
running = false;
}
void Tile::StartSysInfoApp() {
app->StartApp(DisplayApp::Apps::SysInfo);
running = false;
}
void Tile::StartBrightnessApp() {
app->StartApp(DisplayApp::Apps::Brightness);
running = false;
}
void Tile::StartMeterApp() {
app->StartApp(DisplayApp::Apps::Meter);
running = false;
}
void Tile::StartGaugeApp() {
app->StartApp(DisplayApp::Apps::Gauge);
running = false;
}

View File

@ -5,13 +5,19 @@
#include <bits/unique_ptr.h>
#include "Modal.h"
#include <lvgl/src/lv_core/lv_style.h>
#include <DisplayApp/Apps.h>
namespace Pinetime {
namespace Applications {
namespace Screens {
class Tile : public Screen {
public:
explicit Tile(DisplayApp* app);
struct Applications {
const char* icon;
Pinetime::Applications::Apps application;
};
explicit Tile(DisplayApp* app, std::array<Applications, 6>& applications);
~Tile() override;
bool Refresh() override;
@ -20,40 +26,13 @@ namespace Pinetime {
void OnObjectEvent(lv_obj_t* obj, lv_event_t event, uint32_t buttonId);
private:
lv_style_t* labelRelStyle;
lv_style_t* labelPrStyle;
lv_obj_t * label1;
lv_obj_t * label2;
lv_obj_t * label3;
lv_obj_t* backgroundLabel;
lv_obj_t * button;
lv_obj_t * labelClick;
lv_obj_t *tileview;
lv_obj_t * tile1;
lv_obj_t * tile2;
lv_obj_t * list;
lv_obj_t * list_btn;
lv_obj_t * tile3;
lv_obj_t * btn1;
lv_obj_t * btn2;
lv_obj_t * btn3;
lv_obj_t * btnm1;
lv_obj_t * btnm2;
uint32_t clickCount = 0 ;
uint32_t previousClickCount = 0;
void StartClockApp();
void StartSysInfoApp();
void StartMeterApp();
void StartGaugeApp();
bool running = true;
std::unique_ptr<Modal> modal;
void StartBrightnessApp();
const char* btnm_map1[8];
Pinetime::Applications::Apps apps[6];
};
}
}

View File

@ -28,7 +28,7 @@ namespace Pinetime {
if(xTaskGetTickCount() - lastTick > 10000) {
NRF_LOG_INFO("---------------------------------------\nFree heap : %d", xPortGetFreeHeapSize());
auto nb = uxTaskGetSystemState(tasksStatus, 10, NULL);
for (int i = 0; i < nb; i++) {
for (uint32_t i = 0; i < nb; i++) {
NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark);
if (tasksStatus[i].usStackHighWaterMark < 20)
NRF_LOG_INFO("WARNING!!! Task %s task is nearly full, only %dB available", tasksStatus[i].pcTaskName,

View File

@ -12,6 +12,7 @@
#include <host/util/util.h>
#include <drivers/InternalFlash.h>
#include "../main.h"
#include "Components/Ble/NimbleController.h"
using namespace Pinetime::System;
@ -57,14 +58,7 @@ void SystemTask::Work() {
spi.Init();
spiNorFlash.Init();
// Write the 'image OK' flag if it's not already done
// TODO implement a better verification mecanism for the image (ask for user confirmation via UI/BLE ?)
uint32_t* imageOkPtr = reinterpret_cast<uint32_t *>(0x7BFE8);
uint32_t imageOk = *imageOkPtr;
if(imageOk != 1)
Pinetime::Drivers::InternalFlash::WriteWord(0x7BFE8, 1);
spiNorFlash.Wakeup();
nimbleController.Init();
nimbleController.StartAdvertising();
lcd.Init();
@ -112,22 +106,33 @@ void SystemTask::Work() {
Messages message = static_cast<Messages >(msg);
switch(message) {
case Messages::GoToRunning:
isSleeping = false;
spi.Wakeup();
twiMaster.Wakeup();
spiNorFlash.Wakeup();
lcd.Wakeup();
touchPanel.Wakeup();
displayApp->PushMessage(Applications::DisplayApp::Messages::GoToRunning);
displayApp->PushMessage(Applications::DisplayApp::Messages::UpdateBatteryLevel);
xTimerStart(idleTimer, 0);
nimbleController.StartAdvertising();
isSleeping = false;
isWakingUp = false;
break;
case Messages::GoToSleep:
isGoingToSleep = true;
NRF_LOG_INFO("[SystemTask] Going to sleep");
xTimerStop(idleTimer, 0);
displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::GoToSleep);
isSleeping = true;
break;
case Messages::OnNewTime:
ReloadIdleTimer();
displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::UpdateDateTime);
break;
case Messages::OnNewNotification:
if(isSleeping) GoToRunning();
if(isSleeping && !isWakingUp) GoToRunning();
displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::NewNotification);
break;
case Messages::BleConnected:
@ -137,13 +142,12 @@ void SystemTask::Work() {
break;
case Messages::BleFirmwareUpdateStarted:
doNotGoToSleep = true;
if(isSleeping) GoToRunning();
if(isSleeping && !isWakingUp) GoToRunning();
displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::BleFirmwareUpdateStarted);
break;
case Messages::BleFirmwareUpdateFinished:
doNotGoToSleep = false;
xTimerStart(idleTimer, 0);
displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::BleFirmwareUpdateFinished);
if(bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated)
NVIC_SystemReset();
break;
@ -153,6 +157,16 @@ void SystemTask::Work() {
case Messages::OnButtonEvent:
ReloadIdleTimer();
break;
case Messages::OnDisplayTaskSleeping:
spiNorFlash.Sleep();
lcd.Sleep();
touchPanel.Sleep();
spi.Sleep();
twiMaster.Sleep();
isSleeping = true;
isGoingToSleep = false;
break;
default: break;
}
}
@ -180,24 +194,27 @@ void SystemTask::Work() {
}
void SystemTask::OnButtonPushed() {
if(isGoingToSleep) return;
if(!isSleeping) {
NRF_LOG_INFO("[SystemTask] Button pushed");
PushMessage(Messages::OnButtonEvent);
displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::ButtonPushed);
}
else {
NRF_LOG_INFO("[SystemTask] Button pushed, waking up");
GoToRunning();
if(!isWakingUp) {
NRF_LOG_INFO("[SystemTask] Button pushed, waking up");
GoToRunning();
}
}
}
void SystemTask::GoToRunning() {
isWakingUp = true;
PushMessage(Messages::GoToRunning);
displayApp->PushMessage(Applications::DisplayApp::Messages::GoToRunning);
displayApp->PushMessage(Applications::DisplayApp::Messages::UpdateBatteryLevel);
}
void SystemTask::OnTouchEvent() {
if(isGoingToSleep) return ;
NRF_LOG_INFO("[SystemTask] Touch event");
if(!isSleeping) {
PushMessage(Messages::OnTouchEvent);
@ -206,6 +223,9 @@ void SystemTask::OnTouchEvent() {
}
void SystemTask::PushMessage(SystemTask::Messages msg) {
if(msg == Messages::GoToSleep) {
isGoingToSleep = true;
}
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(systemTaksMsgQueue, &msg, &xHigherPriorityTaskWoken);
@ -222,6 +242,6 @@ void SystemTask::OnIdle() {
}
void SystemTask::ReloadIdleTimer() const {
if(isSleeping) return;
if(isSleeping || isGoingToSleep) return;
xTimerReset(idleTimer, 0);
}

View File

@ -8,16 +8,17 @@
#include <Components/Battery/BatteryController.h>
#include <DisplayApp/DisplayApp.h>
#include <drivers/Watchdog.h>
#include <Components/Ble/NimbleController.h>
#include <drivers/SpiNorFlash.h>
#include "SystemMonitor.h"
#include "Components/Ble/NimbleController.h"
#include "timers.h"
namespace Pinetime {
namespace System {
class SystemTask {
public:
enum class Messages {GoToSleep, GoToRunning, OnNewTime, OnNewNotification, BleConnected,
BleFirmwareUpdateStarted, BleFirmwareUpdateFinished, OnTouchEvent, OnButtonEvent
BleFirmwareUpdateStarted, BleFirmwareUpdateFinished, OnTouchEvent, OnButtonEvent, OnDisplayTaskSleeping
};
SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
@ -37,6 +38,8 @@ namespace Pinetime {
void OnIdle();
Pinetime::Controllers::NimbleController& nimble() {return nimbleController;};
private:
TaskHandle_t taskHandle;
@ -51,7 +54,9 @@ namespace Pinetime {
Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime& dateTimeController;
QueueHandle_t systemTaksMsgQueue;
bool isSleeping = false;
std::atomic<bool> isSleeping{false};
std::atomic<bool> isGoingToSleep{false};
std::atomic<bool> isWakingUp{false};
Pinetime::Drivers::Watchdog watchdog;
Pinetime::Drivers::WatchdogView watchdogView;
Pinetime::Controllers::NotificationManager& notificationManager;
@ -84,4 +89,4 @@ namespace Pinetime {
#endif
};
}
}
}

View File

@ -5,12 +5,14 @@
namespace Pinetime {
class Version {
public:
static uint32_t Major() {return major;}
static uint32_t Minor() {return minor;}
static uint32_t Patch() {return patch;}
static constexpr uint32_t Major() {return major;}
static constexpr uint32_t Minor() {return minor;}
static constexpr uint32_t Patch() {return patch;}
static constexpr const char* VersionString() {return versionString;}
private:
static constexpr uint32_t major = @PROJECT_VERSION_MAJOR@;
static constexpr uint32_t minor = @PROJECT_VERSION_MINOR@;
static constexpr uint32_t patch = @PROJECT_VERSION_PATCH@;
static constexpr const char* versionString = "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@";
};
}

View File

@ -2,7 +2,6 @@
#include <task.h>
#include <nrfx_log.h>
#include <legacy/nrf_drv_gpiote.h>
#include "Cst816s.h"
using namespace Pinetime::Drivers;
@ -60,9 +59,9 @@ Cst816S::TouchInfos Cst816S::GetTouchInfo() {
uint16_t y = (yHigh << 8) | yLow;
auto action = touchData[touchEventIndex + (touchStep * i)] >> 6; /* 0 = Down, 1 = Up, 2 = contact*/
auto finger = touchData[touchIdIndex + (touchStep * i)] >> 4;
auto pressure = touchData[touchXYIndex + (touchStep * i)];
auto area = touchData[touchMiscIndex + (touchStep * i)] >> 4;
//auto finger = touchData[touchIdIndex + (touchStep * i)] >> 4;
//auto pressure = touchData[touchXYIndex + (touchStep * i)];
//auto area = touchData[touchMiscIndex + (touchStep * i)] >> 4;
info.x = x;
info.y = y;
@ -89,7 +88,6 @@ Cst816S::TouchInfos Cst816S::GetTouchInfo() {
// case Gestures::LongPress: NRF_LOG_INFO("Gesture : Long press"); break;
// default : NRF_LOG_INFO("Unknown"); break;
// }
}
@ -97,12 +95,16 @@ Cst816S::TouchInfos Cst816S::GetTouchInfo() {
}
void Cst816S::Sleep() {
// TODO re enable sleep mode
//twiMaster.Sleep();
nrf_gpio_cfg_default(6);
nrf_gpio_cfg_default(7);
nrf_gpio_pin_clear(pinReset);
vTaskDelay(5);
nrf_gpio_pin_set(pinReset);
vTaskDelay(50);
static constexpr uint8_t sleepValue = 0x03;
twiMaster.Write(twiAddress, 0xA5, &sleepValue, 1);
NRF_LOG_INFO("[TOUCHPANEL] Sleep");
}
void Cst816S::Wakeup() {
Init();
NRF_LOG_INFO("[TOUCHPANEL] Wakeup");
}

View File

@ -1,4 +1,5 @@
#include <hal/nrf_gpio.h>
#include <nrfx_log.h>
#include "Spi.h"
using namespace Pinetime::Drivers;
@ -18,8 +19,12 @@ bool Spi::Read(uint8_t* cmd, size_t cmdSize, uint8_t *data, size_t dataSize) {
}
void Spi::Sleep() {
// TODO sleep spi
nrf_gpio_cfg_default(pinCsn);
NRF_LOG_INFO("[SPI] Sleep")
}
bool Spi::WriteCmdAndBuffer(const uint8_t *cmd, size_t cmdSize, const uint8_t *data, size_t dataSize) {
return spiMaster.WriteCmdAndBuffer(pinCsn, cmd, cmdSize, data, dataSize);
}
bool Spi::Init() {
@ -27,8 +32,10 @@ bool Spi::Init() {
return true;
}
bool Spi::WriteCmdAndBuffer(const uint8_t *cmd, size_t cmdSize, const uint8_t *data, size_t dataSize) {
return spiMaster.WriteCmdAndBuffer(pinCsn, cmd, cmdSize, data, dataSize);
void Spi::Wakeup() {
nrf_gpio_cfg_output(pinCsn);
nrf_gpio_pin_set(pinCsn);
NRF_LOG_INFO("[SPI] Wakeup")
}

View File

@ -4,6 +4,7 @@
#include "SpiMaster.h"
#include <algorithm>
#include <task.h>
#include <nrfx_log.h>
using namespace Pinetime::Drivers;
@ -117,8 +118,6 @@ void SpiMaster::OnEndEvent() {
spiBaseAddress->TASKS_START = 1;
} else {
uint8_t* buffer = nullptr;
size_t size = 0;
if(taskToNotify != nullptr) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(taskToNotify, &xHigherPriorityTaskWoken);
@ -233,10 +232,13 @@ void SpiMaster::Sleep() {
nrf_gpio_cfg_default(params.pinSCK);
nrf_gpio_cfg_default(params.pinMOSI);
nrf_gpio_cfg_default(params.pinMISO);
NRF_LOG_INFO("[SPIMASTER] sleep")
}
void SpiMaster::Wakeup() {
Init();
NRF_LOG_INFO("[SPIMASTER] Wakeup");
}
bool SpiMaster::WriteCmdAndBuffer(uint8_t pinCsn, const uint8_t *cmd, size_t cmdSize, const uint8_t *data, size_t dataSize) {

View File

@ -11,8 +11,8 @@ SpiNorFlash::SpiNorFlash(Spi& spi) : spi{spi} {
}
void SpiNorFlash::Init() {
auto id = ReadIdentificaion();
NRF_LOG_INFO("[SPI FLASH] Manufacturer : %d, Memory type : %d, memory density : %d", id.manufacturer, id.type, id.density);
device_id = ReadIdentificaion();
NRF_LOG_INFO("[SPI FLASH] Manufacturer : %d, Memory type : %d, memory density : %d", device_id.manufacturer, device_id.type, device_id.density);
}
void SpiNorFlash::Uninit() {
@ -20,11 +20,25 @@ void SpiNorFlash::Uninit() {
}
void SpiNorFlash::Sleep() {
auto cmd = static_cast<uint8_t>(Commands::DeepPowerDown);
spi.Write(&cmd, sizeof(uint8_t));
NRF_LOG_INFO("[FLASH] Sleep")
}
void SpiNorFlash::Wakeup() {
// send Commands::ReleaseFromDeepPowerDown then 3 dummy bytes before reading Device ID
static constexpr uint8_t cmdSize = 4;
uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::ReleaseFromDeepPowerDown), 0x01, 0x02, 0x03};
uint8_t id = 0;
spi.Read(reinterpret_cast<uint8_t *>(&cmd), cmdSize, &id, 1);
auto devId = device_id = ReadIdentificaion();
if(devId.type != device_id.type) {
NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: Failed");
}
else {
NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: %d", id);
}
NRF_LOG_INFO("[FLASH] Wakeup")
}
SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() {

View File

@ -48,11 +48,13 @@ namespace Pinetime {
SectorErase = 0x20,
ReadSecurityRegister = 0x2B,
ReadIdentification = 0x9F,
ReleaseFromDeepPowerDown = 0xAB,
DeepPowerDown = 0xB9
};
static constexpr uint16_t pageSize = 256;
Spi& spi;
Identification device_id;
};
}
}

View File

@ -1,5 +1,6 @@
#include <hal/nrf_gpio.h>
#include <libraries/delay/nrf_delay.h>
#include <nrfx_log.h>
#include "St7789.h"
#include "Spi.h"
@ -174,12 +175,10 @@ void St7789::HardwareReset() {
void St7789::Sleep() {
SleepIn();
nrf_gpio_cfg_default(pinDataCommand);
// spi.Sleep(); // TODO sleep SPI
NRF_LOG_INFO("[LCD] Sleep");
}
void St7789::Wakeup() {
// spi.Wakeup(); // TODO wake up SPI
nrf_gpio_cfg_output(pinDataCommand);
// TODO why do we need to reset the controller?
HardwareReset();
@ -193,4 +192,5 @@ void St7789::Wakeup() {
NormalModeOn();
VerticalScrollStartAddress(verticalScrollingStartAddress);
DisplayOn();
NRF_LOG_INFO("[LCD] Wakeup")
}

View File

@ -1,5 +1,5 @@
#include <sdk/integration/nrfx/nrfx_log.h>
#include <sdk/modules/nrfx/hal/nrf_gpio.h>
#include <nrfx_log.h>
#include <hal/nrf_gpio.h>
#include <cstring>
#include "TwiMaster.h"
@ -137,4 +137,18 @@ void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, b
uint32_t error = twiBaseAddress->ERRORSRC;
twiBaseAddress->ERRORSRC = error;
}
}
}
void TwiMaster::Sleep() {
while(twiBaseAddress->ENABLE != 0) {
twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos);
}
nrf_gpio_cfg_default(6);
nrf_gpio_cfg_default(7);
NRF_LOG_INFO("[TWIMASTER] Sleep");
}
void TwiMaster::Wakeup() {
Init();
NRF_LOG_INFO("[TWIMASTER] Wakeup");
}

View File

@ -22,6 +22,9 @@ namespace Pinetime {
void Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
void Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
void Sleep();
void Wakeup();
private:
void Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
void Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);

View File

@ -79,6 +79,7 @@ void Process(void* instance) {
NRF_LOG_INFO("Init...");
spi.Init();
spiNorFlash.Init();
spiNorFlash.Wakeup();
brightnessController.Init();
lcd.Init();
gfx.Init();
@ -103,10 +104,10 @@ void Process(void* instance) {
static constexpr uint32_t screenWidth = 240;
static constexpr uint32_t screenWidthInBytes = screenWidth*2; // LCD display 16bits color (1 pixel = 2 bytes)
uint16_t displayLineBuffer[screenWidth];
for(int line = 0; line < screenWidth; line++) {
for(uint32_t line = 0; line < screenWidth; line++) {
spiNorFlash.Read(line*screenWidthInBytes, reinterpret_cast<uint8_t *>(displayLineBuffer), screenWidth);
spiNorFlash.Read((line*screenWidthInBytes)+screenWidth, reinterpret_cast<uint8_t *>(displayLineBuffer) + screenWidth, screenWidth);
for(int col = 0; col < screenWidth; col++) {
for(uint32_t col = 0; col < screenWidth; col++) {
gfx.pixel_draw(col, line, displayLineBuffer[col]);
}
}

View File

@ -310,12 +310,14 @@ ble_gap_log_conn(uint8_t own_addr_type, const ble_addr_t *peer_addr,
BLE_HS_LOG_ADDR(INFO, peer_addr->val);
}
/* // NRF LOG support max 6 params in log
BLE_HS_LOG(INFO, " scan_itvl=%d scan_window=%d itvl_min=%d itvl_max=%d "
"latency=%d supervision_timeout=%d min_ce_len=%d "
"max_ce_len=%d own_addr_type=%d",
params->scan_itvl, params->scan_window, params->itvl_min,
params->itvl_max, params->latency, params->supervision_timeout,
params->min_ce_len, params->max_ce_len, own_addr_type);
*/
}
#endif