Merge remote-tracking branch 'upstream/develop' into pinetimestyle-colorpicker

This commit is contained in:
Kieran Cawthray
2021-07-12 13:01:11 +02:00
39 changed files with 754 additions and 610 deletions

View File

@@ -14,7 +14,7 @@ Battery::Battery() {
}
void Battery::Init() {
nrf_gpio_cfg_input(chargingPin, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pullup);
nrf_gpio_cfg_input(chargingPin, static_cast<nrf_gpio_pin_pull_t> GPIO_PIN_CNF_PULL_Pullup);
}
void Battery::Update() {
@@ -22,8 +22,9 @@ void Battery::Update() {
isCharging = !nrf_gpio_pin_read(chargingPin);
isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
if (isReading)
if (isReading) {
return;
}
// Non blocking read
samples = 0;
isReading = true;
@@ -55,17 +56,21 @@ void Battery::SaadcInit() {
void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
const float battery_max = 4.18; // maximum voltage of battery ( max charging voltage is 4.21 )
const float battery_min = 3.20; // minimum voltage of battery before shutdown ( depends on the battery )
const uint16_t battery_max = 4180; // maximum voltage of battery ( max charging voltage is 4.21 )
const uint16_t battery_min = 3200; // minimum voltage of battery before shutdown ( depends on the battery )
if (p_event->type == NRFX_SAADC_EVT_DONE) {
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
voltage = (static_cast<float>(p_event->data.done.p_buffer[0]) * 2.04f) / (1024 / 3.0f);
voltage = roundf(voltage * 100) / 100;
// A hardware voltage divider divides the battery voltage by 2
// ADC gain is 1/5
// thus adc_voltage = battery_voltage / 2 * gain = battery_voltage / 10
// reference_voltage is 0.6V
// p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024
voltage = p_event->data.done.p_buffer[0] * 6000 / 1024;
percentRemaining = static_cast<int>(((voltage - battery_min) / (battery_max - battery_min)) * 100);
percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min);
percentRemaining = std::max(percentRemaining, 0);
percentRemaining = std::min(percentRemaining, 100);

View File

@@ -50,7 +50,7 @@ namespace Pinetime {
return percentRemainingBuffer.GetAverage();
}
float Voltage() const {
uint16_t Voltage() const {
return voltage;
}
@@ -71,7 +71,7 @@ namespace Pinetime {
static constexpr uint32_t chargingPin = 12;
static constexpr uint32_t powerPresentPin = 19;
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
float voltage = 0.0f;
uint16_t voltage = 0;
int percentRemaining = -1;
bool isCharging = false;
@@ -86,4 +86,4 @@ namespace Pinetime {
uint8_t samples = 0;
};
}
}
}

View File

@@ -18,79 +18,99 @@
#include "MusicService.h"
#include "systemtask/SystemTask.h"
int MusicCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
return static_cast<Pinetime::Controllers::MusicService*>(arg)->OnCommand(conn_handle, attr_handle, ctxt);
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
return ble_uuid128_t{
.u = {.type = BLE_UUID_TYPE_128},
.value = { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
};
}
// 00000000-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t BaseUuid() {
return CharUuid(0x00, 0x00);
}
constexpr ble_uuid128_t msUuid {BaseUuid()};
constexpr ble_uuid128_t msEventCharUuid {CharUuid(0x01, 0x00)};
constexpr ble_uuid128_t msStatusCharUuid {CharUuid(0x02, 0x00)};
constexpr ble_uuid128_t msArtistCharUuid {CharUuid(0x03, 0x00)};
constexpr ble_uuid128_t msTrackCharUuid {CharUuid(0x04, 0x00)};
constexpr ble_uuid128_t msAlbumCharUuid {CharUuid(0x05, 0x00)};
constexpr ble_uuid128_t msPositionCharUuid {CharUuid(0x06, 0x00)};
constexpr ble_uuid128_t msTotalLengthCharUuid {CharUuid(0x07, 0x00)};
constexpr ble_uuid128_t msTrackNumberCharUuid {CharUuid(0x08, 0x00)};
constexpr ble_uuid128_t msTrackTotalCharUuid {CharUuid(0x09, 0x00)};
constexpr ble_uuid128_t msPlaybackSpeedCharUuid {CharUuid(0x0a, 0x00)};
constexpr ble_uuid128_t msRepeatCharUuid {CharUuid(0x0b, 0x00)};
constexpr ble_uuid128_t msShuffleCharUuid {CharUuid(0x0c, 0x00)};
int MusicCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
return static_cast<Pinetime::Controllers::MusicService*>(arg)->OnCommand(conn_handle, attr_handle, ctxt);
}
}
Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask& system) : m_system(system) {
characteristicDefinition[0] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msEventCharUuid),
characteristicDefinition[0] = {.uuid = &msEventCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY,
.val_handle = &eventHandle};
characteristicDefinition[1] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msStatusCharUuid),
characteristicDefinition[1] = {.uuid = &msStatusCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[2] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackCharUuid),
characteristicDefinition[2] = {.uuid = &msTrackCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[3] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msArtistCharUuid),
characteristicDefinition[3] = {.uuid = &msArtistCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[4] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msAlbumCharUuid),
characteristicDefinition[4] = {.uuid = &msAlbumCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[5] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msPositionCharUuid),
characteristicDefinition[5] = {.uuid = &msPositionCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[6] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid),
characteristicDefinition[6] = {.uuid = &msTotalLengthCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[7] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid),
characteristicDefinition[7] = {.uuid = &msTotalLengthCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[8] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackNumberCharUuid),
characteristicDefinition[8] = {.uuid = &msTrackNumberCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[9] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackTotalCharUuid),
characteristicDefinition[9] = {.uuid = &msTrackTotalCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[10] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msPlaybackSpeedCharUuid),
characteristicDefinition[10] = {.uuid = &msPlaybackSpeedCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[11] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msRepeatCharUuid),
characteristicDefinition[11] = {.uuid = &msRepeatCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[12] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msShuffleCharUuid),
characteristicDefinition[12] = {.uuid = &msShuffleCharUuid.u,
.access_cb = MusicCallback,
.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 = reinterpret_cast<ble_uuid_t*>(&msUuid), .characteristics = characteristicDefinition};
.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &msUuid.u, .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() {
@@ -109,27 +129,27 @@ int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_
data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data);
char* s = &data[0];
if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msArtistCharUuid)) == 0) {
if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
artistName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackCharUuid.u) == 0) {
trackName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msAlbumCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msAlbumCharUuid.u) == 0) {
albumName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msStatusCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msStatusCharUuid.u) == 0) {
playing = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msRepeatCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msRepeatCharUuid.u) == 0) {
repeat = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msShuffleCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msShuffleCharUuid.u) == 0) {
shuffle = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msPositionCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msPositionCharUuid.u) == 0) {
trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msTotalLengthCharUuid.u) == 0) {
trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackNumberCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackNumberCharUuid.u) == 0) {
trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackTotalCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackTotalCharUuid.u) == 0) {
tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msPlaybackSpeedCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msPlaybackSpeedCharUuid.u) == 0) {
playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f;
}
}
@@ -174,4 +194,4 @@ void Pinetime::Controllers::MusicService::event(char event) {
}
ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
}

