Fix conflicts

This commit is contained in:
JF 2020-10-27 21:55:18 +01:00
commit 7de43a1660
42 changed files with 1217 additions and 407 deletions

View File

@ -7,6 +7,10 @@
<option name="INDENT_INSIDE_CODE_BLOCK" value="2" /> <option name="INDENT_INSIDE_CODE_BLOCK" value="2" />
<option name="INDENT_DIRECTIVE_AS_CODE" value="true" /> <option name="INDENT_DIRECTIVE_AS_CODE" value="true" />
<option name="SPACE_BEFORE_TEMPLATE_DECLARATION_LT" 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> </Objective-C>
<codeStyleSettings language="ObjectiveC"> <codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="140" /> <option name="RIGHT_MARGIN" value="140" />

View File

@ -1,6 +1,6 @@
# Memory analysis # Memory analysis
## FreeRTOS heap and task stack ## 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,...). 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. 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 @@ add_definitions(-D__STACK_SIZE=8192)
*TODO* *TODO*
#Tools #Tools
- https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC. - https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC.

View File

@ -18,7 +18,7 @@ CMake configures the project according to variables you specify the command line
Variable | Description | Example| 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`| **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` **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` **CMAKE_BUILD_TYPE**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`

View File

@ -42,7 +42,7 @@ This firmware is intended to be used with our [MCUBoot-based bootloader](../boot
The following files are not directly usable by the bootloader: 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.hex** : Firmware in Intel HEX file format.
- **pinetime-mcuboot-app.bin** : Firmware in binary format. - **pinetime-mcuboot-app.bin** : Firmware in binary format.
- **pinetime-mcuboot-app.map** : Map file containing all the symbols, addresses in memory,... - **pinetime-mcuboot-app.map** : Map file containing all the symbols, addresses in memory,...

26
src/BootloaderVersion.cpp Normal file
View File

@ -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;
}

12
src/BootloaderVersion.h Normal file
View File

@ -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();
};
}

View File

@ -324,6 +324,7 @@ list(APPEND IMAGE_FILES
) )
list(APPEND SOURCE_FILES list(APPEND SOURCE_FILES
BootloaderVersion.cpp
logging/NrfLogger.cpp logging/NrfLogger.cpp
displayapp/DisplayApp.cpp displayapp/DisplayApp.cpp
displayapp/screens/Screen.cpp displayapp/screens/Screen.cpp
@ -401,6 +402,7 @@ list(APPEND GRAPHICS_SOURCE_FILES
) )
set(INCLUDE_FILES set(INCLUDE_FILES
BootloaderVersion.h
logging/Logger.h logging/Logger.h
logging/NrfLogger.h logging/NrfLogger.h
displayapp/DisplayApp.h displayapp/DisplayApp.h
@ -558,7 +560,7 @@ link_directories(
) )
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(-DCONFIG_GPIO_AS_PINRESET)
add_definitions(-DDEBUG) add_definitions(-DDEBUG)
add_definitions(-DNIMBLE_CFG_CONTROLLER) add_definitions(-DNIMBLE_CFG_CONTROLLER)

View File

@ -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 <systemtask/SystemTask.h>
#include "MusicService.h" #include "MusicService.h"
int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { 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); return musicService->OnCommand(conn_handle, attr_handle, ctxt);
} }
Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask &system) : m_system(system) Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask &system) : m_system(system) {
{ msUuid.value[11] = msId[0];
msUuid.value[11] = msId[0]; msUuid.value[12] = msId[1];
msUuid.value[12] = msId[1]; msEventCharUuid.value[11] = msEventCharId[0];
msEventCharUuid.value[11] = msEventCharId[0]; msEventCharUuid.value[12] = msEventCharId[1];
msEventCharUuid.value[12] = msEventCharId[1]; msStatusCharUuid.value[11] = msStatusCharId[0];
msStatusCharUuid.value[11] = msStatusCharId[0]; msStatusCharUuid.value[12] = msStatusCharId[1];
msStatusCharUuid.value[12] = msStatusCharId[1]; msTrackCharUuid.value[11] = msTrackCharId[0];
msTrackCharUuid.value[11] = msTrackCharId[0]; msTrackCharUuid.value[12] = msTrackCharId[1];
msTrackCharUuid.value[12] = msTrackCharId[1]; msArtistCharUuid.value[11] = msArtistCharId[0];
msArtistCharUuid.value[11] = msArtistCharId[0]; msArtistCharUuid.value[12] = msArtistCharId[1];
msArtistCharUuid.value[12] = msArtistCharId[1]; msAlbumCharUuid.value[11] = msAlbumCharId[0];
msAlbumCharUuid.value[11] = msAlbumCharId[0]; msAlbumCharUuid.value[12] = msAlbumCharId[1];
msAlbumCharUuid.value[12] = msAlbumCharId[1]; msPositionCharUuid.value[11] = msPositionCharId[0];
msPositionCharUuid.value[12] = msPositionCharId[1];
characteristicDefinition[0] = { .uuid = (ble_uuid_t*)(&msEventCharUuid), msTotalLengthCharUuid.value[11] = msTotalLengthCharId[0];
.access_cb = MSCallback, msTotalLengthCharUuid.value[12] = msTotalLengthCharId[1];
.arg = this, msTrackNumberCharUuid.value[11] = msTrackNumberCharId[0];
.flags = BLE_GATT_CHR_F_NOTIFY, msTrackNumberCharUuid.value[12] = msTrackNumberCharId[1];
.val_handle = &m_eventHandle msTrackTotalCharUuid.value[11] = msTrackTotalCharId[0];
}; msTrackTotalCharUuid.value[12] = msTrackTotalCharId[1];
characteristicDefinition[1] = { .uuid = (ble_uuid_t*)(&msStatusCharUuid), msPlaybackSpeedCharUuid.value[11] = msPlaybackSpeedCharId[0];
.access_cb = MSCallback, msPlaybackSpeedCharUuid.value[12] = msPlaybackSpeedCharId[1];
.arg = this, msRepeatCharUuid.value[11] = msRepeatCharId[0];
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ msRepeatCharUuid.value[12] = msRepeatCharId[1];
}; msShuffleCharUuid.value[11] = msShuffleCharId[0];
characteristicDefinition[2] = { .uuid = (ble_uuid_t*)(&msTrackCharUuid), msShuffleCharUuid.value[12] = msShuffleCharId[1];
.access_cb = MSCallback,
.arg = this, characteristicDefinition[0] = {.uuid = (ble_uuid_t *) (&msEventCharUuid),
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ .access_cb = MSCallback,
}; .arg = this,
characteristicDefinition[3] = { .uuid = (ble_uuid_t*)(&msArtistCharUuid), .flags = BLE_GATT_CHR_F_NOTIFY,
.access_cb = MSCallback, .val_handle = &eventHandle
.arg = this, };
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ characteristicDefinition[1] = {.uuid = (ble_uuid_t *) (&msStatusCharUuid),
}; .access_cb = MSCallback,
characteristicDefinition[4] = { .uuid = (ble_uuid_t*)(&msAlbumCharUuid), .arg = this,
.access_cb = MSCallback, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
.arg = this, };
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ characteristicDefinition[2] = {.uuid = (ble_uuid_t *) (&msTrackCharUuid),
}; .access_cb = MSCallback,
characteristicDefinition[5] = {0}; .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
serviceDefinition[0] = { };
.type = BLE_GATT_SVC_TYPE_PRIMARY, characteristicDefinition[3] = {.uuid = (ble_uuid_t *) (&msArtistCharUuid),
.uuid = (ble_uuid_t *) &msUuid, .access_cb = MSCallback,
.characteristics = characteristicDefinition .arg = this,
}; .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
serviceDefinition[1] = {0}; };
characteristicDefinition[4] = {.uuid = (ble_uuid_t *) (&msAlbumCharUuid),
m_artist = "Waiting for"; .access_cb = MSCallback,
m_album = ""; .arg = this,
m_track = "track information..."; .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; int res = 0;
res = ble_gatts_count_cfg(serviceDefinition); res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0); ASSERT(res == 0);
res = ble_gatts_add_svcs(serviceDefinition); res = ble_gatts_add_svcs(serviceDefinition);
ASSERT(res == 0); ASSERT(res == 0);
} }
int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, 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) { if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
size_t notifSize = OS_MBUF_PKTLEN(ctxt->om); size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
uint8_t data[notifSize + 1]; uint8_t data[notifSize + 1];
data[notifSize] = '\0'; data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data); os_mbuf_copydata(ctxt->om, 0, notifSize, data);
char *s = (char *) &data[0]; char *s = (char *) &data[0];
NRF_LOG_INFO("DATA : %s", s); NRF_LOG_INFO("DATA : %s", s);
if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msArtistCharUuid) == 0) { if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msArtistCharUuid) == 0) {
m_artist = s; artistName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msTrackCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTrackCharUuid) == 0) {
m_track = s; trackName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msAlbumCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msAlbumCharUuid) == 0) {
m_album = s; albumName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msStatusCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msStatusCharUuid) == 0) {
m_status = s[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; return 0;
} }
std::string Pinetime::Controllers::MusicService::album() std::string Pinetime::Controllers::MusicService::getAlbum() {
{ return albumName;
return m_album;
} }
std::string Pinetime::Controllers::MusicService::artist() std::string Pinetime::Controllers::MusicService::getArtist() {
{ return artistName;
return m_artist;
} }
std::string Pinetime::Controllers::MusicService::track() std::string Pinetime::Controllers::MusicService::getTrack() {
{ return trackName;
return m_track;
} }
unsigned char Pinetime::Controllers::MusicService::status() bool Pinetime::Controllers::MusicService::isPlaying() {
{ return playing;
return m_status;
} }
void Pinetime::Controllers::MusicService::event(char event) float Pinetime::Controllers::MusicService::getPlaybackSpeed() {
{ return playbackSpeed;
auto *om = ble_hs_mbuf_from_flat(&event, 1); }
uint16_t connectionHandle = m_system.nimble().connHandle(); void Pinetime::Controllers::MusicService::event(char event) {
auto *om = ble_hs_mbuf_from_flat(&event, 1);
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return; uint16_t connectionHandle = m_system.nimble().connHandle();
}
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
ble_gattc_notify_custom(connectionHandle, m_eventHandle, om); return;
}
ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
int Pinetime::Controllers::MusicService::getProgress() {
return trackProgress;
}
int Pinetime::Controllers::MusicService::getTrackLength() {
return trackLength;
} }

