Author: JF <jf@codingfield.com>
Fix conflicts
.idea/codeStyles/Project.xml | 4 doc/MemoryAnalysis.md | 4 doc/buildAndProgram.md | 2 doc/filesInReleaseNotes.md | 2 src/BootloaderVersion.cpp | 26 + src/BootloaderVersion.h | 12 src/CMakeLists.txt | 4 src/components/ble/MusicService.cpp | 284 +++++++++++----- src/components/ble/MusicService.h | 216 ++++++++---- src/displayapp/icons/music/disc.cpp | 110 ++++++ src/displayapp/icons/music/disc_f_1.cpp | 79 ++++ src/displayapp/icons/music/disc_f_2.cpp | 79 ++++ src/displayapp/screens/Brightness.cpp | 6 src/displayapp/screens/Clock.cpp | 23 src/displayapp/screens/DropDownDemo.cpp | 4 src/displayapp/screens/FirmwareUpdate.cpp | 10 src/displayapp/screens/FirmwareValidation.cpp | 20 src/displayapp/screens/Gauge.cpp | 4 src/displayapp/screens/InfiniPaint.cpp | 12 src/displayapp/screens/InfiniPaint.h | 40 +- src/displayapp/screens/Label.cpp | 2 src/displayapp/screens/Meter.cpp | 4 src/displayapp/screens/Modal.cpp | 6 src/displayapp/screens/Music.cpp | 357 +++++++++++++++----- src/displayapp/screens/Music.h | 100 ++++- src/displayapp/screens/Screen.h | 20 src/displayapp/screens/SystemInfo.cpp | 4 src/displayapp/screens/Tab.cpp | 4 src/displayapp/screens/Tile.cpp | 2 src/drivers/Cst816s.cpp | 4 src/drivers/Cst816s.h | 14 src/drivers/SpiNorFlash.cpp | 6 src/drivers/TwiMaster.cpp | 95 +++++ src/drivers/TwiMaster.h | 16 src/graphics.cpp | 8 src/logging/NrfLogger.cpp | 5 src/systemtask/SystemMonitor.h | 2 src/systemtask/SystemTask.cpp | 22 src/systemtask/SystemTask.h | 2
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index b3b93deb397b7901bda0faf8cbf69d403af03cf5..7bdfbcb131514a372b425482ee5a6aec3d4e234b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -7,6 +7,10 @@ <option name="INDENT_INSIDE_CODE_BLOCK" value="2" /> <option name="INDENT_DIRECTIVE_AS_CODE" value="true" /> <option name="SPACE_BEFORE_TEMPLATE_DECLARATION_LT" value="true" /> + <option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" /> + <option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" /> + <option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" /> + <option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" /> </Objective-C> <codeStyleSettings language="ObjectiveC"> <option name="RIGHT_MARGIN" value="140" /> diff --git a/doc/MemoryAnalysis.md b/doc/MemoryAnalysis.md index 1bf6e24d73e9b5a9b2c8176e715c227f40c05dd7..95bd611c298e7e234ae8b5a74958fb232e4e26b2 100644 --- a/doc/MemoryAnalysis.md +++ b/doc/MemoryAnalysis.md @@ -1,6 +1,6 @@ # Memory analysis ## FreeRTOS heap and task stack -FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an aray of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h* +FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an array of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h* FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes,...). The function `xPortGetFreeHeapSize()` returns the amount of memory available in this *ucHeap* buffer. If this value reaches 0, FreeRTOS runs out of memory. @@ -75,4 +75,4 @@ #NimBLE buffers *TODO* #Tools - - https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC. \ No newline at end of file + - https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC. diff --git a/doc/buildAndProgram.md b/doc/buildAndProgram.md index 26f3665ecdd943f8ffb566d6ea1b2a648050b56b..72870e3dc069ea737e371d849902203298fe6c73 100644 --- a/doc/buildAndProgram.md +++ b/doc/buildAndProgram.md @@ -18,7 +18,7 @@ CMake configures the project according to variables you specify the command line. The variables are: Variable | Description | Example| ----------|-------------|--------| -**ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`-DARM_NONE_EABI_TOOLCHAIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/`| +**ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`-DARM_NONE_EABI_TOOLCHAIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2020-q2-update/`| **NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`| **USE_JLINK, USE_GDB_CLIENT and USE_OPENOCD**|Enable *JLink* mode, *GDB Client* (Black Magic Probe) mode or *OpenOCD* mode (set the one you want to use to `1`)|`-DUSE_JLINK=1` **CMAKE_BUILD_TYPE**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug` diff --git a/doc/filesInReleaseNotes.md b/doc/filesInReleaseNotes.md index d2930513a7b98f181c5e36b50d062bc19d0206e1..2fdfadf4f49dcfb0872d35a09d056ebcd85b1d06 100644 --- a/doc/filesInReleaseNotes.md +++ b/doc/filesInReleaseNotes.md @@ -42,7 +42,7 @@ - **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.out** : 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,... diff --git a/src/BootloaderVersion.cpp b/src/BootloaderVersion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8555593fb6781704ebafdd5999b433c6aca30d82 --- /dev/null +++ b/src/BootloaderVersion.cpp @@ -0,0 +1,26 @@ +#include <cstdint> +#include "BootloaderVersion.h" + +using namespace Pinetime; + +// NOTE : current bootloader does not export its version to the application firmware. + +uint32_t BootloaderVersion::Major() { + return 0; +} + +uint32_t BootloaderVersion::Minor() { + return 0; +} + +uint32_t BootloaderVersion::Patch() { + return 0; +} + +const char *BootloaderVersion::VersionString() { + return "0.0.0"; +} + +bool BootloaderVersion::IsValid() { + return false; +} diff --git a/src/BootloaderVersion.h b/src/BootloaderVersion.h new file mode 100644 index 0000000000000000000000000000000000000000..c7fcbd988299738c99755676d0bfe4c864cec952 --- /dev/null +++ b/src/BootloaderVersion.h @@ -0,0 +1,12 @@ +#pragma once + +namespace Pinetime { + class BootloaderVersion { + public: + static uint32_t Major(); + static uint32_t Minor(); + static uint32_t Patch(); + static const char* VersionString(); + static bool IsValid(); + }; +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b18c6ce4d6e4da0297df7ba39ae0543274acfa53..4d691ede93b31eb38f32a1cfff2f58366859ca6b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -324,6 +324,7 @@ displayapp/icons/bluetooth/os_bt_disconnected.c ) list(APPEND SOURCE_FILES + BootloaderVersion.cpp logging/NrfLogger.cpp displayapp/DisplayApp.cpp displayapp/screens/Screen.cpp @@ -401,6 +402,7 @@ graphics.cpp ) set(INCLUDE_FILES + BootloaderVersion.h logging/Logger.h logging/NrfLogger.h displayapp/DisplayApp.h @@ -558,7 +560,7 @@ ../ ) -set(COMMON_FLAGS -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) +set(COMMON_FLAGS -MP -MD -mthumb -mabi=aapcs -Wall -Wno-unknown-pragmas -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) add_definitions(-DCONFIG_GPIO_AS_PINRESET) add_definitions(-DDEBUG) add_definitions(-DNIMBLE_CFG_CONTROLLER) diff --git a/src/components/ble/MusicService.cpp b/src/components/ble/MusicService.cpp index 9105a8e6fdab7f4fcb01bd50fc823e1f3337a899..84f2972b633fccac038d9ad01d46ef07eb994549 100644 --- a/src/components/ble/MusicService.cpp +++ b/src/components/ble/MusicService.cpp @@ -1,129 +1,225 @@ +/* Copyright (C) 2020 JF, Adam Pigg, Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ #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); + 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..."; +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]; + msPositionCharUuid.value[11] = msPositionCharId[0]; + msPositionCharUuid.value[12] = msPositionCharId[1]; + msTotalLengthCharUuid.value[11] = msTotalLengthCharId[0]; + msTotalLengthCharUuid.value[12] = msTotalLengthCharId[1]; + msTrackNumberCharUuid.value[11] = msTrackNumberCharId[0]; + msTrackNumberCharUuid.value[12] = msTrackNumberCharId[1]; + msTrackTotalCharUuid.value[11] = msTrackTotalCharId[0]; + msTrackTotalCharUuid.value[12] = msTrackTotalCharId[1]; + msPlaybackSpeedCharUuid.value[11] = msPlaybackSpeedCharId[0]; + msPlaybackSpeedCharUuid.value[12] = msPlaybackSpeedCharId[1]; + msRepeatCharUuid.value[11] = msRepeatCharId[0]; + msRepeatCharUuid.value[12] = msRepeatCharId[1]; + msShuffleCharUuid.value[11] = msShuffleCharId[0]; + msShuffleCharUuid.value[12] = msShuffleCharId[1]; + + characteristicDefinition[0] = {.uuid = (ble_uuid_t *) (&msEventCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_NOTIFY, + .val_handle = &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] = {.uuid = (ble_uuid_t *) (&msPositionCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }; + characteristicDefinition[6] = {.uuid = (ble_uuid_t *) (&msTotalLengthCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }; + characteristicDefinition[7] = {.uuid = (ble_uuid_t *) (&msTotalLengthCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }; + characteristicDefinition[8] = {.uuid = (ble_uuid_t *) (&msTrackNumberCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }; + characteristicDefinition[9] = {.uuid = (ble_uuid_t *) (&msTrackTotalCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }; + characteristicDefinition[10] = {.uuid = (ble_uuid_t *) (&msPlaybackSpeedCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }; + characteristicDefinition[11] = {.uuid = (ble_uuid_t *) (&msRepeatCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }; + characteristicDefinition[12] = {.uuid = (ble_uuid_t *) (&msShuffleCharUuid), + .access_cb = MSCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }; + characteristicDefinition[13] = {0}; + + serviceDefinition[0] = { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = (ble_uuid_t *) &msUuid, + .characteristics = characteristicDefinition + }; + serviceDefinition[1] = {0}; + + artistName = "Waiting for"; + albumName = ""; + trackName = "track information..."; + playing = false; + repeat = false; + shuffle = false; + playbackSpeed = 1.0f; + trackProgress = 0; + trackLength = 0; } -void Pinetime::Controllers::MusicService::Init() -{ +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) { - + 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]; - } + 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) { + artistName = s; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTrackCharUuid) == 0) { + trackName = s; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msAlbumCharUuid) == 0) { + albumName = s; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msStatusCharUuid) == 0) { + playing = s[0]; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msRepeatCharUuid) == 0) { + repeat = s[0]; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msShuffleCharUuid) == 0) { + shuffle = s[0]; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msPositionCharUuid) == 0) { + trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTotalLengthCharUuid) == 0) { + trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTrackNumberCharUuid) == 0) { + trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTrackTotalCharUuid) == 0) { + tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msPlaybackSpeedCharUuid) == 0) { + playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f; + } } return 0; } -std::string Pinetime::Controllers::MusicService::album() -{ - return m_album; +std::string Pinetime::Controllers::MusicService::getAlbum() { + return albumName; } -std::string Pinetime::Controllers::MusicService::artist() -{ - return m_artist; +std::string Pinetime::Controllers::MusicService::getArtist() { + return artistName; } -std::string Pinetime::Controllers::MusicService::track() -{ - return m_track; +std::string Pinetime::Controllers::MusicService::getTrack() { + return trackName; } -unsigned char Pinetime::Controllers::MusicService::status() -{ - return m_status; +bool Pinetime::Controllers::MusicService::isPlaying() { + return playing; } -void Pinetime::Controllers::MusicService::event(char event) -{ - auto *om = ble_hs_mbuf_from_flat(&event, 1); +float Pinetime::Controllers::MusicService::getPlaybackSpeed() { + return playbackSpeed; +} - uint16_t connectionHandle = m_system.nimble().connHandle(); +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, eventHandle, om); +} - if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { - return; - } +int Pinetime::Controllers::MusicService::getProgress() { + return trackProgress; +} - ble_gattc_notify_custom(connectionHandle, m_eventHandle, om); +int Pinetime::Controllers::MusicService::getTrackLength() { + return trackLength; } diff --git a/src/components/ble/MusicService.h b/src/components/ble/MusicService.h index ab6db572b183980efec46bd4942f729b4762d672..b365909b2bd852b84519b07147737c0b52595c76 100644 --- a/src/components/ble/MusicService.h +++ b/src/components/ble/MusicService.h @@ -1,3 +1,20 @@ +/* Copyright (C) 2020 JF, Adam Pigg, Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ #pragma once #include <cstdint> @@ -14,78 +31,135 @@ 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; - + public: + explicit MusicService(Pinetime::System::SystemTask &system); + + void Init(); + + int OnCommand(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt); + + void event(char event); + + std::string getArtist(); + + std::string getTrack(); + + std::string getAlbum(); + + int getProgress(); + + int getTrackLength(); + + float getPlaybackSpeed(); + + bool isPlaying(); + + 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; + + enum MusicStatus { + NotPlaying = 0x00, + 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}; + static constexpr uint8_t msPositionCharId[2] = {0x00, 0x07}; + static constexpr uint8_t msTotalLengthCharId[2] = {0x00, 0x08}; + static constexpr uint8_t msTrackNumberCharId[2] = {0x00, 0x09}; + static constexpr uint8_t msTrackTotalCharId[2] = {0x00, 0x0a}; + static constexpr uint8_t msPlaybackSpeedCharId[2] = {0x00, 0x0b}; + static constexpr uint8_t msRepeatCharId[2] = {0x00, 0x0c}; + static constexpr uint8_t msShuffleCharId[2] = {0x00, 0x0d}; + + 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 + }; + ble_uuid128_t msPositionCharUuid{ + .u = {.type = BLE_UUID_TYPE_128}, + .value = MUSIC_SERVICE_UUID_BASE + }; + ble_uuid128_t msTotalLengthCharUuid{ + .u = {.type = BLE_UUID_TYPE_128}, + .value = MUSIC_SERVICE_UUID_BASE + }; + ble_uuid128_t msTrackNumberCharUuid{ + .u = {.type = BLE_UUID_TYPE_128}, + .value = MUSIC_SERVICE_UUID_BASE + }; + ble_uuid128_t msTrackTotalCharUuid{ + .u = {.type = BLE_UUID_TYPE_128}, + .value = MUSIC_SERVICE_UUID_BASE + }; + ble_uuid128_t msPlaybackSpeedCharUuid{ + .u = {.type = BLE_UUID_TYPE_128}, + .value = MUSIC_SERVICE_UUID_BASE + }; + ble_uuid128_t msRepeatCharUuid{ + .u = {.type = BLE_UUID_TYPE_128}, + .value = MUSIC_SERVICE_UUID_BASE + }; + ble_uuid128_t msShuffleCharUuid{ + .u = {.type = BLE_UUID_TYPE_128}, + .value = MUSIC_SERVICE_UUID_BASE + }; + + struct ble_gatt_chr_def characteristicDefinition[14]; + struct ble_gatt_svc_def serviceDefinition[2]; + + uint16_t eventHandle; + + std::string artistName; + std::string albumName; + std::string trackName; + + bool playing; + + int trackProgress; + int trackLength; + int trackNumber; + int tracksTotal; + + float playbackSpeed; + + bool repeat; + bool shuffle; + + Pinetime::System::SystemTask &m_system; }; } } diff --git a/src/displayapp/icons/music/disc.cpp b/src/displayapp/icons/music/disc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0957873f878b1a32f081effbb539ef4cd9996c4c --- /dev/null +++ b/src/displayapp/icons/music/disc.cpp @@ -0,0 +1,110 @@ +/* Copyright (C) 2020 Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +#pragma once + +#include "lvgl/lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_DISC +#define LV_ATTRIBUTE_IMG_DISC +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_DISC uint8_t disc_map[] = { + 0xbd, 0xc1, 0xbe, 0xff, /* Color of index 0: foreground */ + 0x00, 0x00, 0x00, 0x00, /* Color of index 1: background */ + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfc, 0x00, 0x00, 0x1f, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0x0f, 0xf8, 0x07, 0xff, 0xff, + 0xff, 0xff, 0xc0, 0xff, 0xff, 0x81, 0xff, 0xff, + 0xff, 0xff, 0x07, 0xff, 0xff, 0xf0, 0x7f, 0xff, + 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, + 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0x0f, 0xff, + 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, + 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, + 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, + 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, + 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, + 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, + 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, + 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, + 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, + 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, + 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, + 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, + 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, + 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, + 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, + 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, + 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, + 0x8f, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xf8, + 0x9f, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xfc, + 0x9f, 0xff, 0xff, 0xe3, 0xe3, 0xff, 0xff, 0xfc, + 0x9f, 0xff, 0xff, 0xe7, 0xf3, 0xff, 0xff, 0xfc, + 0x9f, 0xff, 0xff, 0xe7, 0xf3, 0xff, 0xff, 0xfc, + 0x9f, 0xff, 0xff, 0xe7, 0xf3, 0xff, 0xff, 0xfc, + 0x9f, 0xff, 0xff, 0xe7, 0xf3, 0xff, 0xff, 0xfc, + 0x9f, 0xff, 0xff, 0xe7, 0xf3, 0xff, 0xff, 0xfc, + 0x9f, 0xff, 0xff, 0xe3, 0xe3, 0xff, 0xff, 0xfc, + 0x9f, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xfc, + 0x8f, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xf8, + 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, + 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, + 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, + 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, + 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, + 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, + 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, + 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, + 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, + 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, + 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, + 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, + 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, + 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, + 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, + 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, + 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, + 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0x0f, 0xff, + 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, + 0xff, 0xff, 0x07, 0xff, 0xff, 0xf0, 0x7f, 0xff, + 0xff, 0xff, 0xc0, 0xff, 0xff, 0x81, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0x0f, 0xf8, 0x07, 0xff, 0xff, + 0xff, 0xff, 0xfc, 0x00, 0x00, 0x1f, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xff, +}; + +const lv_img_dsc_t disc = { + { + LV_IMG_CF_INDEXED_1BIT, + 0, + 0, + 64, + 64 + }, + 520, + disc_map +}; \ No newline at end of file diff --git a/src/displayapp/icons/music/disc.png b/src/displayapp/icons/music/disc.png new file mode 100644 index 0000000000000000000000000000000000000000..699734fb9c7f8b8069857730bce2860cfb672460 Binary files /dev/null and b/src/displayapp/icons/music/disc.png differ diff --git a/src/displayapp/icons/music/disc_f_1.cpp b/src/displayapp/icons/music/disc_f_1.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b6b74177f692b85900d613cb8d375c304b0303c --- /dev/null +++ b/src/displayapp/icons/music/disc_f_1.cpp @@ -0,0 +1,79 @@ +/* Copyright (C) 2020 Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +#pragma once + +#include "lvgl/lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_DISC_F_1 +#define LV_ATTRIBUTE_IMG_DISC_F_1 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_DISC_F_1 uint8_t disc_f_1_map[] = { + 0xbd, 0xc1, 0xbe, 0xff, /* Color of index 0: foreground */ + 0x00, 0x00, 0x00, 0x00, /* Color of index 1: background */ + + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc0, + 0xff, 0xff, 0xfc, 0x00, + 0xff, 0xff, 0xf0, 0x0f, + 0xff, 0xff, 0xc0, 0xff, + 0xff, 0xff, 0x07, 0xff, + 0xff, 0xfc, 0x1f, 0xff, + 0xff, 0xf8, 0x7f, 0xff, + 0xff, 0xf0, 0xff, 0xff, + 0xff, 0xe3, 0xff, 0xff, + 0xff, 0xc7, 0xf3, 0xff, + 0xff, 0x8f, 0xc3, 0xff, + 0xff, 0x1f, 0x87, 0xff, + 0xfe, 0x3f, 0x0f, 0xff, + 0xfc, 0x7e, 0x1f, 0xff, + 0xfc, 0x7c, 0x3f, 0xff, + 0xf8, 0xfc, 0x7f, 0xff, + 0xf9, 0xfc, 0xff, 0xff, + 0xf1, 0xff, 0xff, 0xff, + 0xf3, 0xff, 0xff, 0xff, + 0xe3, 0xff, 0xff, 0xff, + 0xe7, 0xff, 0xff, 0xff, + 0xc7, 0xff, 0xff, 0xff, + 0xc7, 0xff, 0xff, 0xff, + 0xcf, 0xff, 0xff, 0xff, + 0xcf, 0xff, 0xff, 0xff, + 0x8f, 0xff, 0xff, 0xff, + 0x8f, 0xff, 0xff, 0xf8, + 0x9f, 0xff, 0xff, 0xf0, + 0x9f, 0xff, 0xff, 0xe3, + 0x9f, 0xff, 0xff, 0xe7, + 0x9f, 0xff, 0xff, 0xe7, +}; + +const lv_img_dsc_t disc_f_1 = { + { + LV_IMG_CF_INDEXED_1BIT, + 0, + 0, + 32, + 32 + }, + 136, + disc_f_1_map +}; + diff --git a/src/displayapp/icons/music/disc_f_1.png b/src/displayapp/icons/music/disc_f_1.png new file mode 100644 index 0000000000000000000000000000000000000000..946577344afa6d87ed4be12d174b4f779acdc287 Binary files /dev/null and b/src/displayapp/icons/music/disc_f_1.png differ diff --git a/src/displayapp/icons/music/disc_f_2.cpp b/src/displayapp/icons/music/disc_f_2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d2331d1718c3602be8df308f145eda0ad2c9a89 --- /dev/null +++ b/src/displayapp/icons/music/disc_f_2.cpp @@ -0,0 +1,79 @@ +/* Copyright (C) 2020 Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +#pragma once + +#include "lvgl/lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_DISC_F_2 +#define LV_ATTRIBUTE_IMG_DISC_F_2 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_DISC_F_2 uint8_t disc_f_2_map[] = { + 0xbd, 0xc1, 0xbe, 0xff, /* Color of index 0: foreground */ + 0x00, 0x00, 0x00, 0x00, /* Color of index 1: background */ + + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc0, + 0xff, 0xff, 0xfc, 0x00, + 0xff, 0xff, 0xf0, 0x0f, + 0xff, 0xff, 0xc0, 0xff, + 0xff, 0xff, 0x07, 0xff, + 0xff, 0xfc, 0x1f, 0xff, + 0xff, 0xf8, 0x7f, 0xf1, + 0xff, 0xf0, 0xff, 0x00, + 0xff, 0xe3, 0xfc, 0x03, + 0xff, 0xc7, 0xf0, 0x3f, + 0xff, 0x8f, 0xf0, 0xff, + 0xff, 0x1f, 0xf3, 0xff, + 0xfe, 0x3f, 0xff, 0xff, + 0xfc, 0x7f, 0xff, 0xff, + 0xfc, 0x7f, 0xff, 0xff, + 0xf8, 0xff, 0xff, 0xff, + 0xf9, 0xff, 0xff, 0xff, + 0xf1, 0xff, 0xff, 0xff, + 0xf3, 0xff, 0xff, 0xff, + 0xe3, 0xff, 0xff, 0xff, + 0xe7, 0xff, 0xff, 0xff, + 0xc7, 0xff, 0xff, 0xff, + 0xc7, 0xff, 0xff, 0xff, + 0xcf, 0xff, 0xff, 0xff, + 0xcf, 0xff, 0xff, 0xff, + 0x8f, 0xff, 0xff, 0xff, + 0x8f, 0xff, 0xff, 0xf8, + 0x9f, 0xff, 0xff, 0xf0, + 0x9f, 0xff, 0xff, 0xe3, + 0x9f, 0xff, 0xff, 0xe7, + 0x9f, 0xff, 0xff, 0xe7, +}; + +const lv_img_dsc_t disc_f_2 = { + { + LV_IMG_CF_INDEXED_1BIT, + 0, + 0, + 32, + 32 + }, + 136, + disc_f_2_map +}; + diff --git a/src/displayapp/icons/music/disc_f_2.png b/src/displayapp/icons/music/disc_f_2.png new file mode 100644 index 0000000000000000000000000000000000000000..4d9a4a388a00c95eac16b501a7c4dac326d029cb Binary files /dev/null and b/src/displayapp/icons/music/disc_f_2.png differ diff --git a/src/displayapp/screens/Brightness.cpp b/src/displayapp/screens/Brightness.cpp index 9e3416c0d958fe8c4983305fcae49b7552baf92d..8ea9a771b743941ac6175244b1bd43f727484ca0 100644 --- a/src/displayapp/screens/Brightness.cpp +++ b/src/displayapp/screens/Brightness.cpp @@ -11,15 +11,15 @@ } } Brightness::Brightness(Pinetime::Applications::DisplayApp *app, Controllers::BrightnessController& brightness) : Screen(app), brightness{brightness} { - slider = lv_slider_create(lv_scr_act(), NULL); + slider = lv_slider_create(lv_scr_act(), nullptr); lv_obj_set_user_data(slider, this); lv_obj_set_width(slider, LV_DPI * 2); - lv_obj_align(slider, NULL, LV_ALIGN_CENTER, 0, 0); + lv_obj_align(slider, nullptr, LV_ALIGN_CENTER, 0, 0); lv_obj_set_event_cb(slider, slider_event_cb); lv_slider_set_range(slider, 0, 2); lv_slider_set_value(slider, LevelToInt(brightness.Level()), LV_ANIM_OFF); - slider_label = lv_label_create(lv_scr_act(), NULL); + slider_label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(slider_label, LevelToString(brightness.Level())); lv_obj_set_auto_realign(slider_label, true); lv_obj_align(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp index eabab21a3c0b01955bb3ed31c81743c833792411..977321c1f0bd07d06561692084246fa89e595521 100644 --- a/src/displayapp/screens/Clock.cpp +++ b/src/displayapp/screens/Clock.cpp @@ -34,15 +34,15 @@ displayedChar[2] = 0; displayedChar[3] = 0; displayedChar[4] = 0; - batteryIcon = lv_label_create(lv_scr_act(), NULL); + batteryIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(batteryIcon, Symbols::batteryFull); lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 2); - batteryPlug = lv_label_create(lv_scr_act(), NULL); + batteryPlug = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(batteryPlug, Symbols::plug); lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); - bleIcon = lv_label_create(lv_scr_act(), NULL); + bleIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(bleIcon, Symbols::bluetooth); lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); @@ -50,14 +50,15 @@ notificationIcon = lv_label_create(lv_scr_act(), NULL); lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0); - label_date = lv_label_create(lv_scr_act(), NULL); + label_date = lv_label_create(lv_scr_act(), nullptr); + lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 60); - label_time = lv_label_create(lv_scr_act(), NULL); + label_time = lv_label_create(lv_scr_act(), nullptr); lv_label_set_style(label_time, LV_LABEL_STYLE_MAIN, LabelBigStyle); lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 0); - backgroundLabel = lv_label_create(lv_scr_act(), NULL); + backgroundLabel = lv_label_create(lv_scr_act(), nullptr); backgroundLabel->user_data = this; lv_obj_set_click(backgroundLabel, true); lv_obj_set_event_cb(backgroundLabel, event_handler); @@ -67,23 +68,23 @@ lv_obj_set_pos(backgroundLabel, 0, 0); lv_label_set_text(backgroundLabel, ""); - heartbeatIcon = lv_label_create(lv_scr_act(), NULL); + heartbeatIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(heartbeatIcon, Symbols::heartBeat); lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); - heartbeatValue = lv_label_create(lv_scr_act(), NULL); + heartbeatValue = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(heartbeatValue, "0"); lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); - heartbeatBpm = lv_label_create(lv_scr_act(), NULL); + heartbeatBpm = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(heartbeatBpm, "BPM"); lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); - stepValue = lv_label_create(lv_scr_act(), NULL); + stepValue = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(stepValue, "0"); lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); - stepIcon = lv_label_create(lv_scr_act(), NULL); + stepIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(stepIcon, Symbols::shoe); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); } diff --git a/src/displayapp/screens/DropDownDemo.cpp b/src/displayapp/screens/DropDownDemo.cpp index 735a0ccedecbcc71b51825830b919677eb7f28ea..ce3acd538f41767f205347fa9de1ab084bc6f182 100644 --- a/src/displayapp/screens/DropDownDemo.cpp +++ b/src/displayapp/screens/DropDownDemo.cpp @@ -9,7 +9,7 @@ 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); + ddlist = lv_ddlist_create(lv_scr_act(), nullptr); lv_ddlist_set_options(ddlist, "Apple\n" "Banana\n" "Orange\n" @@ -24,7 +24,7 @@ "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); + lv_obj_align(ddlist, nullptr, LV_ALIGN_IN_TOP_MID, 0, 20); } DropDownDemo::~DropDownDemo() { diff --git a/src/displayapp/screens/FirmwareUpdate.cpp b/src/displayapp/screens/FirmwareUpdate.cpp index e831114d4ff5878be506deb68811ed895a5c791e..778409ebf2b100910da5c9538afbfb776c78709a 100644 --- a/src/displayapp/screens/FirmwareUpdate.cpp +++ b/src/displayapp/screens/FirmwareUpdate.cpp @@ -10,19 +10,19 @@ FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::Ble& bleController) : Screen(app), bleController{bleController} { - titleLabel = lv_label_create(lv_scr_act(), NULL); + titleLabel = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(titleLabel, "Firmware update"); lv_obj_set_auto_realign(titleLabel, true); - lv_obj_align(titleLabel, NULL, LV_ALIGN_IN_TOP_MID, 0, 50); + lv_obj_align(titleLabel, nullptr, LV_ALIGN_IN_TOP_MID, 0, 50); - bar1 = lv_bar_create(lv_scr_act(), NULL); + bar1 = lv_bar_create(lv_scr_act(), nullptr); lv_obj_set_size(bar1, 200, 30); - lv_obj_align(bar1, NULL, LV_ALIGN_CENTER, 0, 0); + lv_obj_align(bar1, nullptr, LV_ALIGN_CENTER, 0, 0); lv_bar_set_anim_time(bar1, 10); lv_bar_set_range(bar1, 0, 100); lv_bar_set_value(bar1, 0, LV_ANIM_OFF); - percentLabel = lv_label_create(lv_scr_act(), NULL); + percentLabel = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(percentLabel, ""); lv_obj_set_auto_realign(percentLabel, true); lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60); diff --git a/src/displayapp/screens/FirmwareValidation.cpp b/src/displayapp/screens/FirmwareValidation.cpp index 2300b41d47477b14ad00987f71ae522a9a52c852..4ac399ffa384ff3f8f7a314521ebf88261db5d7d 100644 --- a/src/displayapp/screens/FirmwareValidation.cpp +++ b/src/displayapp/screens/FirmwareValidation.cpp @@ -20,20 +20,20 @@ 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); + labelVersionInfo = lv_label_create(lv_scr_act(), nullptr); + lv_obj_align(labelVersionInfo, nullptr, 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); + labelVersionValue = lv_label_create(lv_scr_act(), nullptr); 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); + labelIsValidated = lv_label_create(lv_scr_act(), nullptr); + lv_obj_align(labelIsValidated, nullptr, 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); @@ -44,21 +44,21 @@ 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); + buttonValidate = lv_btn_create(lv_scr_act(), nullptr); 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); + labelButtonValidate = lv_label_create(buttonValidate, nullptr); lv_label_set_recolor(labelButtonValidate, true); lv_label_set_text(labelButtonValidate, "#00ff00 Validate#"); - buttonReset = lv_btn_create(lv_scr_act(), NULL); + buttonReset = lv_btn_create(lv_scr_act(), nullptr); buttonReset->user_data = this; - lv_obj_align(buttonReset, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + lv_obj_align(buttonReset, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); lv_obj_set_event_cb(buttonReset, ButtonEventHandler); - labelButtonReset = lv_label_create(buttonReset, NULL); + labelButtonReset = lv_label_create(buttonReset, nullptr); lv_label_set_recolor(labelButtonReset, true); lv_label_set_text(labelButtonReset, "#ff0000 Reset#"); } diff --git a/src/displayapp/screens/Gauge.cpp b/src/displayapp/screens/Gauge.cpp index fd905231fd835b5b66d92cd2b3541c68061e53f4..81c283ca4a1f1960bae7518995ceb9138136e29b 100644 --- a/src/displayapp/screens/Gauge.cpp +++ b/src/displayapp/screens/Gauge.cpp @@ -25,11 +25,11 @@ needle_colors[0] = LV_COLOR_ORANGE; /*Create a gauge*/ - gauge1 = lv_gauge_create(lv_scr_act(), NULL); + gauge1 = lv_gauge_create(lv_scr_act(), nullptr); lv_gauge_set_style(gauge1, LV_GAUGE_STYLE_MAIN, &style); lv_gauge_set_needle_count(gauge1, 1, needle_colors); lv_obj_set_size(gauge1, 180, 180); - lv_obj_align(gauge1, NULL, LV_ALIGN_CENTER, 0, 0); + lv_obj_align(gauge1, nullptr, LV_ALIGN_CENTER, 0, 0); lv_gauge_set_scale(gauge1, 360, 60, 0); lv_gauge_set_range(gauge1, 0, 59); diff --git a/src/displayapp/screens/InfiniPaint.cpp b/src/displayapp/screens/InfiniPaint.cpp index b340f5d86e8ee6b5cbcd8e74d72e0d34001b66b1..3ea75e9ed167168a7325f30eed3a8cc581036b25 100644 --- a/src/displayapp/screens/InfiniPaint.cpp +++ b/src/displayapp/screens/InfiniPaint.cpp @@ -7,9 +7,9 @@ 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} { +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); + std::fill(b, b + bufferSize, LV_COLOR_WHITE); } InfiniPaint::~InfiniPaint() { @@ -33,10 +33,10 @@ } 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; + 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; diff --git a/src/displayapp/screens/InfiniPaint.h b/src/displayapp/screens/InfiniPaint.h index fb4f979b82e8360e1f0c41d80f38c11f879121be..f29135d595e764d46f97b30b09c4d6b72e386331 100644 --- a/src/displayapp/screens/InfiniPaint.h +++ b/src/displayapp/screens/InfiniPaint.h @@ -11,24 +11,28 @@ 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; + + 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; }; } } diff --git a/src/displayapp/screens/Label.cpp b/src/displayapp/screens/Label.cpp index 780ee88eb25b832d61ad0368e2b1cb5dec5e2e59..540776cc071b405d1a9c6ad39c2b7b57a88d60b2 100644 --- a/src/displayapp/screens/Label.cpp +++ b/src/displayapp/screens/Label.cpp @@ -4,7 +4,7 @@ using namespace Pinetime::Applications::Screens; Label::Label(Pinetime::Applications::DisplayApp *app, const char *text) : Screen(app), text{text} { - label = lv_label_create(lv_scr_act(), NULL); + label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_align(label, LV_LABEL_ALIGN_LEFT); lv_obj_set_size(label, 240, 240); lv_label_set_text(label, text); diff --git a/src/displayapp/screens/Meter.cpp b/src/displayapp/screens/Meter.cpp index c74b8bdfa1f0297a3f4d56205034be4a0ce0f32f..273e111dadc7c4d271d7f5eb04fbd1d8adcedaf9 100644 --- a/src/displayapp/screens/Meter.cpp +++ b/src/displayapp/screens/Meter.cpp @@ -17,14 +17,14 @@ style_lmeter.body.grad_color = lv_color_make(160,0,0); style_lmeter.body.padding.left = 16; /*Line length*/ /*Create a line meter */ - lmeter = lv_lmeter_create(lv_scr_act(), NULL); + lmeter = lv_lmeter_create(lv_scr_act(), nullptr); lv_lmeter_set_range(lmeter, 0, 60); /*Set the range*/ lv_lmeter_set_value(lmeter, value); /*Set the current value*/ lv_lmeter_set_angle_offset(lmeter, 180); lv_lmeter_set_scale(lmeter, 360, 60); /*Set the angle and number of lines*/ lv_lmeter_set_style(lmeter, LV_LMETER_STYLE_MAIN, &style_lmeter); /*Apply the new style*/ lv_obj_set_size(lmeter, 150, 150); - lv_obj_align(lmeter, NULL, LV_ALIGN_CENTER, 0, 0); + lv_obj_align(lmeter, nullptr, LV_ALIGN_CENTER, 0, 0); } diff --git a/src/displayapp/screens/Modal.cpp b/src/displayapp/screens/Modal.cpp index 63ae70c033d7514800107d6b56e5d6ce0f4f3915..29f7bfa7943e414d459178a3e2774f0f3e4c0347 100644 --- a/src/displayapp/screens/Modal.cpp +++ b/src/displayapp/screens/Modal.cpp @@ -54,7 +54,7 @@ lv_style_copy(&modal_style, &lv_style_plain_color); modal_style.body.main_color = modal_style.body.grad_color = LV_COLOR_BLACK; modal_style.body.opa = LV_OPA_50; - obj = lv_obj_create(lv_scr_act(), NULL); + obj = lv_obj_create(lv_scr_act(), nullptr); lv_obj_set_style(obj, &modal_style); lv_obj_set_pos(obj, 0, 0); lv_obj_set_size(obj, LV_HOR_RES, LV_VER_RES); @@ -63,10 +63,10 @@ static const char * btns2[] = {"Ok", ""}; /* Create the message box as a child of the modal background */ - mbox = lv_mbox_create(obj, NULL); + mbox = lv_mbox_create(obj, nullptr); lv_mbox_add_btns(mbox, btns2); lv_mbox_set_text(mbox, msg); - lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0); + lv_obj_align(mbox, nullptr, LV_ALIGN_CENTER, 0, 0); lv_obj_set_event_cb(mbox, Modal::mbox_event_cb); mbox->user_data = this; diff --git a/src/displayapp/screens/Music.cpp b/src/displayapp/screens/Music.cpp index 9b7d198bcde58788e36f92454b28b30daee61e92..225a15a4744731e8b06f4cf3481dbe5210a9b3cf 100644 --- a/src/displayapp/screens/Music.cpp +++ b/src/displayapp/screens/Music.cpp @@ -1,72 +1,131 @@ +/* Copyright (C) 2020 JF, Adam Pigg, Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ #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); +static void event_handler(lv_obj_t *obj, lv_event_t event) { + Music *screen = static_cast<Music *>(obj->user_data); screen->OnObjectEvent(obj, event); } +/** + * Set the pixel array to display by the image + * This just calls lv_img_set_src but adds type safety + * + * @param img pointer to an image object + * @param data the image array + */ +inline void lv_img_set_src_arr(lv_obj_t *img, const lv_img_dsc_t *src_img) { + lv_img_set_src(img, src_img); +} + +/** + * Music control watchapp + * + * TODO: Investigate Apple Media Service and AVRCPv1.6 support for seamless integration + */ 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); + lv_obj_t *label; + + btnVolDown = lv_btn_create(lv_scr_act(), nullptr); + btnVolDown->user_data = this; + lv_obj_set_event_cb(btnVolDown, event_handler); + lv_obj_set_size(btnVolDown, LV_HOR_RES / 3, 80); + lv_obj_align(btnVolDown, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + label = lv_label_create(btnVolDown, nullptr); + lv_label_set_text(label, "V-"); + lv_obj_set_hidden(btnVolDown, !displayVolumeButtons); + + btnVolUp = lv_btn_create(lv_scr_act(), nullptr); + btnVolUp->user_data = this; + lv_obj_set_event_cb(btnVolUp, event_handler); + lv_obj_set_size(btnVolUp, LV_HOR_RES / 3, 80); + lv_obj_align(btnVolUp, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + label = lv_label_create(btnVolUp, nullptr); + lv_label_set_text(label, "V+"); + lv_obj_set_hidden(btnVolDown, !displayVolumeButtons); + + btnPrev = lv_btn_create(lv_scr_act(), nullptr); + btnPrev->user_data = this; + lv_obj_set_event_cb(btnPrev, event_handler); + lv_obj_set_size(btnPrev, LV_HOR_RES / 3, 80); + lv_obj_align(btnPrev, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + label = lv_label_create(btnPrev, nullptr); + lv_label_set_text(label, "<<"); + + btnNext = lv_btn_create(lv_scr_act(), nullptr); + btnNext->user_data = this; + lv_obj_set_event_cb(btnNext, event_handler); + lv_obj_set_size(btnNext, LV_HOR_RES / 3, 80); + lv_obj_align(btnNext, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + label = lv_label_create(btnNext, nullptr); + lv_label_set_text(label, ">>"); + + btnPlayPause = lv_btn_create(lv_scr_act(), nullptr); + btnPlayPause->user_data = this; + lv_obj_set_event_cb(btnPlayPause, event_handler); + lv_obj_set_size(btnPlayPause, LV_HOR_RES / 3, 80); + lv_obj_align(btnPlayPause, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + txtPlayPause = lv_label_create(btnPlayPause, nullptr); + lv_label_set_text(txtPlayPause, ">"); + + txtTrackDuration = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_long_mode(txtTrackDuration, LV_LABEL_LONG_SROLL); + lv_obj_align(txtTrackDuration, nullptr, LV_ALIGN_IN_TOP_LEFT, 12, 20); + lv_label_set_text(txtTrackDuration, "--:--/--:--"); + lv_label_set_align(txtTrackDuration, LV_ALIGN_IN_LEFT_MID); + lv_obj_set_width(txtTrackDuration, LV_HOR_RES); + + constexpr uint8_t FONT_HEIGHT = 12; + constexpr uint8_t LINE_PAD = 15; + constexpr int8_t MIDDLE_OFFSET = -25; + txtArtist = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_long_mode(txtArtist, LV_LABEL_LONG_SROLL); + lv_obj_align(txtArtist, nullptr, LV_ALIGN_IN_LEFT_MID, 12, MIDDLE_OFFSET + 1 * FONT_HEIGHT); + lv_label_set_text(txtArtist, "Artist Name"); + lv_label_set_align(txtArtist, LV_ALIGN_IN_LEFT_MID); + lv_obj_set_width(txtArtist, LV_HOR_RES); + + txtTrack = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_long_mode(txtTrack, LV_LABEL_LONG_SROLL); + lv_obj_align(txtTrack, nullptr, LV_ALIGN_IN_LEFT_MID, 12, MIDDLE_OFFSET + 2 * FONT_HEIGHT + LINE_PAD); + lv_label_set_text(txtTrack, "This is a very long getTrack name"); + lv_label_set_align(txtTrack, LV_ALIGN_IN_LEFT_MID); + lv_obj_set_width(txtTrack, LV_HOR_RES); + + /** Init animation */ + imgDisc = lv_img_create(lv_scr_act(), nullptr); + lv_img_set_src_arr(imgDisc, &disc); + lv_obj_align(imgDisc, nullptr, LV_ALIGN_IN_TOP_RIGHT, -15, 15); + + imgDiscAnim = lv_img_create(lv_scr_act(), nullptr); + lv_img_set_src_arr(imgDiscAnim, &disc_f_1); + lv_obj_align(imgDiscAnim, nullptr, LV_ALIGN_IN_TOP_RIGHT, -15 - 32, 15); + + frameB = false; + + musicService.event(Controllers::MusicService::EVENT_MUSIC_OPEN); } Music::~Music() { @@ -79,47 +138,155 @@ return true; } bool Music::Refresh() { + if (artist != musicService.getArtist()) { + artist = musicService.getArtist(); + currentLength = 0; + lv_label_set_text(txtArtist, artist.data()); + } + + if (track != musicService.getTrack()) { + track = musicService.getTrack(); + currentLength = 0; + lv_label_set_text(txtTrack, track.data()); + } + + if (album != musicService.getAlbum()) { + album = musicService.getAlbum(); + currentLength = 0; + } + + if (playing != musicService.isPlaying()) { + playing = musicService.isPlaying(); + } + + // Because we increment this ourselves, + // we can't compare with the old data directly + // have to update it when there's actually new data + // just to avoid unnecessary draws that make UI choppy + if (lastLength != musicService.getProgress()) { + currentLength = musicService.getProgress(); + lastLength = currentLength; + UpdateLength(); + } + + if (totalLength != musicService.getTrackLength()) { + totalLength = musicService.getTrackLength(); + UpdateLength(); + } + + if (playing == Pinetime::Controllers::MusicService::MusicStatus::Playing) { + lv_label_set_text(txtPlayPause, "||"); + if (xTaskGetTickCount() - 1024 >= lastIncrement) { + + if (frameB) { + lv_img_set_src(imgDiscAnim, &disc_f_1); + } else { + lv_img_set_src(imgDiscAnim, &disc_f_2); + } + frameB = !frameB; + + if (currentLength < totalLength) { + currentLength += static_cast<int>((static_cast<float>(xTaskGetTickCount() - lastIncrement) / 1024.0f) * + musicService.getPlaybackSpeed()); + } else { + // Let's assume the getTrack finished, paused when the timer ends + // and there's no new getTrack being sent to us + // TODO: ideally this would be configurable + playing = false; + } + lastIncrement = xTaskGetTickCount(); + + UpdateLength(); + } + } else { + lv_label_set_text(txtPlayPause, ">"); + } + + return running; +} - if (m_artist != musicService.artist()) { - m_artist = musicService.artist(); - lv_label_set_text(txtArtist, m_artist.data()); +void Music::UpdateLength() { + if (totalLength > (99 * 60 * 60)) { + lv_label_set_text(txtTrackDuration, "Inf/Inf"); + } else if (totalLength > (99 * 60)) { + char timer[12]; + sprintf(timer, "%02d:%02d/%02d:%02d", + (currentLength / (60 * 60)) % 100, + ((currentLength % (60 * 60)) / 60) % 100, + (totalLength / (60 * 60)) % 100, + ((totalLength % (60 * 60)) / 60) % 100 + ); + lv_label_set_text(txtTrackDuration, timer); + } else { + char timer[12]; + sprintf(timer, "%02d:%02d/%02d:%02d", + (currentLength / 60) % 100, + (currentLength % 60) % 100, + (totalLength / 60) % 100, + (totalLength % 60) % 100 + ); + lv_label_set_text(txtTrackDuration, timer); + } +} + +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 (playing == Pinetime::Controllers::MusicService::MusicStatus::Playing) { + musicService.event(Controllers::MusicService::EVENT_MUSIC_PAUSE); + + // Let's assume it stops playing instantly + playing = Controllers::MusicService::NotPlaying; + } else { + musicService.event(Controllers::MusicService::EVENT_MUSIC_PLAY); + + // Let's assume it starts playing instantly + // TODO: In the future should check for BT connection for better UX + playing = Controllers::MusicService::Playing; + } + } else if (obj == btnNext) { + musicService.event(Controllers::MusicService::EVENT_MUSIC_NEXT); } - if (m_track != musicService.track()) { - m_track = musicService.track(); - lv_label_set_text(txtTrack, m_track.data()); + } +} + + +bool Music::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + switch (event) { + case TouchEvents::SwipeUp: { + displayVolumeButtons = true; + lv_obj_set_hidden(btnVolDown, !displayVolumeButtons); + lv_obj_set_hidden(btnVolUp, !displayVolumeButtons); + + lv_obj_set_hidden(btnNext, displayVolumeButtons); + lv_obj_set_hidden(btnPrev, displayVolumeButtons); + return true; } - if (m_album != musicService.album()) { - m_album = musicService.album(); + case TouchEvents::SwipeDown: { + displayVolumeButtons = false; + lv_obj_set_hidden(btnNext, displayVolumeButtons); + lv_obj_set_hidden(btnPrev, displayVolumeButtons); + + lv_obj_set_hidden(btnVolDown, !displayVolumeButtons); + lv_obj_set_hidden(btnVolUp, !displayVolumeButtons); + return true; } - if (m_status != musicService.status()) { - m_status = musicService.status(); + case TouchEvents::SwipeLeft: { + musicService.event(Controllers::MusicService::EVENT_MUSIC_NEXT); + return true; } - if (m_status == Pinetime::Controllers::MusicService::STATUS_MUSIC_PLAYING) { - lv_label_set_text(txtPlayPause, "||"); - } else { - lv_label_set_text(txtPlayPause, ">"); + case TouchEvents::SwipeRight: { + musicService.event(Controllers::MusicService::EVENT_MUSIC_PREV); + return true; } - - 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); - } + default: { + return true; } -} + } +} \ No newline at end of file diff --git a/src/displayapp/screens/Music.h b/src/displayapp/screens/Music.h index d43d31cc9adc2d89c6cec74d27322c56930bab27..81ba79359281ad2d1bdcd5e90ed1bd2312b0dd6c 100644 --- a/src/displayapp/screens/Music.h +++ b/src/displayapp/screens/Music.h @@ -1,3 +1,20 @@ +/* Copyright (C) 2020 JF, Adam Pigg, Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ #pragma once #include <cstdint> @@ -13,37 +30,66 @@ #include#include <libs/lvgl/src/lv_core/lv_style.h> #include <libs/lvgl/src/lv_core/lv_obj.h> #include "../../Version.h" +#include "displayapp/icons/music/disc.cpp" +#include "displayapp/icons/music/disc_f_1.cpp" +#include "displayapp/icons/music/disc_f_2.cpp" 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; + 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: + bool OnTouchEvent(TouchEvents event); + + void UpdateLength(); + + 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; + + lv_obj_t *imgDisc; + lv_obj_t *imgDiscAnim; + lv_obj_t *txtTrackDuration; + + /** For the spinning disc animation */ + bool frameB; + + bool displayVolumeButtons = false; + Pinetime::Controllers::MusicService &musicService; + + std::string artist; + std::string album; + std::string track; + + /** Total length in seconds */ + int totalLength; + /** Current length in seconds */ + int currentLength; + /** Last length */ + int lastLength; + /** Last time an animation update or timer was incremented */ + TickType_t lastIncrement; + + bool playing; + + /** Watchapp */ + bool running = true; }; } } diff --git a/src/displayapp/screens/Screen.h b/src/displayapp/screens/Screen.h index dbf81a440b5ad761d17215b843adde2b4cedde8b..6b1d0eec2d03c84b2b44cedf5f3e27cc9779eaff 100644 --- a/src/displayapp/screens/Screen.h +++ b/src/displayapp/screens/Screen.h @@ -9,16 +9,28 @@ class DisplayApp; namespace Screens { class Screen { public: - Screen(DisplayApp* app) : app{app} {} + explicit Screen(DisplayApp* app) : app{app} {} virtual ~Screen() = default; - // Return false if the app can be closed, true if it must continue to run + /** + * Most of the time, apps only react to events (touch events, for example). + * In this case you don't need to do anything in this method. + * + * For example, InfiniPaint does nothing in Refresh(). + * But, if you want to update your display periodically, draw an animation... + * you cannot do it in a touch event handler because these handlers are not + * called if the user does not touch the screen. + * + * That's why Refresh() is there: update the display periodically. + * + * @return false if the app can be closed, true if it must continue to run + **/ virtual bool Refresh() = 0; - // Return false if the button hasn't been handled by the app, true if it has been handled + /** @return false if the button hasn't been handled by the app, true if it has been handled */ virtual bool OnButtonPushed() { return false; } - // Return false if the event hasn't been handled by the app, true if it has been handled + /** @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; } diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index 8a3b8dbb7bc095bade7616fdbe1f5498d5c6aadf..867fdaebf734de941cdc393b1c615f139dc58b91 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -105,12 +105,12 @@ } std::unique_ptr<Screen> SystemInfo::CreateScreen2() { auto& bleAddr = bleController.Address(); - sprintf(t2, "BLE MAC: \n %2x:%2x:%2x:%2x:%2x:%2x", + sprintf(t2, "BLE MAC: \n %02x:%02x:%02x:%02x:%02x:%02x", bleAddr[5], bleAddr[4], bleAddr[3], bleAddr[2], bleAddr[1], bleAddr[0]); return std::unique_ptr<Screen>(new Screens::Label(app, t2)); } std::unique_ptr<Screen> SystemInfo::CreateScreen3() { - strncpy(t3, "Hello from\nthe developper!", 27); + strncpy(t3, "Hello from\nthe developer!", 27); return std::unique_ptr<Screen>(new Screens::Label(app, t3)); } diff --git a/src/displayapp/screens/Tab.cpp b/src/displayapp/screens/Tab.cpp index adc32578a76f112230e420e37a6bc89e7d8635e8..44b806c0a6fd9acd460078d8f2e62966e096f73d 100644 --- a/src/displayapp/screens/Tab.cpp +++ b/src/displayapp/screens/Tab.cpp @@ -1,13 +1,13 @@ #include <cstdio> #include <libs/date/includes/date/date.h> -#include <Components/DateTime/DateTimeController.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 "Tab.h" -#include <DisplayApp/DisplayApp.h> +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Tile.cpp b/src/displayapp/screens/Tile.cpp index deb88472eb8593bea8679b4912d47af878fc42ae..75fa6ef54f4f91bf055d73e86deb32f8dc9afad4 100644 --- a/src/displayapp/screens/Tile.cpp +++ b/src/displayapp/screens/Tile.cpp @@ -30,7 +30,7 @@ } } modal.reset(new Modal(app)); - btnm1 = lv_btnm_create(lv_scr_act(), NULL); + btnm1 = lv_btnm_create(lv_scr_act(), nullptr); lv_btnm_set_map(btnm1, btnm_map1); lv_obj_set_size(btnm1, LV_HOR_RES, LV_VER_RES); diff --git a/src/drivers/Cst816s.cpp b/src/drivers/Cst816s.cpp index f6816545f80d134bd98982c4d9b85dbdc593841e..94db3b34b23b98be53b266635eaa341b6134ed6c 100644 --- a/src/drivers/Cst816s.cpp +++ b/src/drivers/Cst816s.cpp @@ -37,7 +37,9 @@ Cst816S::TouchInfos Cst816S::GetTouchInfo() { Cst816S::TouchInfos info; - twiMaster.Read(twiAddress, 0, touchData, 63); + auto ret = twiMaster.Read(twiAddress, 0, touchData, 63); + if(ret != TwiMaster::ErrorCodes::NoError) return {}; + auto nbTouchPoints = touchData[2] & 0x0f; // uint8_t i = 0; diff --git a/src/drivers/Cst816s.h b/src/drivers/Cst816s.h index b115a6880978316e014e2a2079f201bda880c0f6..4569e82fa6a8782e8016dd734dada3ad0dc3ba8a 100644 --- a/src/drivers/Cst816s.h +++ b/src/drivers/Cst816s.h @@ -18,13 +18,13 @@ DoubleTap = 0x0B, LongPress = 0x0C }; struct TouchInfos { - uint16_t x; - uint16_t y; - uint8_t action; - uint8_t finger; - uint8_t pressure; - uint8_t area; - Gestures gesture; + uint16_t x = 0; + uint16_t y = 0; + uint8_t action = 0; + uint8_t finger = 0; + uint8_t pressure = 0; + uint8_t area = 0; + Gestures gesture = Gestures::None; bool isTouch = false; }; diff --git a/src/drivers/SpiNorFlash.cpp b/src/drivers/SpiNorFlash.cpp index 351a9dfc6f1d99d48a8873d1d366d1539faba42a..bd24834e851bf13a7c04667b61f33f2c2290bf70 100644 --- a/src/drivers/SpiNorFlash.cpp +++ b/src/drivers/SpiNorFlash.cpp @@ -12,7 +12,7 @@ } void SpiNorFlash::Init() { 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); + NRF_LOG_INFO("[SpiNorFlash] Manufacturer : %d, Memory type : %d, memory density : %d", device_id.manufacturer, device_id.type, device_id.density); } void SpiNorFlash::Uninit() { @@ -22,7 +22,7 @@ void SpiNorFlash::Sleep() { auto cmd = static_cast<uint8_t>(Commands::DeepPowerDown); spi.Write(&cmd, sizeof(uint8_t)); - NRF_LOG_INFO("[FLASH] Sleep") + NRF_LOG_INFO("[SpiNorFlash] Sleep") } void SpiNorFlash::Wakeup() { @@ -38,7 +38,7 @@ } else { NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: %d", id); } - NRF_LOG_INFO("[FLASH] Wakeup") + NRF_LOG_INFO("[SpiNorFlash] Wakeup") } SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() { diff --git a/src/drivers/TwiMaster.cpp b/src/drivers/TwiMaster.cpp index a9eb5d0c6d9cc33bafd24276999c25bf116c13d7..3ff8a9520e8009a6f4e22cafeb539ba424de2221 100644 --- a/src/drivers/TwiMaster.cpp +++ b/src/drivers/TwiMaster.cpp @@ -60,24 +60,53 @@ xSemaphoreGive(mutex); } -void TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) { +TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) { xSemaphoreTake(mutex, portMAX_DELAY); - Write(deviceAddress, ®isterAddress, 1, false); - Read(deviceAddress, data, size, true); + auto ret = ReadWithRetry(deviceAddress, registerAddress, data, size); xSemaphoreGive(mutex); + + return ret; } -void TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) { +TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) { ASSERT(size <= maxDataSize); xSemaphoreTake(mutex, portMAX_DELAY); + + auto ret = WriteWithRetry(deviceAddress, registerAddress, data, size); + xSemaphoreGive(mutex); + return ret; +} + +/* Execute a read transaction (composed of a write and a read operation). If one of these opeartion fails, + * it's retried once. If it fails again, an error is returned */ +TwiMaster::ErrorCodes TwiMaster::ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) { + TwiMaster::ErrorCodes ret; + ret = Write(deviceAddress, ®isterAddress, 1, false); + if(ret != ErrorCodes::NoError) + ret = Write(deviceAddress, ®isterAddress, 1, false); + + if(ret != ErrorCodes::NoError) return ret; + + ret = Read(deviceAddress, data, size, true); + if(ret != ErrorCodes::NoError) + ret = Read(deviceAddress, data, size, true); + + return ret; +} + +/* Execute a write transaction. If it fails, it is retried once. If it fails again, an error is returned. */ +TwiMaster::ErrorCodes TwiMaster::WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) { internalBuffer[0] = registerAddress; std::memcpy(internalBuffer+1, data, size); - Write(deviceAddress, internalBuffer, size+1, true); - xSemaphoreGive(mutex); + auto ret = Write(deviceAddress, internalBuffer, size+1, true); + if(ret != ErrorCodes::NoError) + ret = Write(deviceAddress, internalBuffer, size+1, true); + + return ret; } -void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool stop) { +TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool stop) { twiBaseAddress->ADDRESS = deviceAddress; twiBaseAddress->TASKS_RESUME = 0x1UL; twiBaseAddress->RXD.PTR = (uint32_t)buffer; @@ -88,7 +117,15 @@ while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR); twiBaseAddress->EVENTS_RXSTARTED = 0x0UL; - while(!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR); + txStartedCycleCount = DWT->CYCCNT; + uint32_t currentCycleCount; + while(!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR) { + currentCycleCount = DWT->CYCCNT; + if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) { + FixHwFreezed(); + return ErrorCodes::TransactionFailed; + } + } twiBaseAddress->EVENTS_LASTRX = 0x0UL; if (stop || twiBaseAddress->EVENTS_ERROR) { @@ -105,9 +142,10 @@ if (twiBaseAddress->EVENTS_ERROR) { twiBaseAddress->EVENTS_ERROR = 0x0UL; } + return ErrorCodes::NoError; } -void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, bool stop) { +TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, bool stop) { twiBaseAddress->ADDRESS = deviceAddress; twiBaseAddress->TASKS_RESUME = 0x1UL; twiBaseAddress->TXD.PTR = (uint32_t)data; @@ -118,7 +156,15 @@ while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR); twiBaseAddress->EVENTS_TXSTARTED = 0x0UL; - while(!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR); + txStartedCycleCount = DWT->CYCCNT; + uint32_t currentCycleCount; + while(!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR) { + currentCycleCount = DWT->CYCCNT; + if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) { + FixHwFreezed(); + return ErrorCodes::TransactionFailed; + } + } twiBaseAddress->EVENTS_LASTTX = 0x0UL; if (stop || twiBaseAddress->EVENTS_ERROR) { @@ -137,6 +183,8 @@ twiBaseAddress->EVENTS_ERROR = 0x0UL; uint32_t error = twiBaseAddress->ERRORSRC; twiBaseAddress->ERRORSRC = error; } + + return ErrorCodes::NoError; } void TwiMaster::Sleep() { @@ -152,3 +200,30 @@ void TwiMaster::Wakeup() { Init(); NRF_LOG_INFO("[TWIMASTER] Wakeup"); } + +/* Sometimes, the TWIM device just freeze and never set the event EVENTS_LASTTX. + * This method disable and re-enable the peripheral so that it works again. + * This is just a workaround, and it would be better if we could find a way to prevent + * this issue from happening. + * */ +void TwiMaster::FixHwFreezed() { + NRF_LOG_INFO("I2C device frozen, reinitializing it!"); + // Disable I²C + uint32_t twi_state = NRF_TWI1->ENABLE; + twiBaseAddress->ENABLE = TWIM_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos; + + NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) + | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) + | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) + | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) + | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos); + + NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) + | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) + | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) + | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) + | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos); + + // Re-enable I²C + twiBaseAddress->ENABLE = twi_state; +} diff --git a/src/drivers/TwiMaster.h b/src/drivers/TwiMaster.h index 9b6b5070e49ef407c3c84a27da78bdf7b3f94fb9..52e39098aef2239f194db013d61f6acd08c839bb 100644 --- a/src/drivers/TwiMaster.h +++ b/src/drivers/TwiMaster.h @@ -10,6 +10,7 @@ class TwiMaster { public: enum class Modules { TWIM1 }; enum class Frequencies {Khz100, Khz250, Khz400}; + enum class ErrorCodes {NoError, TransactionFailed}; struct Parameters { uint32_t frequency; uint8_t pinSda; @@ -19,15 +20,19 @@ TwiMaster(const Modules module, const Parameters& params); void Init(); - 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); + ErrorCodes Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size); + ErrorCodes 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); + ErrorCodes ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size); + ErrorCodes WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size); + + ErrorCodes Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop); + ErrorCodes Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop); + void FixHwFreezed(); NRF_TWIM_Type* twiBaseAddress; SemaphoreHandle_t mutex; const Modules module; @@ -35,7 +40,8 @@ const Parameters params; static constexpr uint8_t maxDataSize{8}; static constexpr uint8_t registerSize{1}; uint8_t internalBuffer[maxDataSize + registerSize]; - + uint32_t txStartedCycleCount = 0; + static constexpr uint32_t HwFreezedDelay{161000}; }; } } \ No newline at end of file diff --git a/src/graphics.cpp b/src/graphics.cpp index 9373a9b61612bb94be9bb05f22dcdb8985a884e3..288b5e9aa7763245ab6cb7ae6ed287f40373266a 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -11,15 +11,15 @@ #include #include <libraries/gpiote/app_gpiote.h> #include <hal/nrf_wdt.h> #include <cstring> -#include <Components/Gfx/Gfx.h> +#include <components/gfx/Gfx.h> #include <drivers/St7789.h> -#include <Components/Brightness/BrightnessController.h> +#include <components/brightness/BrightnessController.h> #if NRF_LOG_ENABLED -#include "Logging/NrfLogger.h" +#include "logging/NrfLogger.h" Pinetime::Logging::NrfLogger logger; #else -#include "Logging/DummyLogger.h" +#include "logging/DummyLogger.h" Pinetime::Logging::DummyLogger logger; #endif diff --git a/src/logging/NrfLogger.cpp b/src/logging/NrfLogger.cpp index 7ccacc82deb513ad11c20537a614936c20f9d29b..0d95c06a073bbfe4ce881e467d107daf7cbbffa2 100644 --- a/src/logging/NrfLogger.cpp +++ b/src/logging/NrfLogger.cpp @@ -19,10 +19,15 @@ } void NrfLogger::Process(void*) { NRF_LOG_INFO("Logger task started!"); + // Suppress endless loop diagnostic + #pragma clang diagnostic push + #pragma ide diagnostic ignored "EndlessLoop" while (1) { NRF_LOG_FLUSH(); vTaskDelay(100); // Not good for power consumption, it will wake up every 100ms... } + // Clear diagnostic suppression + #pragma clang diagnostic pop } void NrfLogger::Resume() { diff --git a/src/systemtask/SystemMonitor.h b/src/systemtask/SystemMonitor.h index ec1fd817a035ecf3f55cdac3b02ecff686431f82..029a13641a1481a6e3f78c240d1efa968053bf28 100644 --- a/src/systemtask/SystemMonitor.h +++ b/src/systemtask/SystemMonitor.h @@ -27,7 +27,7 @@ public: void Process() const { if(xTaskGetTickCount() - lastTick > 10000) { NRF_LOG_INFO("---------------------------------------\nFree heap : %d", xPortGetFreeHeapSize()); - auto nb = uxTaskGetSystemState(tasksStatus, 10, NULL); + auto nb = uxTaskGetSystemState(tasksStatus, 10, nullptr); 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) diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 68f8ab53dc2eb7ee94dea5a0e6be67b0621c98ae..dac4ce29a3adfffe5a27514562a7f52c842d575f 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -11,8 +11,9 @@ #include #include <host/ble_gap.h> #include <host/util/util.h> #include <drivers/InternalFlash.h> -#include "../main.h" +#include "main.h" #include "components/ble/NimbleController.h" +#include "../BootloaderVersion.h" using namespace Pinetime::System; @@ -36,7 +37,7 @@ twiMaster{twiMaster}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController}, bleController{bleController}, dateTimeController{dateTimeController}, watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager}, nimbleController(*this, bleController,dateTimeController, notificationManager, batteryController, spiNorFlash) { - systemTaksMsgQueue = xQueueCreate(10, 1); + systemTasksMsgQueue = xQueueCreate(10, 1); } void SystemTask::Start() { @@ -100,9 +101,12 @@ idleTimer = xTimerCreate ("idleTimer", idleTime, pdFALSE, this, IdleTimerCallback); xTimerStart(idleTimer, 0); + // Suppress endless loop diagnostic + #pragma clang diagnostic push + #pragma ide diagnostic ignored "EndlessLoop" while(true) { uint8_t msg; - if (xQueueReceive(systemTaksMsgQueue, &msg, isSleeping?2500 : 1000)) { + if (xQueueReceive(systemTasksMsgQueue, &msg, isSleeping ? 2500 : 1000)) { Messages message = static_cast<Messages >(msg); switch(message) { case Messages::GoToRunning: @@ -158,7 +162,11 @@ case Messages::OnButtonEvent: ReloadIdleTimer(); break; case Messages::OnDisplayTaskSleeping: - spiNorFlash.Sleep(); + if(BootloaderVersion::IsValid()) { + // First versions of the bootloader do not expose their version and cannot initialize the SPI NOR FLASH + // if it's in sleep mode. Avoid bricked device by disabling sleep mode on these versions. + spiNorFlash.Sleep(); + } lcd.Sleep(); touchPanel.Sleep(); @@ -191,6 +199,8 @@ if(!nrf_gpio_pin_read(pinButton)) watchdog.Kick(); } + // Clear diagnostic suppression + #pragma clang diagnostic pop } void SystemTask::OnButtonPushed() { @@ -228,10 +238,10 @@ isGoingToSleep = true; } BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; - xQueueSendFromISR(systemTaksMsgQueue, &msg, &xHigherPriorityTaskWoken); + xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken); if (xHigherPriorityTaskWoken) { /* Actual macro used here is port specific. */ - // TODO : should I do something here? + // TODO: should I do something here? } } diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 1be28e3f485b1bd4a9dd40697b0c33c739623235..6ef0cfbf8a184c2f17a747d28bedeb7892333025 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -54,7 +54,7 @@ Pinetime::Controllers::Battery& batteryController; std::unique_ptr<Pinetime::Applications::DisplayApp> displayApp; Pinetime::Controllers::Ble& bleController; Pinetime::Controllers::DateTime& dateTimeController; - QueueHandle_t systemTaksMsgQueue; + QueueHandle_t systemTasksMsgQueue; std::atomic<bool> isSleeping{false}; std::atomic<bool> isGoingToSleep{false}; std::atomic<bool> isWakingUp{false};