View File

@@ -26,18 +26,11 @@
#undef max
#undef min
// 00000000-78fc-48fe-8e23-433b3a1942d0
#define MUSIC_SERVICE_UUID_BASE \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 }
#define MUSIC_SERVICE_CHAR_UUID(x, y) \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class MusicService {
public:
explicit MusicService(Pinetime::System::SystemTask& system);
@@ -73,41 +66,26 @@ namespace Pinetime {
enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
private:
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_CHAR_UUID(0x01, 0x00)};
ble_uuid128_t msStatusCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x02, 0x00)};
ble_uuid128_t msArtistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x03, 0x00)};
ble_uuid128_t msTrackCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x04, 0x00)};
ble_uuid128_t msAlbumCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x05, 0x00)};
ble_uuid128_t msPositionCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x06, 0x00)};
ble_uuid128_t msTotalLengthCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x07, 0x00)};
ble_uuid128_t msTrackNumberCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x08, 0x00)};
ble_uuid128_t msTrackTotalCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x09, 0x00)};
ble_uuid128_t msPlaybackSpeedCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0a, 0x00)};
ble_uuid128_t msRepeatCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0b, 0x00)};
ble_uuid128_t msShuffleCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0c, 0x00)};
struct ble_gatt_chr_def characteristicDefinition[14];
struct ble_gatt_svc_def serviceDefinition[2];
uint16_t eventHandle;
uint16_t eventHandle {};
std::string artistName;
std::string albumName;
std::string trackName;
std::string artistName {"Waiting for"};
std::string albumName {};
std::string trackName {"track information.."};
bool playing;
bool playing {false};
int trackProgress;
int trackLength;
int trackNumber;
int tracksTotal;
int trackProgress {0};
int trackLength {0};
int trackNumber {};
int tracksTotal {};
float playbackSpeed;
float playbackSpeed {1.0f};
bool repeat;
bool shuffle;
bool repeat {false};
bool shuffle {false};
Pinetime::System::SystemTask& m_system;
};