View File

@ -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 #pragma once
#include <cstdint> #include <cstdint>
@ -14,78 +31,135 @@ namespace Pinetime {
class SystemTask; class SystemTask;
} }
namespace Controllers { namespace Controllers {
class MusicService { class MusicService {
public: public:
MusicService(Pinetime::System::SystemTask &system); explicit MusicService(Pinetime::System::SystemTask &system);
void Init();
int OnCommand(uint16_t conn_handle, uint16_t attr_handle, void Init();
struct ble_gatt_access_ctxt *ctxt);
int OnCommand(uint16_t conn_handle, uint16_t attr_handle,
std::string artist(); struct ble_gatt_access_ctxt *ctxt);
std::string track();
std::string album(); void event(char event);
unsigned char status();
std::string getArtist();
void event(char event);
std::string getTrack();
static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00; std::string getAlbum();
static const char EVENT_MUSIC_PAUSE = 0x01;
static const char EVENT_MUSIC_NEXT = 0x03; int getProgress();
static const char EVENT_MUSIC_PREV = 0x04;
static const char EVENT_MUSIC_VOLUP = 0x05; int getTrackLength();
static const char EVENT_MUSIC_VOLDOWN = 0x06;
static const char STATUS_MUSIC_PAUSED = 0x00; float getPlaybackSpeed();
static const char STATUS_MUSIC_PLAYING = 0x01;
bool isPlaying();
private:
static constexpr uint8_t msId[2] = {0x00, 0x01}; static const char EVENT_MUSIC_OPEN = 0xe0;
static constexpr uint8_t msEventCharId[2] = {0x00, 0x02}; static const char EVENT_MUSIC_PLAY = 0x00;
static constexpr uint8_t msStatusCharId[2] = {0x00, 0x03}; static const char EVENT_MUSIC_PAUSE = 0x01;
static constexpr uint8_t msArtistCharId[2] = {0x00, 0x04}; static const char EVENT_MUSIC_NEXT = 0x03;
static constexpr uint8_t msTrackCharId[2] = {0x00, 0x05}; static const char EVENT_MUSIC_PREV = 0x04;
static constexpr uint8_t msAlbumCharId[2] = {0x00, 0x06}; static const char EVENT_MUSIC_VOLUP = 0x05;
static const char EVENT_MUSIC_VOLDOWN = 0x06;
ble_uuid128_t msUuid {
.u = { .type = BLE_UUID_TYPE_128 }, enum MusicStatus {
.value = MUSIC_SERVICE_UUID_BASE NotPlaying = 0x00,
}; Playing = 0x01
};
ble_uuid128_t msEventCharUuid { private:
.u = { .type = BLE_UUID_TYPE_128 }, static constexpr uint8_t msId[2] = {0x00, 0x01};
.value = MUSIC_SERVICE_UUID_BASE static constexpr uint8_t msEventCharId[2] = {0x00, 0x02};
}; static constexpr uint8_t msStatusCharId[2] = {0x00, 0x03};
ble_uuid128_t msStatusCharUuid { static constexpr uint8_t msArtistCharId[2] = {0x00, 0x04};
.u = { .type = BLE_UUID_TYPE_128 }, static constexpr uint8_t msTrackCharId[2] = {0x00, 0x05};
.value = MUSIC_SERVICE_UUID_BASE static constexpr uint8_t msAlbumCharId[2] = {0x00, 0x06};
}; static constexpr uint8_t msPositionCharId[2] = {0x00, 0x07};
ble_uuid128_t msArtistCharUuid { static constexpr uint8_t msTotalLengthCharId[2] = {0x00, 0x08};
.u = { .type = BLE_UUID_TYPE_128 }, static constexpr uint8_t msTrackNumberCharId[2] = {0x00, 0x09};
.value = MUSIC_SERVICE_UUID_BASE static constexpr uint8_t msTrackTotalCharId[2] = {0x00, 0x0a};
}; static constexpr uint8_t msPlaybackSpeedCharId[2] = {0x00, 0x0b};
ble_uuid128_t msTrackCharUuid { static constexpr uint8_t msRepeatCharId[2] = {0x00, 0x0c};
.u = { .type = BLE_UUID_TYPE_128 }, static constexpr uint8_t msShuffleCharId[2] = {0x00, 0x0d};
.value = MUSIC_SERVICE_UUID_BASE
}; ble_uuid128_t msUuid{
ble_uuid128_t msAlbumCharUuid { .u = {.type = BLE_UUID_TYPE_128},
.u = { .type = BLE_UUID_TYPE_128 }, .value = MUSIC_SERVICE_UUID_BASE
.value = MUSIC_SERVICE_UUID_BASE };
};
ble_uuid128_t msEventCharUuid{
struct ble_gatt_chr_def characteristicDefinition[6]; .u = {.type = BLE_UUID_TYPE_128},
struct ble_gatt_svc_def serviceDefinition[2]; .value = MUSIC_SERVICE_UUID_BASE
};
uint16_t m_eventHandle; ble_uuid128_t msStatusCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
std::string m_artist; .value = MUSIC_SERVICE_UUID_BASE
std::string m_album; };
std::string m_track; ble_uuid128_t msArtistCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
unsigned char m_status; .value = MUSIC_SERVICE_UUID_BASE
};
Pinetime::System::SystemTask& m_system; 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;
}; };
} }
} }