197
src/components/fs/FS.cpp Normal file
View File

@@ -0,0 +1,197 @@
#include "FS.h"
#include <cstring>
#include <littlefs/lfs.h>
#include <lvgl/lvgl.h>
using namespace Pinetime::Controllers;
FS::FS(Pinetime::Drivers::SpiNorFlash& driver) :
flashDriver{ driver },
lfsConfig{
.context = this,
.read = SectorRead,
.prog = SectorProg,
.erase = SectorErase,
.sync = SectorSync,
.read_size = 16,
.prog_size = 8,
.block_size = blockSize,
.block_count = size / blockSize,
.block_cycles = 1000u,
.cache_size = 16,
.lookahead_size = 16,
.name_max = 50,
.attr_max = 50,
}
{ }
void FS::Init() {
// try mount
int err = lfs_mount(&lfs, &lfsConfig);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if (err != LFS_ERR_OK) {
lfs_format(&lfs, &lfsConfig);
err = lfs_mount(&lfs, &lfsConfig);
if (err != LFS_ERR_OK) {
return;
}
}
#ifndef PINETIME_IS_RECOVERY
VerifyResource();
LVGLFileSystemInit();
#endif
}
void FS::VerifyResource() {
// validate the resource metadata
resourcesValid = true;
}
int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) {
return lfs_file_open(&lfs, file_p, fileName, flags);
}
int FS::FileClose(lfs_file_t* file_p) {
return lfs_file_close(&lfs, file_p);
}
int FS::FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size) {
return lfs_file_read(&lfs, file_p, buff, size);
}
int FS::FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size) {
return lfs_file_write(&lfs, file_p, buff, size);
}
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos) {
return lfs_file_seek(&lfs, file_p, pos, LFS_SEEK_SET);
}
int FS::FileDelete(const char* fileName) {
return lfs_remove(&lfs, fileName);
}
int FS::DirCreate(const char* path) {
return lfs_mkdir(&lfs, path);
}
// Delete directory and all files inside
int FS::DirDelete(const char* path) {
lfs_dir_t lfs_dir;
lfs_info entryInfo;
int err;
err = lfs_dir_open(&lfs, &lfs_dir, path);
if (err) {
return err;
}
while (lfs_dir_read(&lfs, &lfs_dir, &entryInfo)) {
lfs_remove(&lfs, entryInfo.name);
}
lfs_dir_close(&lfs, &lfs_dir);
return LFS_ERR_OK;
}
/*
----------- Interface between littlefs and SpiNorFlash -----------
*/
int FS::SectorSync(const struct lfs_config* c) {
return 0;
}
int FS::SectorErase(const struct lfs_config* c, lfs_block_t block) {
Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
const size_t address = startAddress + (block * blockSize);
lfs.flashDriver.SectorErase(address);
return lfs.flashDriver.EraseFailed() ? -1 : 0;
}
int FS::SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size) {
Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
const size_t address = startAddress + (block * blockSize) + off;
lfs.flashDriver.Write(address, (uint8_t*) buffer, size);
return lfs.flashDriver.ProgramFailed() ? -1 : 0;
}
int FS::SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size) {
Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
const size_t address = startAddress + (block * blockSize) + off;
lfs.flashDriver.Read(address, static_cast<uint8_t*>(buffer), size);
return 0;
}
/*
----------- LVGL filesystem integration -----------
*/
namespace {
lv_fs_res_t lvglOpen(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode) {
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
FS* filesys = static_cast<FS*>(drv->user_data);
filesys->FileOpen(file, path, LFS_O_RDONLY);
if (file->type == 0) {
return LV_FS_RES_FS_ERR;
}
else {
return LV_FS_RES_OK;
}
}
lv_fs_res_t lvglClose(lv_fs_drv_t* drv, void* file_p) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileClose(file);
return LV_FS_RES_OK;
}
lv_fs_res_t lvglRead(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t btr, uint32_t* br) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileRead(file, static_cast<uint8_t*>(buf), btr);
*br = btr;
return LV_FS_RES_OK;
}
lv_fs_res_t lvglSeek(lv_fs_drv_t* drv, void* file_p, uint32_t pos) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileSeek(file, pos);
return LV_FS_RES_OK;
}
}
void FS::LVGLFileSystemInit() {
lv_fs_drv_t fs_drv;
lv_fs_drv_init(&fs_drv);
fs_drv.file_size = sizeof(lfs_file_t);
fs_drv.letter = 'F';
fs_drv.open_cb = lvglOpen;
fs_drv.close_cb = lvglClose;
fs_drv.read_cb = lvglRead;
fs_drv.seek_cb = lvglSeek;
fs_drv.user_data = this;
lv_fs_drv_register(&fs_drv);
}

71
src/components/fs/FS.h Normal file
View File

@@ -0,0 +1,71 @@
#pragma once
#include <cstdint>
#include "drivers/SpiNorFlash.h"
#include <littlefs/lfs.h>
namespace Pinetime {
namespace Controllers {
class FS {
public:
FS(Pinetime::Drivers::SpiNorFlash&);
void Init();
void LVGLFileSystemInit();
int FileOpen(lfs_file_t* file_p, const char* fileName, const int flags);
int FileClose(lfs_file_t* file_p);
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
int FileSeek(lfs_file_t* file_p, uint32_t pos);
int FileDelete(const char* fileName);
int DirCreate(const char* path);
int DirDelete(const char* path);
void VerifyResource();
private:
Pinetime::Drivers::SpiNorFlash& flashDriver;
/*
* External Flash MAP (4 MBytes)
*
* 0x000000 +---------------------------------------+
* | Bootloader Assets |
* | 256 KBytes |
* | |
* 0x040000 +---------------------------------------+
* | OTA |
* | 464 KBytes |
* | |
* | |
* | |
* 0x0B4000 +---------------------------------------+
* | File System |
* | |
* | |
* | |
* | |
* 0x400000 +---------------------------------------+
*
*/
static constexpr size_t startAddress = 0x0B4000;
static constexpr size_t size = 0x3C0000;
static constexpr size_t blockSize = 4096;
bool resourcesValid = false;
const struct lfs_config lfsConfig;
lfs_t lfs;
static int SectorSync(const struct lfs_config* c);
static int SectorErase(const struct lfs_config* c, lfs_block_t block);
static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
};
}
}

View File