View File

@ -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
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

View File

@ -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
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

View File

@ -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
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

View File

@ -11,15 +11,15 @@ void slider_event_cb(lv_obj_t * slider, lv_event_t event) {
} }
Brightness::Brightness(Pinetime::Applications::DisplayApp *app, Controllers::BrightnessController& brightness) : Screen(app), brightness{brightness} { 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_user_data(slider, this);
lv_obj_set_width(slider, LV_DPI * 2); 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_obj_set_event_cb(slider, slider_event_cb);
lv_slider_set_range(slider, 0, 2); lv_slider_set_range(slider, 0, 2);
lv_slider_set_value(slider, LevelToInt(brightness.Level()), LV_ANIM_OFF); 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_label_set_text(slider_label, LevelToString(brightness.Level()));
lv_obj_set_auto_realign(slider_label, true); lv_obj_set_auto_realign(slider_label, true);
lv_obj_align(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); lv_obj_align(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);

View File

@ -34,15 +34,15 @@ Clock::Clock(DisplayApp* app,
displayedChar[3] = 0; displayedChar[3] = 0;
displayedChar[4] = 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_label_set_text(batteryIcon, Symbols::batteryFull);
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 2); 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_label_set_text(batteryPlug, Symbols::plug);
lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); 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_label_set_text(bleIcon, Symbols::bluetooth);
lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0);
@ -50,14 +50,15 @@ Clock::Clock(DisplayApp* app,
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0); 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); 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_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); 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; backgroundLabel->user_data = this;
lv_obj_set_click(backgroundLabel, true); lv_obj_set_click(backgroundLabel, true);
lv_obj_set_event_cb(backgroundLabel, event_handler); lv_obj_set_event_cb(backgroundLabel, event_handler);
@ -67,23 +68,23 @@ Clock::Clock(DisplayApp* app,
lv_label_set_text(backgroundLabel, ""); 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_label_set_text(heartbeatIcon, Symbols::heartBeat);
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); 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_label_set_text(heartbeatValue, "0");
lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 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_label_set_text(heartbeatBpm, "BPM");
lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); 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_label_set_text(stepValue, "0");
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); 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_label_set_text(stepIcon, Symbols::shoe);
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);
} }

View File

@ -9,7 +9,7 @@ extern lv_font_t jetbrains_mono_bold_20;
DropDownDemo::DropDownDemo(Pinetime::Applications::DisplayApp *app) : Screen(app) { DropDownDemo::DropDownDemo(Pinetime::Applications::DisplayApp *app) : Screen(app) {
// Create the dropdown object, with many item, and fix its height // 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" lv_ddlist_set_options(ddlist, "Apple\n"
"Banana\n" "Banana\n"
"Orange\n" "Orange\n"
@ -24,7 +24,7 @@ DropDownDemo::DropDownDemo(Pinetime::Applications::DisplayApp *app) : Screen(app
lv_ddlist_set_fix_width(ddlist, 150); lv_ddlist_set_fix_width(ddlist, 150);
lv_ddlist_set_draw_arrow(ddlist, true); lv_ddlist_set_draw_arrow(ddlist, true);
lv_ddlist_set_fix_height(ddlist, 150); 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() { DropDownDemo::~DropDownDemo() {

View File

@ -10,19 +10,19 @@ extern lv_font_t jetbrains_mono_bold_20;
FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::Ble& bleController) : FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::Ble& bleController) :
Screen(app), bleController{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_label_set_text(titleLabel, "Firmware update");
lv_obj_set_auto_realign(titleLabel, true); 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_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_anim_time(bar1, 10);
lv_bar_set_range(bar1, 0, 100); lv_bar_set_range(bar1, 0, 100);
lv_bar_set_value(bar1, 0, LV_ANIM_OFF); 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_label_set_text(percentLabel, "");
lv_obj_set_auto_realign(percentLabel, true); lv_obj_set_auto_realign(percentLabel, true);
lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60); lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60);

View File

@ -20,20 +20,20 @@ namespace {
FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app, FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app,
Pinetime::Controllers::FirmwareValidator &validator) Pinetime::Controllers::FirmwareValidator &validator)
: Screen{app}, validator{validator} { : Screen{app}, validator{validator} {
labelVersionInfo = lv_label_create(lv_scr_act(), NULL); labelVersionInfo = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelVersionInfo, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); lv_obj_align(labelVersionInfo, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_label_set_text(labelVersionInfo, "Version : "); lv_label_set_text(labelVersionInfo, "Version : ");
lv_label_set_align(labelVersionInfo, LV_LABEL_ALIGN_LEFT); 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_obj_align(labelVersionValue, labelVersionInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_label_set_recolor(labelVersionValue, true); lv_label_set_recolor(labelVersionValue, true);
sprintf(version, "%ld.%ld.%ld", Version::Major(), Version::Minor(), Version::Patch()); sprintf(version, "%ld.%ld.%ld", Version::Major(), Version::Minor(), Version::Patch());
lv_label_set_text(labelVersionValue, version); lv_label_set_text(labelVersionValue, version);
labelIsValidated = lv_label_create(lv_scr_act(), NULL); labelIsValidated = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelIsValidated, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 50); lv_obj_align(labelIsValidated, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 50);
lv_label_set_recolor(labelIsValidated, true); lv_label_set_recolor(labelIsValidated, true);
lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK); lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK);
lv_obj_set_width(labelIsValidated, 240); lv_obj_set_width(labelIsValidated, 240);
@ -44,21 +44,21 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app,
lv_label_set_text(labelIsValidated, lv_label_set_text(labelIsValidated,
"Please #00ff00 Validate# this version or\n#ff0000 Reset# to rollback to the previous version."); "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); lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
buttonValidate->user_data = this; buttonValidate->user_data = this;
lv_obj_set_event_cb(buttonValidate, ButtonEventHandler); 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_recolor(labelButtonValidate, true);
lv_label_set_text(labelButtonValidate, "#00ff00 Validate#"); 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; 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); 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_recolor(labelButtonReset, true);
lv_label_set_text(labelButtonReset, "#ff0000 Reset#"); lv_label_set_text(labelButtonReset, "#ff0000 Reset#");
} }

View File

@ -25,11 +25,11 @@ Gauge::Gauge(Pinetime::Applications::DisplayApp *app) : Screen(app) {
needle_colors[0] = LV_COLOR_ORANGE; needle_colors[0] = LV_COLOR_ORANGE;
/*Create a gauge*/ /*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_style(gauge1, LV_GAUGE_STYLE_MAIN, &style);
lv_gauge_set_needle_count(gauge1, 1, needle_colors); lv_gauge_set_needle_count(gauge1, 1, needle_colors);
lv_obj_set_size(gauge1, 180, 180); 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_scale(gauge1, 360, 60, 0);
lv_gauge_set_range(gauge1, 0, 59); lv_gauge_set_range(gauge1, 0, 59);

View File

@ -7,9 +7,9 @@ using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_extrabold_compressed; extern lv_font_t jetbrains_mono_extrabold_compressed;
extern lv_font_t jetbrains_mono_bold_20; 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); app->SetTouchMode(DisplayApp::TouchModes::Polling);
std::fill(b, b+bufferSize, LV_COLOR_WHITE); std::fill(b, b + bufferSize, LV_COLOR_WHITE);
} }
InfiniPaint::~InfiniPaint() { InfiniPaint::~InfiniPaint() {
@ -33,10 +33,10 @@ bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
bool InfiniPaint::OnTouchEvent(uint16_t x, uint16_t y) { bool InfiniPaint::OnTouchEvent(uint16_t x, uint16_t y) {
lv_area_t area; lv_area_t area;
area.x1 = x-(width/2); area.x1 = x - (width / 2);
area.y1 = y-(height/2); area.y1 = y - (height / 2);
area.x2 = x+(width/2)-1; area.x2 = x + (width / 2) - 1;
area.y2 = y+(height/2)-1; area.y2 = y + (height / 2) - 1;
lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None);
lvgl.FlushDisplay(&area, b); lvgl.FlushDisplay(&area, b);
return true; return true;

View File

@ -11,24 +11,28 @@
namespace Pinetime { namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class InfiniPaint : public Screen{ class InfiniPaint : public Screen {
public: public:
InfiniPaint(DisplayApp* app, Pinetime::Components::LittleVgl& lvgl); InfiniPaint(DisplayApp* app, Pinetime::Components::LittleVgl& lvgl);
~InfiniPaint() override;
~InfiniPaint() override;
bool Refresh() override;
bool OnButtonPushed() override; bool Refresh() override;
bool OnTouchEvent(TouchEvents event) override;
bool OnTouchEvent(uint16_t x, uint16_t y) override; bool OnButtonPushed() override;
private: bool OnTouchEvent(TouchEvents event) override;
Pinetime::Components::LittleVgl& lvgl;
static constexpr uint16_t width = 10; bool OnTouchEvent(uint16_t x, uint16_t y) override;
static constexpr uint16_t height = 10;
static constexpr uint16_t bufferSize = width*height; private:
lv_color_t b[bufferSize]; Pinetime::Components::LittleVgl& lvgl;
bool running = true; 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

@ -4,7 +4,7 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
Label::Label(Pinetime::Applications::DisplayApp *app, const char *text) : Screen(app), text{text} { 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_label_set_align(label, LV_LABEL_ALIGN_LEFT);
lv_obj_set_size(label, 240, 240); lv_obj_set_size(label, 240, 240);
lv_label_set_text(label, text); lv_label_set_text(label, text);

View File

@ -17,14 +17,14 @@ Meter::Meter(Pinetime::Applications::DisplayApp *app) : Screen(app) {
style_lmeter.body.padding.left = 16; /*Line length*/ style_lmeter.body.padding.left = 16; /*Line length*/
/*Create a line meter */ /*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_range(lmeter, 0, 60); /*Set the range*/
lv_lmeter_set_value(lmeter, value); /*Set the current value*/ lv_lmeter_set_value(lmeter, value); /*Set the current value*/
lv_lmeter_set_angle_offset(lmeter, 180); lv_lmeter_set_angle_offset(lmeter, 180);
lv_lmeter_set_scale(lmeter, 360, 60); /*Set the angle and number of lines*/ 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_lmeter_set_style(lmeter, LV_LMETER_STYLE_MAIN, &style_lmeter); /*Apply the new style*/
lv_obj_set_size(lmeter, 150, 150); 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);
} }

View File