@@ -4,108 +4,44 @@
using namespace Pinetime::Controllers;
struct SettingsHeader {
uint8_t isActive; // 0xF1 = Block is active, 0xF0 = Block is inactive
uint16_t version; // Current version, to verify if the saved data is for the current Version
};
#define HEADER_SIZE sizeof(SettingsHeader)
Settings::Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash {spiNorFlash} {
Settings::Settings(Pinetime::Controllers::FS& fs) : fs {fs} {
}
void Settings::Init() {
// Load default settings from Flash
LoadSettingsFromFlash();
LoadSettingsFromFile();
}
void Settings::SaveSettings() {
// verify if is necessary to save
if (settingsChanged) {
SaveSettingsToFlash();
SaveSettingsToFile();
}
settingsChanged = false;
}
bool Settings::FindHeader() {
SettingsHeader settingsHeader;
uint8_t bufferHead[sizeof(settingsHeader)];
void Settings::LoadSettingsFromFile() {
SettingsData bufferSettings;
lfs_file_t settingsFile;
for (uint8_t block = 0; block < 10; block++) {
spiNorFlash.Read(settingsBaseAddr + (block * 0x1000), bufferHead, sizeof(settingsHeader));
std::memcpy(&settingsHeader, bufferHead, sizeof(settingsHeader));
if (settingsHeader.isActive == 0xF1 && settingsHeader.version == settingsVersion) {
settingsFlashBlock = block;
return true;
}
if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
return;
}
return false;
}
void Settings::ReadSettingsData() {
uint8_t bufferSettings[sizeof(settings)];
spiNorFlash.Read(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
std::memcpy(&settings, bufferSettings, sizeof(settings));
}
void Settings::EraseBlock() {
spiNorFlash.SectorErase(settingsBaseAddr + (settingsFlashBlock * 0x1000));
}
void Settings::SetHeader(bool state) {
SettingsHeader settingsHeader;
uint8_t bufferHead[sizeof(settingsHeader)];
settingsHeader.isActive = state ? 0xF1 : 0xF0;
settingsHeader.version = settingsVersion;
std::memcpy(bufferHead, &settingsHeader, sizeof(settingsHeader));
spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000), bufferHead, sizeof(settingsHeader));
}
void Settings::SaveSettingsData() {
uint8_t bufferSettings[sizeof(settings)];
std::memcpy(bufferSettings, &settings, sizeof(settings));
spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
}
void Settings::LoadSettingsFromFlash() {
if (settingsFlashBlock == 99) {
// Find current Block, if can't find use default settings and set block to 0 ans save !
if (FindHeader()) {
ReadSettingsData();
} else {
SaveSettingsToFlash();
}
} else {
// Read Settings from flash...
// never used :)
ReadSettingsData();
fs.FileRead(&settingsFile, reinterpret_cast<uint8_t*>(&bufferSettings), sizeof(settings));
fs.FileClose(&settingsFile);
if ( bufferSettings.version == settingsVersion ) {
settings = bufferSettings;
}
}
void Settings::SaveSettingsToFlash() {
void Settings::SaveSettingsToFile() {
lfs_file_t settingsFile;
// calculate where to save...
// mark current to inactive
// erase the new location and save
// set settingsFlashBlock
// if first time hever, only saves to block 0 and set settingsFlashBlock
if (settingsFlashBlock != 99) {
SetHeader(false);
if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
return;
}
settingsFlashBlock++;
if (settingsFlashBlock > 9)
settingsFlashBlock = 0;
EraseBlock();
SetHeader(true);
SaveSettingsData();
fs.FileWrite(&settingsFile, reinterpret_cast<uint8_t*>(&settings), sizeof(settings));
fs.FileClose(&settingsFile);
}

View File

@@ -2,25 +2,26 @@
#include <cstdint>
#include "components/datetime/DateTimeController.h"
#include "components/brightness/BrightnessController.h"
#include "drivers/SpiNorFlash.h"
#include "components/fs/FS.h"
#include "drivers/Cst816s.h"
namespace Pinetime {
namespace Controllers {
class Settings {
public:
enum class ClockType { H24, H12 };
enum class Vibration { ON, OFF };
enum class WakeUpMode { None, SingleTap, DoubleTap, RaiseWrist };
enum class ClockType : uint8_t { H24, H12 };
enum class Vibration : uint8_t { ON, OFF };
enum class WakeUpMode : uint8_t { None, SingleTap, DoubleTap, RaiseWrist };
Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash);
Settings(Pinetime::Controllers::FS& fs);
void Init();
void SaveSettings();
void SetClockFace(uint8_t face) {
if (face != settings.clockFace)
if (face != settings.clockFace) {
settingsChanged = true;
}
settings.clockFace = face;
};
uint8_t GetClockFace() const {
@@ -69,8 +70,9 @@ namespace Pinetime {
};
void SetClockType(ClockType clocktype) {
if (clocktype != settings.clockType)
if (clocktype != settings.clockType) {
settingsChanged = true;
}
settings.clockType = clocktype;
};
ClockType GetClockType() const {
@@ -78,8 +80,9 @@ namespace Pinetime {
};
void SetVibrationStatus(Vibration status) {
if (status != settings.vibrationStatus)
if (status != settings.vibrationStatus) {
settingsChanged = true;
}
settings.vibrationStatus = status;
};
Vibration GetVibrationStatus() const {
@@ -87,8 +90,9 @@ namespace Pinetime {
};
void SetScreenTimeOut(uint32_t timeout) {
if (timeout != settings.screenTimeOut)
if (timeout != settings.screenTimeOut) {
settingsChanged = true;
}
settings.screenTimeOut = timeout;
};
uint32_t GetScreenTimeOut() const {
@@ -96,8 +100,9 @@ namespace Pinetime {
};
void setWakeUpMode(WakeUpMode wakeUp) {
if (wakeUp != settings.wakeUpMode)
if (wakeUp != settings.wakeUpMode) {
settingsChanged = true;
}
settings.wakeUpMode = wakeUp;
};
WakeUpMode getWakeUpMode() const {
@@ -105,8 +110,9 @@ namespace Pinetime {
};
void SetBrightness(Controllers::BrightnessController::Levels level) {
if (level != settings.brightLevel)
if (level != settings.brightLevel) {
settingsChanged = true;
}
settings.brightLevel = level;
};
Controllers::BrightnessController::Levels GetBrightness() const {
@@ -114,17 +120,24 @@ namespace Pinetime {
};
void SetStepsGoal( uint32_t goal ) {
if ( goal != settings.stepsGoal )
if ( goal != settings.stepsGoal ) {
settingsChanged = true;
}
settings.stepsGoal = goal;
};
uint32_t GetStepsGoal() const { return settings.stepsGoal; };
private:
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
Pinetime::Controllers::FS& fs;
static constexpr uint32_t settingsVersion = 0x0001;
struct SettingsData {
uint32_t version = settingsVersion;
uint32_t stepsGoal = 10000;
uint32_t screenTimeOut = 15000;
ClockType clockType = ClockType::H24;
Vibration vibrationStatus = Vibration::ON;
@@ -134,9 +147,6 @@ namespace Pinetime {
uint8_t PTSColorBar = 11;
uint8_t PTSColorBG = 3;
uint32_t stepsGoal = 10000;
uint32_t screenTimeOut = 15000;
WakeUpMode wakeUpMode = WakeUpMode::None;
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
@@ -148,20 +158,8 @@ namespace Pinetime {
uint8_t appMenu = 0;
uint8_t settingsMenu = 0;
// There are 10 blocks of reserved flash to save settings
// to minimize wear, the recording is done in a rotating way by the 10 blocks
uint8_t settingsFlashBlock = 99; // default to indicate it needs to find the active block
static constexpr uint32_t settingsBaseAddr = 0x3F6000; // Flash Settings Location
static constexpr uint16_t settingsVersion = 0x0100; // Flash Settings Version
bool FindHeader();
void ReadSettingsData();
void EraseBlock();
void SetHeader(bool state);
void SaveSettingsData();
void LoadSettingsFromFlash();
void SaveSettingsToFlash();
void LoadSettingsFromFile();
void SaveSettingsToFile();
};
}
}