@ -54,7 +54,7 @@ void Modal::Show(const char* msg) {
modal_style.body.main_color = modal_style.body.grad_color = LV_COLOR_BLACK; modal_style.body.main_color = modal_style.body.grad_color = LV_COLOR_BLACK;
modal_style.body.opa = LV_OPA_50; 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_style(obj, &modal_style);
lv_obj_set_pos(obj, 0, 0); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_HOR_RES, LV_VER_RES); lv_obj_set_size(obj, LV_HOR_RES, LV_VER_RES);
@ -63,10 +63,10 @@ void Modal::Show(const char* msg) {
static const char * btns2[] = {"Ok", ""}; static const char * btns2[] = {"Ok", ""};
/* Create the message box as a child of the modal background */ /* 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_add_btns(mbox, btns2);
lv_mbox_set_text(mbox, msg); 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); lv_obj_set_event_cb(mbox, Modal::mbox_event_cb);
mbox->user_data = this; mbox->user_data = this;

View File

@ -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 <libs/lvgl/lvgl.h>
#include "Music.h" #include "Music.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_extrabold_compressed; extern lv_font_t jetbrains_mono_extrabold_compressed;
extern lv_font_t jetbrains_mono_bold_20; extern lv_font_t jetbrains_mono_bold_20;
static void event_handler(lv_obj_t * obj, lv_event_t event) static void event_handler(lv_obj_t *obj, lv_event_t event) {
{ Music *screen = static_cast<Music *>(obj->user_data);
Music* screen = static_cast<Music *>(obj->user_data);
screen->OnObjectEvent(obj, event); 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) { Music::Music(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::MusicService &music) : Screen(app), musicService(music) {
lv_obj_t * label; lv_obj_t *label;
btnVolDown = lv_btn_create(lv_scr_act(), NULL); btnVolDown = lv_btn_create(lv_scr_act(), nullptr);
btnVolDown->user_data = this; btnVolDown->user_data = this;
lv_obj_set_event_cb(btnVolDown, event_handler); lv_obj_set_event_cb(btnVolDown, event_handler);
lv_obj_align(btnVolDown, NULL, LV_ALIGN_IN_TOP_LEFT, 10, 10); lv_obj_set_size(btnVolDown, LV_HOR_RES / 3, 80);
label = lv_label_create(btnVolDown, NULL); lv_obj_align(btnVolDown, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
lv_label_set_text(label, "v-"); label = lv_label_create(btnVolDown, nullptr);
lv_label_set_text(label, "V-");
btnVolUp = lv_btn_create(lv_scr_act(), NULL); lv_obj_set_hidden(btnVolDown, !displayVolumeButtons);
btnVolUp->user_data = this;
lv_obj_set_event_cb(btnVolUp, event_handler); btnVolUp = lv_btn_create(lv_scr_act(), nullptr);
lv_obj_align(btnVolUp, NULL, LV_ALIGN_IN_TOP_RIGHT, -10, 10); btnVolUp->user_data = this;
label = lv_label_create(btnVolUp, NULL); lv_obj_set_event_cb(btnVolUp, event_handler);
lv_label_set_text(label, "v+"); lv_obj_set_size(btnVolUp, LV_HOR_RES / 3, 80);
lv_obj_align(btnVolUp, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
btnPrev = lv_btn_create(lv_scr_act(), NULL); label = lv_label_create(btnVolUp, nullptr);
btnPrev->user_data = this; lv_label_set_text(label, "V+");
lv_obj_set_event_cb(btnPrev, event_handler); lv_obj_set_hidden(btnVolDown, !displayVolumeButtons);
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); btnPrev = lv_btn_create(lv_scr_act(), nullptr);
label = lv_label_create(btnPrev, NULL); btnPrev->user_data = this;
lv_label_set_text(label, "<<"); lv_obj_set_event_cb(btnPrev, event_handler);
lv_obj_set_size(btnPrev, LV_HOR_RES / 3, 80);
btnPlayPause = lv_btn_create(lv_scr_act(), NULL); lv_obj_align(btnPrev, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
btnPlayPause->user_data = this; label = lv_label_create(btnPrev, nullptr);
lv_obj_set_event_cb(btnPlayPause, event_handler); lv_label_set_text(label, "<<");
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); btnNext = lv_btn_create(lv_scr_act(), nullptr);
txtPlayPause = lv_label_create(btnPlayPause, NULL); btnNext->user_data = this;
lv_label_set_text(txtPlayPause, ">"); lv_obj_set_event_cb(btnNext, event_handler);
lv_obj_set_size(btnNext, LV_HOR_RES / 3, 80);
btnNext = lv_btn_create(lv_scr_act(), NULL); lv_obj_align(btnNext, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
btnNext->user_data = this; label = lv_label_create(btnNext, nullptr);
lv_obj_set_event_cb(btnNext, event_handler); lv_label_set_text(label, ">>");
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); btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
label = lv_label_create(btnNext, NULL); btnPlayPause->user_data = this;
lv_label_set_text(label, ">>"); lv_obj_set_event_cb(btnPlayPause, event_handler);
lv_obj_set_size(btnPlayPause, LV_HOR_RES / 3, 80);
txtArtist = lv_label_create(lv_scr_act(), NULL); lv_obj_align(btnPlayPause, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_label_set_long_mode(txtArtist, LV_LABEL_LONG_SROLL); txtPlayPause = lv_label_create(btnPlayPause, nullptr);
lv_obj_align(txtArtist, NULL, LV_ALIGN_IN_LEFT_MID, 0,-20); lv_label_set_text(txtPlayPause, ">");
lv_label_set_text(txtArtist, "Artist Name");
lv_label_set_align(txtArtist, LV_LABEL_ALIGN_CENTER); txtTrackDuration = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_width(txtArtist, LV_HOR_RES); lv_label_set_long_mode(txtTrackDuration, LV_LABEL_LONG_SROLL);
lv_obj_align(txtTrackDuration, nullptr, LV_ALIGN_IN_TOP_LEFT, 12, 20);
txtTrack = lv_label_create(lv_scr_act(), NULL); lv_label_set_text(txtTrackDuration, "--:--/--:--");
lv_label_set_long_mode(txtTrack, LV_LABEL_LONG_DOT); lv_label_set_align(txtTrackDuration, LV_ALIGN_IN_LEFT_MID);
lv_obj_align(txtTrack, NULL, LV_ALIGN_IN_LEFT_MID, 0,20); lv_obj_set_width(txtTrackDuration, LV_HOR_RES);
lv_label_set_text(txtTrack, "This is a very long track name");
lv_label_set_align(txtTrack, LV_LABEL_ALIGN_CENTER); constexpr uint8_t FONT_HEIGHT = 12;
lv_obj_set_width(txtTrack, LV_HOR_RES); constexpr uint8_t LINE_PAD = 15;
constexpr int8_t MIDDLE_OFFSET = -25;
musicService.event(Controllers::MusicService::EVENT_MUSIC_OPEN); 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() { Music::~Music() {
@ -79,47 +138,155 @@ bool Music::OnButtonPushed() {
} }
bool Music::Refresh() { bool Music::Refresh() {
if (artist != musicService.getArtist()) {
if (m_artist != musicService.artist()) { artist = musicService.getArtist();
m_artist = musicService.artist(); currentLength = 0;
lv_label_set_text(txtArtist, m_artist.data()); 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();
} }
if (m_track != musicService.track()) { } else {
m_track = musicService.track(); lv_label_set_text(txtPlayPause, ">");
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; return running;
} }
void Music::OnObjectEvent(lv_obj_t* obj, lv_event_t event) void Music::UpdateLength() {
{ if (totalLength > (99 * 60 * 60)) {
if (event == LV_EVENT_CLICKED) { lv_label_set_text(txtTrackDuration, "Inf/Inf");
if (obj == btnVolDown) { } else if (totalLength > (99 * 60)) {
musicService.event(Controllers::MusicService::EVENT_MUSIC_VOLDOWN); char timer[12];
} else if (obj == btnVolUp) { sprintf(timer, "%02d:%02d/%02d:%02d",
musicService.event(Controllers::MusicService::EVENT_MUSIC_VOLUP); (currentLength / (60 * 60)) % 100,
} else if (obj == btnPrev) { ((currentLength % (60 * 60)) / 60) % 100,
musicService.event(Controllers::MusicService::EVENT_MUSIC_PREV); (totalLength / (60 * 60)) % 100,
} else if (obj == btnPlayPause) { ((totalLength % (60 * 60)) / 60) % 100
if (m_status == Pinetime::Controllers::MusicService::STATUS_MUSIC_PLAYING) { );
musicService.event(Controllers::MusicService::EVENT_MUSIC_PAUSE); lv_label_set_text(txtTrackDuration, timer);
} else { } else {
musicService.event(Controllers::MusicService::EVENT_MUSIC_PLAY); char timer[12];
} sprintf(timer, "%02d:%02d/%02d:%02d",
} else if (obj == btnNext) { (currentLength / 60) % 100,
musicService.event(Controllers::MusicService::EVENT_MUSIC_NEXT); (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);
}
}
}
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;
}
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;
}
case TouchEvents::SwipeLeft: {
musicService.event(Controllers::MusicService::EVENT_MUSIC_NEXT);
return true;
}
case TouchEvents::SwipeRight: {
musicService.event(Controllers::MusicService::EVENT_MUSIC_PREV);
return true;
}
default: {
return true;
}
}
}

View File

@ -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 #pragma once
#include <cstdint> #include <cstdint>
@ -13,37 +30,66 @@
#include <libs/lvgl/src/lv_core/lv_style.h> #include <libs/lvgl/src/lv_core/lv_style.h>
#include <libs/lvgl/src/lv_core/lv_obj.h> #include <libs/lvgl/src/lv_core/lv_obj.h>
#include "../../Version.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 Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class Music : public Screen {
class Music : public Screen{ public:
public: Music(DisplayApp *app, Pinetime::Controllers::MusicService &music);
Music(DisplayApp* app, Pinetime::Controllers::MusicService &music);
~Music() override; ~Music() override;
bool Refresh() override; bool Refresh() override;
bool OnButtonPushed() override;
bool OnButtonPushed() override;
void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
void OnObjectEvent(lv_obj_t *obj, lv_event_t event);
private:
lv_obj_t * btnPrev; private:
lv_obj_t * btnPlayPause; bool OnTouchEvent(TouchEvents event);
lv_obj_t * btnNext;
lv_obj_t * btnVolDown; void UpdateLength();
lv_obj_t * btnVolUp;
lv_obj_t * txtArtist; lv_obj_t *btnPrev;
lv_obj_t * txtTrack; lv_obj_t *btnPlayPause;
lv_obj_t * txtPlayPause; lv_obj_t *btnNext;
lv_obj_t *btnVolDown;
bool running = true; lv_obj_t *btnVolUp;
Pinetime::Controllers::MusicService &musicService; lv_obj_t *txtArtist;
std::string m_artist; lv_obj_t *txtTrack;
std::string m_album; lv_obj_t *txtPlayPause;
std::string m_track;
unsigned char m_status; 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;
}; };
} }
} }

View File

@ -9,16 +9,28 @@ namespace Pinetime {
namespace Screens { namespace Screens {
class Screen { class Screen {
public: public:
Screen(DisplayApp* app) : app{app} {} explicit Screen(DisplayApp* app) : app{app} {}
virtual ~Screen() = default; 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; 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; } 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(TouchEvents event) { return false; }
virtual bool OnTouchEvent(uint16_t x, uint16_t y) { return false; } virtual bool OnTouchEvent(uint16_t x, uint16_t y) { return false; }

View File

@ -105,12 +105,12 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
std::unique_ptr<Screen> SystemInfo::CreateScreen2() { std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
auto& bleAddr = bleController.Address(); 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]); bleAddr[5], bleAddr[4], bleAddr[3], bleAddr[2], bleAddr[1], bleAddr[0]);
return std::unique_ptr<Screen>(new Screens::Label(app, t2)); return std::unique_ptr<Screen>(new Screens::Label(app, t2));
} }
std::unique_ptr<Screen> SystemInfo::CreateScreen3() { 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)); return std::unique_ptr<Screen>(new Screens::Label(app, t3));
} }

View File

@ -1,13 +1,13 @@
#include <cstdio> #include <cstdio>
#include <libs/date/includes/date/date.h> #include <libs/date/includes/date/date.h>
#include <Components/DateTime/DateTimeController.h> #include "components/datetime/DateTimeController.h"
#include <Version.h> #include <Version.h>
#include <libs/lvgl/src/lv_core/lv_obj.h> #include <libs/lvgl/src/lv_core/lv_obj.h>
#include <libs/lvgl/src/lv_font/lv_font.h> #include <libs/lvgl/src/lv_font/lv_font.h>
#include <libs/lvgl/lvgl.h> #include <libs/lvgl/lvgl.h>
#include <libraries/log/nrf_log.h> #include <libraries/log/nrf_log.h>
#include "Tab.h" #include "Tab.h"
#include <DisplayApp/DisplayApp.h> #include "displayapp/DisplayApp.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;

View File

@ -30,7 +30,7 @@ Tile::Tile(DisplayApp* app, std::array<Applications, 6>& applications) : Screen(
} }
modal.reset(new Modal(app)); 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_btnm_set_map(btnm1, btnm_map1);
lv_obj_set_size(btnm1, LV_HOR_RES, LV_VER_RES); lv_obj_set_size(btnm1, LV_HOR_RES, LV_VER_RES);

View File

@ -37,7 +37,9 @@ void Cst816S::Init() {
Cst816S::TouchInfos Cst816S::GetTouchInfo() { Cst816S::TouchInfos Cst816S::GetTouchInfo() {
Cst816S::TouchInfos info; 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; auto nbTouchPoints = touchData[2] & 0x0f;
// uint8_t i = 0; // uint8_t i = 0;

View File

@ -18,13 +18,13 @@ namespace Pinetime {
LongPress = 0x0C LongPress = 0x0C
}; };
struct TouchInfos { struct TouchInfos {
uint16_t x; uint16_t x = 0;
uint16_t y; uint16_t y = 0;
uint8_t action; uint8_t action = 0;
uint8_t finger; uint8_t finger = 0;
uint8_t pressure; uint8_t pressure = 0;
uint8_t area; uint8_t area = 0;
Gestures gesture; Gestures gesture = Gestures::None;
bool isTouch = false; bool isTouch = false;
}; };

View File

@ -12,7 +12,7 @@ SpiNorFlash::SpiNorFlash(Spi& spi) : spi{spi} {
void SpiNorFlash::Init() { void SpiNorFlash::Init() {
device_id = ReadIdentificaion(); 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() { void SpiNorFlash::Uninit() {
@ -22,7 +22,7 @@ void SpiNorFlash::Uninit() {
void SpiNorFlash::Sleep() { void SpiNorFlash::Sleep() {
auto cmd = static_cast<uint8_t>(Commands::DeepPowerDown); auto cmd = static_cast<uint8_t>(Commands::DeepPowerDown);
spi.Write(&cmd, sizeof(uint8_t)); spi.Write(&cmd, sizeof(uint8_t));
NRF_LOG_INFO("[FLASH] Sleep") NRF_LOG_INFO("[SpiNorFlash] Sleep")
} }
void SpiNorFlash::Wakeup() { void SpiNorFlash::Wakeup() {
@ -38,7 +38,7 @@ void SpiNorFlash::Wakeup() {
else { else {
NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: %d", id); NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: %d", id);
} }
NRF_LOG_INFO("[FLASH] Wakeup") NRF_LOG_INFO("[SpiNorFlash] Wakeup")
} }
SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() { SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() {

View File

@ -60,24 +60,53 @@ void TwiMaster::Init() {
} }
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); xSemaphoreTake(mutex, portMAX_DELAY);
Write(deviceAddress, &registerAddress, 1, false); auto ret = ReadWithRetry(deviceAddress, registerAddress, data, size);
Read(deviceAddress, data, size, true);
xSemaphoreGive(mutex); 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); ASSERT(size <= maxDataSize);
xSemaphoreTake(mutex, portMAX_DELAY); 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, &registerAddress, 1, false);
if(ret != ErrorCodes::NoError)
ret = Write(deviceAddress, &registerAddress, 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; internalBuffer[0] = registerAddress;
std::memcpy(internalBuffer+1, data, size); std::memcpy(internalBuffer+1, data, size);
Write(deviceAddress, internalBuffer, size+1, true); auto ret = Write(deviceAddress, internalBuffer, size+1, true);
xSemaphoreGive(mutex); 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->ADDRESS = deviceAddress;
twiBaseAddress->TASKS_RESUME = 0x1UL; twiBaseAddress->TASKS_RESUME = 0x1UL;
twiBaseAddress->RXD.PTR = (uint32_t)buffer; twiBaseAddress->RXD.PTR = (uint32_t)buffer;
@ -88,7 +117,15 @@ void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool s
while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR); while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR);
twiBaseAddress->EVENTS_RXSTARTED = 0x0UL; 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; twiBaseAddress->EVENTS_LASTRX = 0x0UL;
if (stop || twiBaseAddress->EVENTS_ERROR) { if (stop || twiBaseAddress->EVENTS_ERROR) {
@ -105,9 +142,10 @@ void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool s
if (twiBaseAddress->EVENTS_ERROR) { if (twiBaseAddress->EVENTS_ERROR) {
twiBaseAddress->EVENTS_ERROR = 0x0UL; 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->ADDRESS = deviceAddress;
twiBaseAddress->TASKS_RESUME = 0x1UL; twiBaseAddress->TASKS_RESUME = 0x1UL;
twiBaseAddress->TXD.PTR = (uint32_t)data; twiBaseAddress->TXD.PTR = (uint32_t)data;
@ -118,7 +156,15 @@ void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, b
while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR); while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR);
twiBaseAddress->EVENTS_TXSTARTED = 0x0UL; 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; twiBaseAddress->EVENTS_LASTTX = 0x0UL;
if (stop || twiBaseAddress->EVENTS_ERROR) { if (stop || twiBaseAddress->EVENTS_ERROR) {
@ -137,6 +183,8 @@ void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, b
uint32_t error = twiBaseAddress->ERRORSRC; uint32_t error = twiBaseAddress->ERRORSRC;
twiBaseAddress->ERRORSRC = error; twiBaseAddress->ERRORSRC = error;
} }
return ErrorCodes::NoError;
} }
void TwiMaster::Sleep() { void TwiMaster::Sleep() {
@ -152,3 +200,30 @@ void TwiMaster::Wakeup() {
Init(); Init();
NRF_LOG_INFO("[TWIMASTER] Wakeup"); 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;
}

View File

@ -10,6 +10,7 @@ namespace Pinetime {
public: public:
enum class Modules { TWIM1 }; enum class Modules { TWIM1 };
enum class Frequencies {Khz100, Khz250, Khz400}; enum class Frequencies {Khz100, Khz250, Khz400};
enum class ErrorCodes {NoError, TransactionFailed};
struct Parameters { struct Parameters {
uint32_t frequency; uint32_t frequency;
uint8_t pinSda; uint8_t pinSda;
@ -19,15 +20,19 @@ namespace Pinetime {
TwiMaster(const Modules module, const Parameters& params); TwiMaster(const Modules module, const Parameters& params);
void Init(); void Init();
void Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size); ErrorCodes 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 Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
void Sleep(); void Sleep();
void Wakeup(); void Wakeup();
private: private:
void Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop); ErrorCodes ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
void Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop); 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; NRF_TWIM_Type* twiBaseAddress;
SemaphoreHandle_t mutex; SemaphoreHandle_t mutex;
const Modules module; const Modules module;
@ -35,7 +40,8 @@ namespace Pinetime {
static constexpr uint8_t maxDataSize{8}; static constexpr uint8_t maxDataSize{8};
static constexpr uint8_t registerSize{1}; static constexpr uint8_t registerSize{1};
uint8_t internalBuffer[maxDataSize + registerSize]; uint8_t internalBuffer[maxDataSize + registerSize];
uint32_t txStartedCycleCount = 0;
static constexpr uint32_t HwFreezedDelay{161000};
}; };
} }
} }

View File

@ -11,15 +11,15 @@
#include <libraries/gpiote/app_gpiote.h> #include <libraries/gpiote/app_gpiote.h>
#include <hal/nrf_wdt.h> #include <hal/nrf_wdt.h>
#include <cstring> #include <cstring>
#include <Components/Gfx/Gfx.h> #include <components/gfx/Gfx.h>
#include <drivers/St7789.h> #include <drivers/St7789.h>
#include <Components/Brightness/BrightnessController.h> #include <components/brightness/BrightnessController.h>
#if NRF_LOG_ENABLED #if NRF_LOG_ENABLED
#include "Logging/NrfLogger.h" #include "logging/NrfLogger.h"
Pinetime::Logging::NrfLogger logger; Pinetime::Logging::NrfLogger logger;
#else #else
#include "Logging/DummyLogger.h" #include "logging/DummyLogger.h"
Pinetime::Logging::DummyLogger logger; Pinetime::Logging::DummyLogger logger;
#endif #endif

View File

@ -19,10 +19,15 @@ void NrfLogger::Init() {
void NrfLogger::Process(void*) { void NrfLogger::Process(void*) {
NRF_LOG_INFO("Logger task started!"); NRF_LOG_INFO("Logger task started!");
// Suppress endless loop diagnostic
#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
while (1) { while (1) {
NRF_LOG_FLUSH(); NRF_LOG_FLUSH();
vTaskDelay(100); // Not good for power consumption, it will wake up every 100ms... vTaskDelay(100); // Not good for power consumption, it will wake up every 100ms...
} }
// Clear diagnostic suppression
#pragma clang diagnostic pop
} }
void NrfLogger::Resume() { void NrfLogger::Resume() {

View File

@ -27,7 +27,7 @@ namespace Pinetime {
void Process() const { void Process() const {
if(xTaskGetTickCount() - lastTick > 10000) { if(xTaskGetTickCount() - lastTick > 10000) {
NRF_LOG_INFO("---------------------------------------\nFree heap : %d", xPortGetFreeHeapSize()); 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++) { for (uint32_t i = 0; i < nb; i++) {
NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark); NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark);
if (tasksStatus[i].usStackHighWaterMark < 20) if (tasksStatus[i].usStackHighWaterMark < 20)

View File

@ -11,8 +11,9 @@
#include <host/ble_gap.h> #include <host/ble_gap.h>
#include <host/util/util.h> #include <host/util/util.h>
#include <drivers/InternalFlash.h> #include <drivers/InternalFlash.h>
#include "../main.h" #include "main.h"
#include "components/ble/NimbleController.h" #include "components/ble/NimbleController.h"
#include "../BootloaderVersion.h"
using namespace Pinetime::System; using namespace Pinetime::System;
@ -36,7 +37,7 @@ SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
bleController{bleController}, dateTimeController{dateTimeController}, bleController{bleController}, dateTimeController{dateTimeController},
watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager}, watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager},
nimbleController(*this, bleController,dateTimeController, notificationManager, batteryController, spiNorFlash) { nimbleController(*this, bleController,dateTimeController, notificationManager, batteryController, spiNorFlash) {
systemTaksMsgQueue = xQueueCreate(10, 1); systemTasksMsgQueue = xQueueCreate(10, 1);
} }
void SystemTask::Start() { void SystemTask::Start() {
@ -100,9 +101,12 @@ void SystemTask::Work() {
idleTimer = xTimerCreate ("idleTimer", idleTime, pdFALSE, this, IdleTimerCallback); idleTimer = xTimerCreate ("idleTimer", idleTime, pdFALSE, this, IdleTimerCallback);
xTimerStart(idleTimer, 0); xTimerStart(idleTimer, 0);
// Suppress endless loop diagnostic
#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
while(true) { while(true) {
uint8_t msg; uint8_t msg;
if (xQueueReceive(systemTaksMsgQueue, &msg, isSleeping?2500 : 1000)) { if (xQueueReceive(systemTasksMsgQueue, &msg, isSleeping ? 2500 : 1000)) {
Messages message = static_cast<Messages >(msg); Messages message = static_cast<Messages >(msg);
switch(message) { switch(message) {
case Messages::GoToRunning: case Messages::GoToRunning:
@ -158,7 +162,11 @@ void SystemTask::Work() {
ReloadIdleTimer(); ReloadIdleTimer();
break; break;
case Messages::OnDisplayTaskSleeping: 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(); lcd.Sleep();
touchPanel.Sleep(); touchPanel.Sleep();
@ -191,6 +199,8 @@ void SystemTask::Work() {
if(!nrf_gpio_pin_read(pinButton)) if(!nrf_gpio_pin_read(pinButton))
watchdog.Kick(); watchdog.Kick();
} }
// Clear diagnostic suppression
#pragma clang diagnostic pop
} }
void SystemTask::OnButtonPushed() { void SystemTask::OnButtonPushed() {
@ -228,10 +238,10 @@ void SystemTask::PushMessage(SystemTask::Messages msg) {
} }
BaseType_t xHigherPriorityTaskWoken; BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE; xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(systemTaksMsgQueue, &msg, &xHigherPriorityTaskWoken); xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) { if (xHigherPriorityTaskWoken) {
/* Actual macro used here is port specific. */ /* Actual macro used here is port specific. */
// TODO : should I do something here? // TODO: should I do something here?
} }
} }

View File

@ -54,7 +54,7 @@ namespace Pinetime {
std::unique_ptr<Pinetime::Applications::DisplayApp> displayApp; std::unique_ptr<Pinetime::Applications::DisplayApp> displayApp;
Pinetime::Controllers::Ble& bleController; Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime& dateTimeController; Pinetime::Controllers::DateTime& dateTimeController;
QueueHandle_t systemTaksMsgQueue; QueueHandle_t systemTasksMsgQueue;
std::atomic<bool> isSleeping{false}; std::atomic<bool> isSleeping{false};
std::atomic<bool> isGoingToSleep{false}; std::atomic<bool> isGoingToSleep{false};
std::atomic<bool> isWakingUp{false}; std::atomic<bool> isWakingUp{false};