Initial InfiniSim project

This commit is contained in:
Reinhold Gschweicher
2022-02-16 21:42:29 +01:00
parent cea006b049
commit f19355949b
78 changed files with 9978 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
#include "components/battery/BatteryController.h"
//#include "drivers/PinMap.h"
//#include <hal/nrf_gpio.h>
//#include <nrfx_saadc.h>
#include <algorithm>
using namespace Pinetime::Controllers;
Battery* Battery::instance = nullptr;
Battery::Battery() {
instance = this;
//nrf_gpio_cfg_input(PinMap::Charging, static_cast<nrf_gpio_pin_pull_t> GPIO_PIN_CNF_PULL_Disabled);
}
void Battery::ReadPowerState() {
if (isPowerPresent && !isCharging) {
isFull = true;
} else if (!isPowerPresent) {
isFull = false;
}
}
void Battery::MeasureVoltage() {
ReadPowerState();
if (isReading) {
return;
}
// Non blocking read
isReading = true;
//SaadcInit();
//nrfx_saadc_sample();
}
//void Battery::AdcCallbackStatic(nrfx_saadc_evt_t const* event) {
// instance->SaadcEventHandler(event);
//}
//void Battery::SaadcInit() {
// nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
// APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, AdcCallbackStatic));
//
// nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
// .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
// .gain = NRF_SAADC_GAIN1_4,
// .reference = NRF_SAADC_REFERENCE_INTERNAL,
// .acq_time = NRF_SAADC_ACQTIME_40US,
// .mode = NRF_SAADC_MODE_SINGLE_ENDED,
// .burst = NRF_SAADC_BURST_ENABLED,
// .pin_p = batteryVoltageAdcInput,
// .pin_n = NRF_SAADC_INPUT_DISABLED};
// APP_ERROR_CHECK(nrfx_saadc_channel_init(0, &adcChannelConfig));
// APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
//}
//
//void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
// 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));
//
// // A hardware voltage divider divides the battery voltage by 2
// // ADC gain is 1/4
// // thus adc_voltage = battery_voltage / 2 * gain = battery_voltage / 8
// // reference_voltage is 600mV
// // p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024
// voltage = p_event->data.done.p_buffer[0] * (8 * 600) / 1024;
//
// uint8_t newPercent;
// if (isFull) {
// newPercent = 100;
// } else if (voltage < battery_min) {
// newPercent = 0;
// } else {
// newPercent = std::min((voltage - battery_min) * 100 / (battery_max - battery_min), isCharging ? 99 : 100);
// }
//
// if ((isPowerPresent && newPercent > percentRemaining) || (!isPowerPresent && newPercent < percentRemaining) || firstMeasurement) {
// firstMeasurement = false;
// percentRemaining = newPercent;
// systemTask->PushMessage(System::Messages::BatteryPercentageUpdated);
// }
//
// nrfx_saadc_uninit();
// isReading = false;
// }
//}
void Battery::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}

View File

@@ -0,0 +1,57 @@
#pragma once
#include <cstdint>
#include "systemtask/SystemTask.h"
namespace Pinetime {
namespace Controllers {
class Battery {
public:
Battery();
void ReadPowerState();
void MeasureVoltage();
void Register(System::SystemTask* systemTask);
uint8_t PercentRemaining() const {
return percentRemaining;
}
uint16_t Voltage() const {
return voltage;
}
bool IsCharging() const {
// isCharging will go up and down when fully charged
// isFull makes sure this returns false while fully charged.
return isCharging && !isFull;
}
bool IsPowerPresent() const {
return isPowerPresent;
}
private:
static Battery* instance;
//static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
public:
uint16_t voltage = 0;
uint8_t percentRemaining = 0;
bool isFull = false;
bool isCharging = false;
bool isPowerPresent = false;
bool firstMeasurement = true;
//void SaadcInit();
//void SaadcEventHandler(nrfx_saadc_evt_t const* p_event);
//static void AdcCallbackStatic(nrfx_saadc_evt_t const* event);
bool isReading = false;
Pinetime::System::SystemTask* systemTask = nullptr;
};
}
}

View File

@@ -0,0 +1,125 @@
#include "components/ble/AlertNotificationService.h"
#include <hal/nrf_rtc.h>
#include <cstring>
#include <algorithm>
#include "components/ble/NotificationManager.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
//constexpr ble_uuid16_t AlertNotificationService::ansUuid;
//constexpr ble_uuid16_t AlertNotificationService::ansCharUuid;
//constexpr ble_uuid128_t AlertNotificationService::notificationEventUuid;
int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto anService = static_cast<AlertNotificationService*>(arg);
return anService->OnAlert(conn_handle, attr_handle, ctxt);
}
void AlertNotificationService::Init() {
// int res;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
AlertNotificationService::AlertNotificationService(System::SystemTask& systemTask, NotificationManager& notificationManager)
:
// : characteristicDefinition {{.uuid = &ansCharUuid.u, .access_cb = AlertNotificationCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE},
// {.uuid = &notificationEventUuid.u,
// .access_cb = AlertNotificationCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_NOTIFY,
// .val_handle = &eventHandle},
// {0}},
// serviceDefinition {
// {/* Device Information Service */
// .type = BLE_GATT_SVC_TYPE_PRIMARY,
// .uuid = &ansUuid.u,
// .characteristics = characteristicDefinition},
// {0},
// },
systemTask {systemTask},
notificationManager {notificationManager} {
}
int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// constexpr size_t stringTerminatorSize = 1; // end of string '\0'
// constexpr size_t headerSize = 3;
// const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
// const auto maxBufferSize {maxMessageSize + headerSize};
//
// // Ignore notifications with empty message
// const auto packetLen = OS_MBUF_PKTLEN(ctxt->om);
// if (packetLen <= headerSize) {
// return 0;
// }
//
// size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize);
// auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize));
// Categories category;
//
// NotificationManager::Notification notif;
// os_mbuf_copydata(ctxt->om, headerSize, messageSize - 1, notif.message.data());
// os_mbuf_copydata(ctxt->om, 0, 1, &category);
// notif.message[messageSize - 1] = '\0';
// notif.size = messageSize;
//
// // TODO convert all ANS categories to NotificationController categories
// switch (category) {
// case Categories::Call:
// notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall;
// break;
// default:
// notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
// break;
// }
//
// auto event = Pinetime::System::Messages::OnNewNotification;
// notificationManager.Push(std::move(notif));
// systemTask.PushMessage(event);
// }
return 0;
}
void AlertNotificationService::AcceptIncomingCall() {
// auto response = IncomingCallResponses::Answer;
// auto* om = ble_hs_mbuf_from_flat(&response, 1);
//
// uint16_t connectionHandle = systemTask.nimble().connHandle();
//
// if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
// }
//
// ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
void AlertNotificationService::RejectIncomingCall() {
// auto response = IncomingCallResponses::Reject;
// auto* om = ble_hs_mbuf_from_flat(&response, 1);
//
// uint16_t connectionHandle = systemTask.nimble().connHandle();
//
// if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
// }
//
// ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
void AlertNotificationService::MuteIncomingCall() {
// auto response = IncomingCallResponses::Mute;
// auto* om = ble_hs_mbuf_from_flat(&response, 1);
//
// uint16_t connectionHandle = systemTask.nimble().connHandle();
//
// if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
// }
//
// ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}

View File

@@ -0,0 +1,68 @@
#pragma once
#include <cstdint>
#include <array>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#undef max
//#undef min
// 00020001-78fc-48fe-8e23-433b3a1942d0
//#define NOTIFICATION_EVENT_SERVICE_UUID_BASE \
// { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x01, 0x00, 0x02, 0x00 }
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class NotificationManager;
class AlertNotificationService {
public:
AlertNotificationService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager);
void Init();
int OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
void AcceptIncomingCall();
void RejectIncomingCall();
void MuteIncomingCall();
enum class IncomingCallResponses : uint8_t { Reject = 0x00, Answer = 0x01, Mute = 0x02 };
private:
enum class Categories : uint8_t {
SimpleAlert = 0x00,
Email = 0x01,
News = 0x02,
Call = 0x03,
MissedCall = 0x04,
MmsSms = 0x05,
VoiceMail = 0x06,
Schedule = 0x07,
HighPrioritizedAlert = 0x08,
InstantMessage = 0x09,
All = 0xff
};
static constexpr uint16_t ansId {0x1811};
static constexpr uint16_t ansCharId {0x2a46};
// static constexpr ble_uuid16_t ansUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansId};
// static constexpr ble_uuid16_t ansCharUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansCharId};
// static constexpr ble_uuid128_t notificationEventUuid {.u {.type = BLE_UUID_TYPE_128}, .value = NOTIFICATION_EVENT_SERVICE_UUID_BASE};
// struct ble_gatt_chr_def characteristicDefinition[3];
// struct ble_gatt_svc_def serviceDefinition[2];
Pinetime::System::SystemTask& systemTask;
NotificationManager& notificationManager;
uint16_t eventHandle;
};
}
}

View File

@@ -0,0 +1,198 @@
/* Copyright (C) 2020-2021 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 "components/ble/MusicService.h"
#include "systemtask/SystemTask.h"
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 = &msEventCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_NOTIFY,
// .val_handle = &eventHandle};
// characteristicDefinition[1] = {.uuid = &msStatusCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[2] = {.uuid = &msTrackCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[3] = {.uuid = &msArtistCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[4] = {.uuid = &msAlbumCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[5] = {.uuid = &msPositionCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[6] = {.uuid = &msTotalLengthCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[7] = {.uuid = &msTotalLengthCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[8] = {.uuid = &msTrackNumberCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[9] = {.uuid = &msTrackTotalCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[10] = {.uuid = &msPlaybackSpeedCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[11] = {.uuid = &msRepeatCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// 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 = &msUuid.u, .characteristics = characteristicDefinition};
// serviceDefinition[1] = {0};
}
void Pinetime::Controllers::MusicService::Init() {
//uint8_t res = 0;
//res = ble_gatts_count_cfg(serviceDefinition);
//ASSERT(res == 0);
//res = ble_gatts_add_svcs(serviceDefinition);
//ASSERT(res == 0);
}
//int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
// char data[notifSize + 1];
// data[notifSize] = '\0';
// os_mbuf_copydata(ctxt->om, 0, notifSize, data);
// char* s = &data[0];
// if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
// artistName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackCharUuid.u) == 0) {
// trackName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msAlbumCharUuid.u) == 0) {
// albumName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msStatusCharUuid.u) == 0) {
// playing = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msRepeatCharUuid.u) == 0) {
// repeat = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msShuffleCharUuid.u) == 0) {
// shuffle = s[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, &msTotalLengthCharUuid.u) == 0) {
// trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } 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, &msTrackTotalCharUuid.u) == 0) {
// tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } 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;
// }
// }
// return 0;
//}
std::string Pinetime::Controllers::MusicService::getAlbum() const {
return albumName;
}
std::string Pinetime::Controllers::MusicService::getArtist() const {
return artistName;
}
std::string Pinetime::Controllers::MusicService::getTrack() const {
return trackName;
}
bool Pinetime::Controllers::MusicService::isPlaying() const {
return playing;
}
float Pinetime::Controllers::MusicService::getPlaybackSpeed() const {
return playbackSpeed;
}
int Pinetime::Controllers::MusicService::getProgress() const {
return trackProgress;
}
int Pinetime::Controllers::MusicService::getTrackLength() const {
return trackLength;
}
void Pinetime::Controllers::MusicService::event(char event) {
std::ignore = event;
//auto* om = ble_hs_mbuf_from_flat(&event, 1);
//uint16_t connectionHandle = m_system.nimble().connHandle();
//if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
//}
//ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}

View File

@@ -0,0 +1,87 @@
/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <string>
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class MusicService {
public:
explicit MusicService(Pinetime::System::SystemTask& system);
void Init();
//int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
void event(char event);
std::string getArtist() const;
std::string getTrack() const;
std::string getAlbum() const;
int getProgress() const;
int getTrackLength() const;
float getPlaybackSpeed() const;
bool isPlaying() const;
static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00;
static const char EVENT_MUSIC_PAUSE = 0x01;
static const char EVENT_MUSIC_NEXT = 0x03;
static const char EVENT_MUSIC_PREV = 0x04;
static const char EVENT_MUSIC_VOLUP = 0x05;
static const char EVENT_MUSIC_VOLDOWN = 0x06;
enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
private:
//struct ble_gatt_chr_def characteristicDefinition[14];
//struct ble_gatt_svc_def serviceDefinition[2];
uint16_t eventHandle {};
std::string artistName {"Waiting for"};
std::string albumName {};
std::string trackName {"track information.."};
bool playing {false};
int trackProgress {0};
int trackLength {0};
int trackNumber {};
int tracksTotal {};
float playbackSpeed {1.0f};
bool repeat {false};
bool shuffle {false};
Pinetime::System::SystemTask& m_system;
};
}
}

View File

@@ -0,0 +1,111 @@
/* Copyright (C) 2021 Adam Pigg
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 "components/ble/NavigationService.h"
#include "systemtask/SystemTask.h"
namespace {
// // 0001yyxx-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, 0x01, 0x00}};
// }
//
// // 00010000-78fc-48fe-8e23-433b3a1942d0
// constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
// }
//
// constexpr ble_uuid128_t navUuid {BaseUuid()};
//
// constexpr ble_uuid128_t navFlagCharUuid {CharUuid(0x01, 0x00)};
// constexpr ble_uuid128_t navNarrativeCharUuid {CharUuid(0x02, 0x00)};
// constexpr ble_uuid128_t navManDistCharUuid {CharUuid(0x03, 0x00)};
// constexpr ble_uuid128_t navProgressCharUuid {CharUuid(0x04, 0x00)};
//
// int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
// auto navService = static_cast<Pinetime::Controllers::NavigationService*>(arg);
// return navService->OnCommand(conn_handle, attr_handle, ctxt);
// }
} // namespace
Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) {
// characteristicDefinition[0] = {
// .uuid = &navFlagCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
//
// characteristicDefinition[1] = {
// .uuid = &navNarrativeCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[2] = {
// .uuid = &navManDistCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[3] = {
// .uuid = &navProgressCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
//
// characteristicDefinition[4] = {0};
//
// serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &navUuid.u, .characteristics = characteristicDefinition};
// serviceDefinition[1] = {0};
m_progress = 0;
}
void Pinetime::Controllers::NavigationService::Init() {
// int res = 0;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
//int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
//
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
// uint8_t data[notifSize + 1];
// data[notifSize] = '\0';
// os_mbuf_copydata(ctxt->om, 0, notifSize, data);
// char* s = (char*) &data[0];
// if (ble_uuid_cmp(ctxt->chr->uuid, &navFlagCharUuid.u) == 0) {
// m_flag = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navNarrativeCharUuid.u) == 0) {
// m_narrative = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navManDistCharUuid.u) == 0) {
// m_manDist = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navProgressCharUuid.u) == 0) {
// m_progress = data[0];
// }
// }
// return 0;
//}
std::string Pinetime::Controllers::NavigationService::getFlag() {
return m_flag;
}
std::string Pinetime::Controllers::NavigationService::getNarrative() {
return m_narrative;
}
std::string Pinetime::Controllers::NavigationService::getManDist() {
return m_manDist;
}
int Pinetime::Controllers::NavigationService::getProgress() {
return m_progress;
}

View File

@@ -0,0 +1,63 @@
/* Copyright (C) 2021 Adam Pigg
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <string>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_uuid.h>
//#undef max
//#undef min
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class NavigationService {
public:
explicit NavigationService(Pinetime::System::SystemTask& system);
void Init();
// int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
std::string getFlag();
std::string getNarrative();
std::string getManDist();
int getProgress();
private:
// struct ble_gatt_chr_def characteristicDefinition[5];
// struct ble_gatt_svc_def serviceDefinition[2];
std::string m_flag;
std::string m_narrative;
std::string m_manDist;
int m_progress = 0;
Pinetime::System::SystemTask& m_system;
};
}
}

View File

@@ -0,0 +1,456 @@
#include "components/ble/NimbleController.h"
#include <cstring>
//#include <hal/nrf_rtc.h>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_hs.h>
//#include <host/ble_hs_id.h>
//#include <host/util/util.h>
//#include <controller/ble_ll.h>
//#include <controller/ble_hw.h>
//#include <services/gap/ble_svc_gap.h>
//#include <services/gatt/ble_svc_gatt.h>
//#undef max
//#undef min
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/datetime/DateTimeController.h"
#include "components/fs/FS.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::Ble& bleController,
DateTime& dateTimeController,
Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Controllers::FS& fs)
: systemTask {systemTask},
bleController {bleController},
dateTimeController {dateTimeController},
notificationManager {notificationManager},
spiNorFlash {spiNorFlash},
fs {fs},
// dfuService {systemTask, bleController, spiNorFlash},
// currentTimeClient {dateTimeController},
anService {systemTask, notificationManager},
// alertNotificationClient {systemTask, notificationManager},
// currentTimeService {dateTimeController},
musicService {systemTask},
weatherService {systemTask, dateTimeController},
navService {systemTask} {
// batteryInformationService {batteryController},
// immediateAlertService {systemTask, notificationManager},
// heartRateService {systemTask, heartRateController},
// motionService {systemTask, motionController},
// fsService {systemTask, fs},
// serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
}
//void nimble_on_reset(int reason) {
// NRF_LOG_INFO("Nimble lost sync, resetting state; reason=%d", reason);
//}
//
//void nimble_on_sync(void) {
// int rc;
//
// NRF_LOG_INFO("Nimble is synced");
//
// rc = ble_hs_util_ensure_addr(0);
// ASSERT(rc == 0);
//
// nptr->StartAdvertising();
//}
//
//int GAPEventCallback(struct ble_gap_event* event, void* arg) {
// auto nimbleController = static_cast<NimbleController*>(arg);
// return nimbleController->OnGAPEvent(event);
//}
void NimbleController::Init() {
// while (!ble_hs_synced()) {
// }
//
// nptr = this;
// ble_hs_cfg.reset_cb = nimble_on_reset;
// ble_hs_cfg.sync_cb = nimble_on_sync;
// ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
//
// ble_svc_gap_init();
// ble_svc_gatt_init();
//
// deviceInformationService.Init();
// currentTimeClient.Init();
// currentTimeService.Init();
musicService.Init();
weatherService.Init();
navService.Init();
// anService.Init();
// dfuService.Init();
// batteryInformationService.Init();
// immediateAlertService.Init();
// heartRateService.Init();
// motionService.Init();
// fsService.Init();
//
// int rc;
// rc = ble_hs_util_ensure_addr(0);
// ASSERT(rc == 0);
// rc = ble_hs_id_infer_auto(0, &addrType);
// ASSERT(rc == 0);
// rc = ble_svc_gap_device_name_set(deviceName);
// ASSERT(rc == 0);
// rc = ble_svc_gap_device_appearance_set(0xC2);
// ASSERT(rc == 0);
// Pinetime::Controllers::Ble::BleAddress address;
// rc = ble_hs_id_copy_addr(addrType, address.data(), nullptr);
// ASSERT(rc == 0);
//
// bleController.Address(std::move(address));
// switch (addrType) {
// case BLE_OWN_ADDR_PUBLIC:
// bleController.AddressType(Ble::AddressTypes::Public);
// break;
// case BLE_OWN_ADDR_RANDOM:
// bleController.AddressType(Ble::AddressTypes::Random);
// break;
// case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
// bleController.AddressType(Ble::AddressTypes::RPA_Public);
// break;
// case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
// bleController.AddressType(Ble::AddressTypes::RPA_Random);
// break;
// }
//
// rc = ble_gatts_start();
// ASSERT(rc == 0);
//
// RestoreBond();
//
// StartAdvertising();
}
//void NimbleController::StartAdvertising() {
// struct ble_gap_adv_params adv_params;
// struct ble_hs_adv_fields fields;
// struct ble_hs_adv_fields rsp_fields;
//
// memset(&adv_params, 0, sizeof(adv_params));
// memset(&fields, 0, sizeof(fields));
// memset(&rsp_fields, 0, sizeof(rsp_fields));
//
// adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
// adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
// /* fast advertise for 30 sec */
// if (fastAdvCount < 15) {
// adv_params.itvl_min = 32;
// adv_params.itvl_max = 47;
// fastAdvCount++;
// } else {
// adv_params.itvl_min = 1636;
// adv_params.itvl_max = 1651;
// }
//
// fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
// fields.uuids128 = &dfuServiceUuid;
// fields.num_uuids128 = 1;
// fields.uuids128_is_complete = 1;
// fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
//
// rsp_fields.name = reinterpret_cast<const uint8_t*>(deviceName);
// rsp_fields.name_len = strlen(deviceName);
// rsp_fields.name_is_complete = 1;
//
// int rc;
// rc = ble_gap_adv_set_fields(&fields);
// ASSERT(rc == 0);
//
// rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
// ASSERT(rc == 0);
//
// rc = ble_gap_adv_start(addrType, NULL, 2000, &adv_params, GAPEventCallback, this);
// ASSERT(rc == 0);
//}
//
//int NimbleController::OnGAPEvent(ble_gap_event* event) {
// switch (event->type) {
// case BLE_GAP_EVENT_ADV_COMPLETE:
// NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE");
// NRF_LOG_INFO("reason=%d; status=%0X", event->adv_complete.reason, event->connect.status);
// StartAdvertising();
// break;
//
// case BLE_GAP_EVENT_CONNECT:
// /* A new connection was established or a connection attempt failed. */
// NRF_LOG_INFO("Connect event : BLE_GAP_EVENT_CONNECT");
// NRF_LOG_INFO("connection %s; status=%0X ", event->connect.status == 0 ? "established" : "failed", event->connect.status);
//
// if (event->connect.status != 0) {
// /* Connection failed; resume advertising. */
// currentTimeClient.Reset();
// alertNotificationClient.Reset();
// connectionHandle = BLE_HS_CONN_HANDLE_NONE;
// bleController.Disconnect();
// fastAdvCount = 0;
// StartAdvertising();
// } else {
// connectionHandle = event->connect.conn_handle;
// bleController.Connect();
// systemTask.PushMessage(Pinetime::System::Messages::BleConnected);
// // Service discovery is deferred via systemtask
// }
// break;
//
// case BLE_GAP_EVENT_DISCONNECT:
// /* Connection terminated; resume advertising. */
// NRF_LOG_INFO("Disconnect event : BLE_GAP_EVENT_DISCONNECT");
// NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason);
//
// if (event->disconnect.conn.sec_state.bonded) {
// PersistBond(event->disconnect.conn);
// }
//
// currentTimeClient.Reset();
// alertNotificationClient.Reset();
// connectionHandle = BLE_HS_CONN_HANDLE_NONE;
// bleController.Disconnect();
// fastAdvCount = 0;
// StartAdvertising();
// break;
//
// case BLE_GAP_EVENT_CONN_UPDATE:
// /* The central has updated the connection parameters. */
// NRF_LOG_INFO("Update event : BLE_GAP_EVENT_CONN_UPDATE");
// NRF_LOG_INFO("update status=%0X ", event->conn_update.status);
// break;
//
// case BLE_GAP_EVENT_CONN_UPDATE_REQ:
// /* The central has requested updated connection parameters */
// NRF_LOG_INFO("Update event : BLE_GAP_EVENT_CONN_UPDATE_REQ");
// NRF_LOG_INFO("update request : itvl_min=%d itvl_max=%d latency=%d supervision=%d",
// event->conn_update_req.peer_params->itvl_min,
// event->conn_update_req.peer_params->itvl_max,
// event->conn_update_req.peer_params->latency,
// event->conn_update_req.peer_params->supervision_timeout);
// break;
//
// case BLE_GAP_EVENT_ENC_CHANGE:
// /* Encryption has been enabled or disabled for this connection. */
// NRF_LOG_INFO("Security event : BLE_GAP_EVENT_ENC_CHANGE");
// NRF_LOG_INFO("encryption change event; status=%0X ", event->enc_change.status);
//
// if (event->enc_change.status == 0) {
// struct ble_gap_conn_desc desc;
// ble_gap_conn_find(event->enc_change.conn_handle, &desc);
// if (desc.sec_state.bonded) {
// PersistBond(desc);
// }
//
// NRF_LOG_INFO("new state: encrypted=%d authenticated=%d bonded=%d key_size=%d",
// desc.sec_state.encrypted,
// desc.sec_state.authenticated,
// desc.sec_state.bonded,
// desc.sec_state.key_size);
// }
// break;
//
// case BLE_GAP_EVENT_PASSKEY_ACTION:
// /* Authentication has been requested for this connection.
// *
// * BLE authentication is determined by the combination of I/O capabilities
// * on the central and peripheral. When the peripheral is display only and
// * the central has a keyboard and display then passkey auth is selected.
// * When both the central and peripheral have displays and support yes/no
// * buttons then numeric comparison is selected. We currently advertise
// * display capability only so we only handle the "display" action here.
// *
// * Standards insist that the rand() PRNG be deterministic.
// * Use the tinycrypt prng here since rand() is predictable.
// */
// NRF_LOG_INFO("Security event : BLE_GAP_EVENT_PASSKEY_ACTION");
// if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
// struct ble_sm_io pkey = {0};
// pkey.action = event->passkey.params.action;
// pkey.passkey = ble_ll_rand() % 1000000;
// bleController.SetPairingKey(pkey.passkey);
// systemTask.PushMessage(Pinetime::System::Messages::OnPairing);
// ble_sm_inject_io(event->passkey.conn_handle, &pkey);
// }
// break;
//
// case BLE_GAP_EVENT_SUBSCRIBE:
// NRF_LOG_INFO("Subscribe event; conn_handle=%d attr_handle=%d "
// "reason=%d prevn=%d curn=%d previ=%d curi=???\n",
// event->subscribe.conn_handle,
// event->subscribe.attr_handle,
// event->subscribe.reason,
// event->subscribe.prev_notify,
// event->subscribe.cur_notify,
// event->subscribe.prev_indicate);
//
// if (event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) {
// heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// } else if (event->subscribe.prev_notify == 0 && event->subscribe.cur_notify == 1) {
// heartRateService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// } else if (event->subscribe.prev_notify == 1 && event->subscribe.cur_notify == 0) {
// heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// }
// break;
//
// case BLE_GAP_EVENT_MTU:
// NRF_LOG_INFO("MTU Update event; conn_handle=%d cid=%d mtu=%d", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
// break;
//
// case BLE_GAP_EVENT_REPEAT_PAIRING: {
// NRF_LOG_INFO("Pairing event : BLE_GAP_EVENT_REPEAT_PAIRING");
// /* We already have a bond with the peer, but it is attempting to
// * establish a new secure link. This app sacrifices security for
// * convenience: just throw away the old bond and accept the new link.
// */
//
// /* Delete the old bond. */
// struct ble_gap_conn_desc desc;
// ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
// ble_store_util_delete_peer(&desc.peer_id_addr);
//
// /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
// * continue with the pairing operation.
// */
// }
// return BLE_GAP_REPEAT_PAIRING_RETRY;
//
// case BLE_GAP_EVENT_NOTIFY_RX: {
// /* Peer sent us a notification or indication. */
// /* Attribute data is contained in event->notify_rx.attr_data. */
// NRF_LOG_INFO("Notify event : BLE_GAP_EVENT_NOTIFY_RX");
// size_t notifSize = OS_MBUF_PKTLEN(event->notify_rx.om);
//
// NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d "
// "attr_len=%d",
// event->notify_rx.indication ? "indication" : "notification",
// event->notify_rx.conn_handle,
// event->notify_rx.attr_handle,
// notifSize);
//
// alertNotificationClient.OnNotification(event);
// } break;
//
// case BLE_GAP_EVENT_NOTIFY_TX:
// NRF_LOG_INFO("Notify event : BLE_GAP_EVENT_NOTIFY_TX");
// break;
//
// case BLE_GAP_EVENT_IDENTITY_RESOLVED:
// NRF_LOG_INFO("Identity event : BLE_GAP_EVENT_IDENTITY_RESOLVED");
// break;
//
// default:
// NRF_LOG_INFO("UNHANDLED GAP event : %d", event->type);
// break;
// }
// return 0;
//}
void NimbleController::StartDiscovery() {
// if (connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
// serviceDiscovery.StartDiscovery(connectionHandle);
// }
}
//uint16_t NimbleController::connHandle() {
// return connectionHandle;
//}
void NimbleController::NotifyBatteryLevel(uint8_t level) {
// if (connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
// batteryInformationService.NotifyBatteryLevel(connectionHandle, level);
// }
}
//void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
// union ble_store_key key;
// union ble_store_value our_sec, peer_sec, peer_cccd_set[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)] = {0};
// int rc;
//
// memset(&key, 0, sizeof key);
// memset(&our_sec, 0, sizeof our_sec);
// key.sec.peer_addr = desc.peer_id_addr;
// rc = ble_store_read_our_sec(&key.sec, &our_sec.sec);
//
// if (memcmp(&our_sec.sec, &bondId, sizeof bondId) == 0) {
// return;
// }
//
// memcpy(&bondId, &our_sec.sec, sizeof bondId);
//
// memset(&key, 0, sizeof key);
// memset(&peer_sec, 0, sizeof peer_sec);
// key.sec.peer_addr = desc.peer_id_addr;
// rc += ble_store_read_peer_sec(&key.sec, &peer_sec.sec);
//
// if (rc == 0) {
// memset(&key, 0, sizeof key);
// key.cccd.peer_addr = desc.peer_id_addr;
// int peer_count = 0;
// ble_store_util_count(BLE_STORE_OBJ_TYPE_CCCD, &peer_count);
// for (int i = 0; i < peer_count; i++) {
// key.cccd.idx = peer_count;
// ble_store_read_cccd(&key.cccd, &peer_cccd_set[i].cccd);
// }
//
// /* Wakeup Spi and SpiNorFlash before accessing the file system
// * This should be fixed in the FS driver
// */
// systemTask.PushMessage(Pinetime::System::Messages::GoToRunning);
// systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
// vTaskDelay(10);
//
// lfs_file_t file_p;
//
// rc = fs.FileOpen(&file_p, "/bond.dat", LFS_O_WRONLY | LFS_O_CREAT);
// if (rc == 0) {
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&our_sec.sec), sizeof our_sec);
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_sec.sec), sizeof peer_sec);
// fs.FileWrite(&file_p, reinterpret_cast<const uint8_t*>(&peer_count), 1);
// for (int i = 0; i < peer_count; i++) {
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_cccd_set[i].cccd), sizeof(struct ble_store_value_cccd));
// }
// fs.FileClose(&file_p);
// }
// systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
// }
//}
//void NimbleController::RestoreBond() {
// lfs_file_t file_p;
// union ble_store_value sec, cccd;
// uint8_t peer_count = 0;
//
// if (fs.FileOpen(&file_p, "/bond.dat", LFS_O_RDONLY) == 0) {
// memset(&sec, 0, sizeof sec);
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&sec.sec), sizeof sec);
// ble_store_write_our_sec(&sec.sec);
//
// memset(&sec, 0, sizeof sec);
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&sec.sec), sizeof sec);
// ble_store_write_peer_sec(&sec.sec);
//
// fs.FileRead(&file_p, &peer_count, 1);
// for (int i = 0; i < peer_count; i++) {
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&cccd.cccd), sizeof(struct ble_store_value_cccd));
// ble_store_write_cccd(&cccd.cccd);
// }
//
// fs.FileClose(&file_p);
// fs.FileDelete("/bond.dat");
// }
//}

View File

@@ -0,0 +1,132 @@
#pragma once
#include <cstdint>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#undef max
//#undef min
//#include "components/ble/AlertNotificationClient.h"
#include "components/ble/AlertNotificationService.h"
//#include "components/ble/BatteryInformationService.h"
//#include "components/ble/CurrentTimeClient.h"
//#include "components/ble/CurrentTimeService.h"
//#include "components/ble/DeviceInformationService.h"
//#include "components/ble/DfuService.h"
//#include "components/ble/HeartRateService.h"
//#include "components/ble/ImmediateAlertService.h"
#include "components/ble/MusicService.h"
#include "components/ble/NavigationService.h"
//#include "components/ble/ServiceDiscovery.h"
//#include "components/ble/MotionService.h"
#include "components/ble/weather/WeatherService.h"
#include "components/fs/FS.h"
//#include "components/ble/FSService.h"
namespace Pinetime {
namespace Drivers {
class SpiNorFlash;
}
namespace System {
class SystemTask;
}
namespace Controllers {
class Battery;
class Ble;
class DateTime;
class FS;
class HeartRateController;
class MotionController;
class NotificationManager;
class NimbleController {
public:
NimbleController(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::Ble& bleController,
DateTime& dateTimeController,
Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Pinetime::Controllers::FS& fs);
void Init();
void StartAdvertising();
// int OnGAPEvent(ble_gap_event* event);
// int OnDiscoveryEvent(uint16_t i, const ble_gatt_error* pError, const ble_gatt_svc* pSvc);
// int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
// int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
// int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
// int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
// const ble_gatt_error* error,
// uint16_t characteristicValueHandle,
// const ble_gatt_dsc* descriptor);
void StartDiscovery();
Pinetime::Controllers::MusicService& music() {
return musicService;
};
Pinetime::Controllers::NavigationService& navigation() {
return navService;
};
Pinetime::Controllers::AlertNotificationService& alertService() {
return anService;
};
Pinetime::Controllers::WeatherService& weather() {
return weatherService;
};
uint16_t connHandle();
void NotifyBatteryLevel(uint8_t level);
void RestartFastAdv() {
fastAdvCount = 0;
}
private:
// void PersistBond(struct ble_gap_conn_desc& desc);
// void RestoreBond();
static constexpr const char* deviceName = "InfiniTime";
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::Ble& bleController;
DateTime& dateTimeController;
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
Pinetime::Controllers::FS& fs;
// Pinetime::Controllers::DfuService dfuService;
// DeviceInformationService deviceInformationService;
// CurrentTimeClient currentTimeClient;
AlertNotificationService anService;
// AlertNotificationClient alertNotificationClient;
// CurrentTimeService currentTimeService;
MusicService musicService;
WeatherService weatherService;
NavigationService navService;
// BatteryInformationService batteryInformationService;
// ImmediateAlertService immediateAlertService;
// HeartRateService heartRateService;
// MotionService motionService;
// FSService fsService;
// ServiceDiscovery serviceDiscovery;
uint8_t addrType;
// uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
uint8_t fastAdvCount = 0;
uint8_t bondId[16] = {0};
// ble_uuid128_t dfuServiceUuid {
// .u {.type = BLE_UUID_TYPE_128},
// .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
};
// static NimbleController* nptr;
}
}

View File

@@ -0,0 +1,602 @@
/* Copyright (C) 2021 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 <qcbor/qcbor_spiffy_decode.h>
#include "components/ble/weather/WeatherService.h"
//#include "libs/QCBOR/inc/qcbor/qcbor.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
return static_cast<Pinetime::Controllers::WeatherService*>(arg)->OnCommand(connHandle, attrHandle, ctxt);
}
WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController)
: system(system), dateTimeController(dateTimeController) {
nullHeader = &nullTimelineheader;
nullTimelineheader->timestamp = 0;
}
void WeatherService::Init() {
// uint8_t res = 0;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// const uint8_t packetLen = OS_MBUF_PKTLEN(ctxt->om); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
// if (packetLen <= 0) {
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// // Decode
// QCBORDecodeContext decodeContext;
// UsefulBufC encodedCbor = {ctxt->om->om_data, OS_MBUF_PKTLEN(ctxt->om)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
// QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL);
// // KINDLY provide us a fixed-length map
// QCBORDecode_EnterMap(&decodeContext, nullptr);
// // Always encodes to the smallest number of bytes based on the value
// int64_t tmpTimestamp = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// int64_t tmpExpires = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpExpires < 0 || tmpExpires > 4294967295) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// int64_t tmpEventType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpEventType < 0 ||
// tmpEventType >= static_cast<int64_t>(WeatherData::eventtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// switch (static_cast<WeatherData::eventtype>(tmpEventType)) {
// case WeatherData::eventtype::AirQuality: {
// std::unique_ptr<WeatherData::AirQuality> airquality = std::make_unique<WeatherData::AirQuality>();
// airquality->timestamp = tmpTimestamp;
// airquality->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// airquality->expires = tmpExpires;
// UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here?
// QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &stringBuf);
// if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// airquality->polluter = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 4294967295) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// airquality->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(airquality))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Obscuration: {
// std::unique_ptr<WeatherData::Obscuration> obscuration = std::make_unique<WeatherData::Obscuration>();
// obscuration->timestamp = tmpTimestamp;
// obscuration->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// obscuration->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::obscurationtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// obscuration->type = static_cast<WeatherData::obscurationtype>(tmpType);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 65535) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// obscuration->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(obscuration))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Precipitation: {
// std::unique_ptr<WeatherData::Precipitation> precipitation = std::make_unique<WeatherData::Precipitation>();
// precipitation->timestamp = tmpTimestamp;
// precipitation->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// precipitation->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::precipitationtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// precipitation->type = static_cast<WeatherData::precipitationtype>(tmpType);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// precipitation->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(precipitation))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Wind: {
// std::unique_ptr<WeatherData::Wind> wind = std::make_unique<WeatherData::Wind>();
// wind->timestamp = tmpTimestamp;
// wind->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// wind->expires = tmpExpires;
// int64_t tmpMin = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMin);
// if (tmpMin < 0 || tmpMin > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->speedMin = tmpMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpMax = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMax);
// if (tmpMax < 0 || tmpMax > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->speedMax = tmpMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDMin = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMin", &tmpDMin);
// if (tmpDMin < 0 || tmpDMin > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->directionMin = tmpDMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDMax = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMax", &tmpDMax);
// if (tmpDMax < 0 || tmpDMax > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->directionMax = tmpDMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(wind))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Temperature: {
// std::unique_ptr<WeatherData::Temperature> temperature = std::make_unique<WeatherData::Temperature>();
// temperature->timestamp = tmpTimestamp;
// temperature->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// temperature->expires = tmpExpires;
// int64_t tmpTemperature = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Temperature", &tmpTemperature);
// if (tmpTemperature < -32768 || tmpTemperature > 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// temperature->temperature =
// static_cast<int16_t>(tmpTemperature); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDewPoint = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint);
// if (tmpDewPoint < -32768 || tmpDewPoint > 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// temperature->dewPoint =
// static_cast<int16_t>(tmpDewPoint); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(temperature))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Special: {
// std::unique_ptr<WeatherData::Special> special = std::make_unique<WeatherData::Special>();
// special->timestamp = tmpTimestamp;
// special->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// special->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::specialtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// special->type = static_cast<WeatherData::specialtype>(tmpType);
// if (!AddEventToTimeline(std::move(special))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Pressure: {
// std::unique_ptr<WeatherData::Pressure> pressure = std::make_unique<WeatherData::Pressure>();
// pressure->timestamp = tmpTimestamp;
// pressure->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// pressure->expires = tmpExpires;
// int64_t tmpPressure = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Pressure", &tmpPressure);
// if (tmpPressure < 0 || tmpPressure >= 65535) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// pressure->pressure = tmpPressure; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(pressure))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Location: {
// std::unique_ptr<WeatherData::Location> location = std::make_unique<WeatherData::Location>();
// location->timestamp = tmpTimestamp;
// location->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// location->expires = tmpExpires;
// UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here?
// QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Location", &stringBuf);
// if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->location = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len);
// int64_t tmpAltitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Altitude", &tmpAltitude);
// if (tmpAltitude < -32768 || tmpAltitude >= 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->altitude = static_cast<int16_t>(tmpAltitude);
// int64_t tmpLatitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Latitude", &tmpLatitude);
// if (tmpLatitude < -2147483648 || tmpLatitude >= 2147483647) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->latitude = static_cast<int32_t>(tmpLatitude);
// int64_t tmpLongitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Longitude", &tmpLongitude);
// if (tmpLongitude < -2147483648 || tmpLongitude >= 2147483647) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->latitude = static_cast<int32_t>(tmpLongitude);
// if (!AddEventToTimeline(std::move(location))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Clouds: {
// std::unique_ptr<WeatherData::Clouds> clouds = std::make_unique<WeatherData::Clouds>();
// clouds->timestamp = tmpTimestamp;
// clouds->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// clouds->expires = tmpExpires;
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// clouds->amount = static_cast<uint8_t>(tmpAmount);
// if (!AddEventToTimeline(std::move(clouds))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Humidity: {
// std::unique_ptr<WeatherData::Humidity> humidity = std::make_unique<WeatherData::Humidity>();
// humidity->timestamp = tmpTimestamp;
// humidity->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// humidity->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Humidity", &tmpType);
// if (tmpType < 0 || tmpType >= 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// humidity->humidity = static_cast<uint8_t>(tmpType);
// if (!AddEventToTimeline(std::move(humidity))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// default: {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// }
// QCBORDecode_ExitMap(&decodeContext);
// GetTimelineLength();
// TidyTimeline();
// if (QCBORDecode_Finish(&decodeContext) != QCBOR_SUCCESS) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
// // Encode
// uint8_t buffer[64];
// QCBOREncodeContext encodeContext;
// /* TODO: This is very much still a test endpoint
// * it needs a characteristic UUID check
// * and actual implementations that show
// * what actually has to be read.
// * WARN: Consider commands not part of the API for now!
// */
// QCBOREncode_Init(&encodeContext, UsefulBuf_FROM_BYTE_ARRAY(buffer));
// QCBOREncode_OpenMap(&encodeContext);
// QCBOREncode_AddTextToMap(&encodeContext, "test", UsefulBuf_FROM_SZ_LITERAL("test"));
// QCBOREncode_AddInt64ToMap(&encodeContext, "test", 1ul);
// QCBOREncode_CloseMap(&encodeContext);
// UsefulBufC encodedEvent;
// auto uErr = QCBOREncode_Finish(&encodeContext, &encodedEvent);
// if (uErr != 0) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// auto res = os_mbuf_append(ctxt->om, &buffer, sizeof(buffer));
// if (res == 0) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// return 0;
// }
return 0;
}
std::unique_ptr<WeatherData::Clouds>& WeatherService::GetCurrentClouds() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Obscuration>& WeatherService::GetCurrentObscuration() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Precipitation>& WeatherService::GetCurrentPrecipitation() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Wind>& WeatherService::GetCurrentWind() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Temperature>& WeatherService::GetCurrentTemperature() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Humidity>& WeatherService::GetCurrentHumidity() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Pressure>& WeatherService::GetCurrentPressure() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Location>& WeatherService::GetCurrentLocation() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::AirQuality>& WeatherService::GetCurrentQuality() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(*this->nullHeader);
}
size_t WeatherService::GetTimelineLength() const {
return timeline.size();
}
bool WeatherService::AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event) {
if (timeline.size() == timeline.max_size()) {
return false;
}
timeline.push_back(std::move(event));
return true;
}
bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : timeline) {
if (header->eventType == type && IsEventStillValid(header, currentTimestamp)) {
return true;
}
}
return false;
}
void WeatherService::TidyTimeline() {
uint64_t timeCurrent = GetCurrentUnixTimestamp();
timeline.erase(std::remove_if(std::begin(timeline),
std::end(timeline),
[&](std::unique_ptr<WeatherData::TimelineHeader> const& header) {
return !IsEventStillValid(header, timeCurrent);
}),
std::end(timeline));
std::sort(std::begin(timeline), std::end(timeline), CompareTimelineEvents);
}
bool WeatherService::CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first,
const std::unique_ptr<WeatherData::TimelineHeader>& second) {
return first->timestamp > second->timestamp;
}
bool WeatherService::IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp) {
// Not getting timestamp in isEventStillValid for more speed
return uniquePtr->timestamp + uniquePtr->expires >= timestamp;
}
uint64_t WeatherService::GetCurrentUnixTimestamp() const {
return std::chrono::duration_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime().time_since_epoch()).count();
}
int16_t WeatherService::GetTodayMinTemp() const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) -
((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds());
int16_t result = -32768;
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) &&
header->timestamp < currentDayEnd &&
reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) {
int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature;
if (result == -32768) {
result = temperature;
} else if (result > temperature) {
result = temperature;
} else {
// The temperature in this item is higher than the lowest we've found
}
}
}
return result;
}
int16_t WeatherService::GetTodayMaxTemp() const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) -
((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds());
int16_t result = -32768;
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) &&
header->timestamp < currentDayEnd &&
reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) {
int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature;
if (result == -32768) {
result = temperature;
} else if (result < temperature) {
result = temperature;
} else {
// The temperature in this item is lower than the highest we've found
}
}
}
return result;
}
// void WeatherService::CleanUpQcbor(QCBORDecodeContext* decodeContext) {
// QCBORDecode_ExitMap(decodeContext);
// QCBORDecode_Finish(decodeContext);
// }

View File

@@ -0,0 +1,172 @@
/* Copyright (C) 2021 Avamander
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_uuid.h>
//#undef max
//#undef min
#include "components/ble/weather/WeatherData.h"
//#include "libs/QCBOR/inc/qcbor/qcbor.h"
#include "components/datetime/DateTimeController.h"
//int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg);
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class WeatherService {
public:
explicit WeatherService(System::SystemTask& system, DateTime& dateTimeController);
void Init();
int OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt);
/*
* Helper functions for quick access to currently valid data
*/
std::unique_ptr<WeatherData::Location>& GetCurrentLocation();
std::unique_ptr<WeatherData::Clouds>& GetCurrentClouds();
std::unique_ptr<WeatherData::Obscuration>& GetCurrentObscuration();
std::unique_ptr<WeatherData::Precipitation>& GetCurrentPrecipitation();
std::unique_ptr<WeatherData::Wind>& GetCurrentWind();
std::unique_ptr<WeatherData::Temperature>& GetCurrentTemperature();
std::unique_ptr<WeatherData::Humidity>& GetCurrentHumidity();
std::unique_ptr<WeatherData::Pressure>& GetCurrentPressure();
std::unique_ptr<WeatherData::AirQuality>& GetCurrentQuality();
/**
* Searches for the current day's maximum temperature
* @return -32768 if there's no data, degrees Celsius times 100 otherwise
*/
int16_t GetTodayMaxTemp() const;
/**
* Searches for the current day's minimum temperature
* @return -32768 if there's no data, degrees Celsius times 100 otherwise
*/
int16_t GetTodayMinTemp() const;
/*
* Management functions
*/
/**
* Adds an event to the timeline
* @return
*/
bool AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event);
/**
* Gets the current timeline length
*/
size_t GetTimelineLength() const;
/**
* Checks if an event of a certain type exists in the timeline
*/
bool HasTimelineEventOfType(WeatherData::eventtype type) const;
private:
// 00040000-78fc-48fe-8e23-433b3a1942d0
// static constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
// }
// // 0004yyxx-78fc-48fe-8e23-433b3a1942d0
// static 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, y, x, 0x04, 0x00}};
// }
// ble_uuid128_t weatherUuid {BaseUuid()};
/**
* Just write timeline data here.
*
* See {@link WeatherData.h} for more information.
*/
// ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)};
/**
* This doesn't take timeline data, provides some control over it.
*
* NOTE: Currently not supported. Companion app implementer feedback required.
* There's very little point in solidifying an API before we know the needs.
*/
// ble_uuid128_t weatherControlCharUuid {CharUuid(0x00, 0x02)};
// const struct ble_gatt_chr_def characteristicDefinition[3] = {
// {.uuid = &weatherDataCharUuid.u,
// .access_cb = WeatherCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE,
// .val_handle = &eventHandle},
// {.uuid = &weatherControlCharUuid.u, .access_cb = WeatherCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
// {nullptr}};
// const struct ble_gatt_svc_def serviceDefinition[2] = {
// {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition}, {0}};
uint16_t eventHandle {};
Pinetime::System::SystemTask& system;
Pinetime::Controllers::DateTime& dateTimeController;
std::vector<std::unique_ptr<WeatherData::TimelineHeader>> timeline;
std::unique_ptr<WeatherData::TimelineHeader> nullTimelineheader = std::make_unique<WeatherData::TimelineHeader>();
std::unique_ptr<WeatherData::TimelineHeader>* nullHeader;
/**
* Cleans up the timeline of expired events
*/
void TidyTimeline();
/**
* Compares two timeline events
*/
static bool CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first,
const std::unique_ptr<WeatherData::TimelineHeader>& second);
/**
* Returns current UNIX timestamp
*/
uint64_t GetCurrentUnixTimestamp() const;
/**
* Checks if the event hasn't gone past and expired
*
* @param header timeline event to check
* @param currentTimestamp what's the time right now
* @return if the event is valid
*/
static bool IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp);
/**
* This is a helper function that closes a QCBOR map and decoding context cleanly
*/
// void CleanUpQcbor(QCBORDecodeContext* decodeContext);
};
}
}

View File

@@ -0,0 +1,126 @@
#include "BrightnessController.h"
//#include <hal/nrf_gpio.h>
#include "displayapp/screens/Symbols.h"
#include "drivers/PinMap.h"
using namespace Pinetime::Controllers;
void BrightnessController::Init() {
//nrf_gpio_cfg_output(PinMap::LcdBacklightLow);
//nrf_gpio_cfg_output(PinMap::LcdBacklightMedium);
//nrf_gpio_cfg_output(PinMap::LcdBacklightHigh);
Set(level);
}
void BrightnessController::Set(BrightnessController::Levels level) {
this->level = level;
//switch (level) {
// default:
// case Levels::High:
// nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
// nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
// nrf_gpio_pin_clear(PinMap::LcdBacklightHigh);
// break;
// case Levels::Medium:
// nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
// nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
// nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
// break;
// case Levels::Low:
// nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
// nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
// nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
// break;
// case Levels::Off:
// nrf_gpio_pin_set(PinMap::LcdBacklightLow);
// nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
// nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
// break;
//}
}
void BrightnessController::Lower() {
switch (level) {
case Levels::High:
Set(Levels::Medium);
break;
case Levels::Medium:
Set(Levels::Low);
break;
case Levels::Low:
Set(Levels::Off);
break;
default:
break;
}
}
void BrightnessController::Higher() {
switch (level) {
case Levels::Off:
Set(Levels::Low);
break;
case Levels::Low:
Set(Levels::Medium);
break;
case Levels::Medium:
Set(Levels::High);
break;
default:
break;
}
}
BrightnessController::Levels BrightnessController::Level() const {
return level;
}
void BrightnessController::Backup() {
backupLevel = level;
}
void BrightnessController::Restore() {
Set(backupLevel);
}
void BrightnessController::Step() {
switch (level) {
case Levels::Low:
Set(Levels::Medium);
break;
case Levels::Medium:
Set(Levels::High);
break;
case Levels::High:
Set(Levels::Low);
break;
default:
break;
}
}
const char* BrightnessController::GetIcon() {
switch (level) {
case Levels::Medium:
return Applications::Screens::Symbols::brightnessMedium;
case Levels::High:
return Applications::Screens::Symbols::brightnessHigh;
default:
break;
}
return Applications::Screens::Symbols::brightnessLow;
}
const char* BrightnessController::ToString() {
switch (level) {
case Levels::Off:
return "Off";
case Levels::Low:
return "Low";
case Levels::Medium:
return "Medium";
case Levels::High:
return "High";
default:
return "???";
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Controllers {
class BrightnessController {
public:
enum class Levels { Off, Low, Medium, High };
void Init();
void Set(Levels level);
Levels Level() const;
void Lower();
void Higher();
void Step();
void Backup();
void Restore();
const char* GetIcon();
const char* ToString();
private:
Levels level = Levels::High;
Levels backupLevel = Levels::High;
};
}
}

View File

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

View File

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

250
sim/components/fs/FS.cpp Normal file
View File

@@ -0,0 +1,250 @@
#include "FS.h"
#include <cassert>
#include <cstring>
#include <filesystem>
//#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) {
// create the file in the current directory
const char *local_filename = fileName[0]=='/' ? &fileName[1] : fileName;
const char *mode;
bool flag_read = flags & LFS_O_RDONLY;
bool flag_write = flags & LFS_O_WRONLY;
bool flag_create = flags & LFS_O_CREAT;
if (flag_create) {
if (std::filesystem::exists(local_filename)) {
if (flag_read && flag_write) {
mode = "rb+";
} else if (flag_read) {
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
} else {
if (flag_read && flag_write) {
mode = "wb+";
} else if (flag_read) {
assert(false); // read only file not existing
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
}
} else {
if (std::filesystem::exists(local_filename)) {
if (flag_read && flag_write) {
mode = "rb+";
} else if (flag_read) {
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
} else {
return LFS_ERR_IO;
}
}
FILE *fptr = fopen(local_filename, mode);
if (fptr == nullptr) {
return LFS_ERR_BADF;
} else {
*file_p = fptr;
return LFS_ERR_OK;
}
//return lfs_file_open(&lfs, file_p, fileName, flags);
}
int FS::FileClose(lfs_file_t* file_p) {
return fclose(*file_p);
//return lfs_file_close(&lfs, file_p);
}
int FS::FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size) {
return fread(buff, sizeof(uint8_t), size, *file_p);
//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 fwrite((void*)buff, sizeof(uint8_t), size, *file_p);
//return lfs_file_write(&lfs, file_p, buff, size);
}
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos) {
return fseek(*file_p, pos, SEEK_SET);
//return lfs_file_seek(&lfs, file_p, pos, whence);
}
int FS::FileDelete(const char* fileName) {
return std::filesystem::remove(fileName);
//return lfs_remove(&lfs, fileName);
}
int FS::DirCreate(const char* path) {
return std::filesystem::create_directory(path);
//return lfs_mkdir(&lfs, path);
}
// Delete directory and all files inside
int FS::DirDelete(const char* path) {
return std::filesystem::remove_all(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);
int ret = filesys->FileOpen(file, path, LFS_O_RDONLY);
if (ret != LFS_ERR_OK) {
return LV_FS_RES_FS_ERR;
}
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_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);
}

134
sim/components/fs/FS.h Normal file
View File

@@ -0,0 +1,134 @@
#pragma once
#include <cstdio>
#include <cstdint>
//#include "drivers/SpiNorFlash.h"
//#include <littlefs/lfs.h>
#include <lvgl/lvgl.h>
using lfs_file_t = FILE*;
// copied from src/libs/littlefs/lfs.h
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs_error {
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -84, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_FBIG = -27, // File too large
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_NOATTR = -61, // No data/attr available
LFS_ERR_NAMETOOLONG = -36, // File name too long
};
enum lfs_open_flags {
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
#ifndef LFS_READONLY
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
#endif
};
// File seek flags
enum lfs_whence_flags {
LFS_SEEK_SET = 0, // Seek relative to an absolute position
LFS_SEEK_CUR = 1, // Seek relative to the current file position
LFS_SEEK_END = 2, // Seek relative to the end of the file
};
typedef int32_t lfs_ssize_t;
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 DirOpen(const char* path, lfs_dir_t* lfs_dir);
//int DirClose(lfs_dir_t* lfs_dir);
//int DirRead(lfs_dir_t* dir, lfs_info* info);
//int DirRewind(lfs_dir_t* dir);
int DirCreate(const char* path);
int DirDelete(const char* path);
lfs_ssize_t GetFSSize();
int Rename(const char* oldPath, const char* newPath);
//int Stat(const char* path, lfs_info* info);
void VerifyResource();
static size_t getSize() {
return size;
}
static size_t getBlockSize() {
return blockSize;
}
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 = 0x34C000;
static constexpr size_t blockSize = 4096;
lv_fs_drv_t fs_drv;
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

@@ -0,0 +1,35 @@
#include "components/heartrate/HeartRateController.h"
#include <heartratetask/HeartRateTask.h>
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) {
this->state = newState;
if (this->heartRate != heartRate) {
this->heartRate = heartRate;
//service->OnNewHeartRateValue(heartRate);
}
}
void HeartRateController::Start() {
if (task != nullptr) {
state = States::NotEnoughData;
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StartMeasurement);
}
}
void HeartRateController::Stop() {
if (task != nullptr) {
state = States::Stopped;
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StopMeasurement);
}
}
void HeartRateController::SetHeartRateTask(Pinetime::Applications::HeartRateTask* task) {
this->task = task;
}
//void HeartRateController::SetService(Pinetime::Controllers::HeartRateService* service) {
// this->service = service;
//}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <cstdint>
//#include <components/ble/HeartRateService.h>
namespace Pinetime {
namespace Applications {
class HeartRateTask;
}
namespace System {
class SystemTask;
}
namespace Controllers {
class HeartRateController {
public:
enum class States { Stopped, NotEnoughData, NoTouch, Running };
HeartRateController() = default;
void Start();
void Stop();
void Update(States newState, uint8_t heartRate);
void SetHeartRateTask(Applications::HeartRateTask* task);
States State() const {
return state;
}
uint8_t HeartRate() const {
return heartRate;
}
// void SetService(Pinetime::Controllers::HeartRateService* service);
private:
Applications::HeartRateTask* task = nullptr;
States state = States::Stopped;
uint8_t heartRate = 0;
//Pinetime::Controllers::HeartRateService* service = nullptr;
};
}
}

View File

@@ -0,0 +1,88 @@
#include "components/motion/MotionController.h"
//#include "os/os_cputime.h"
using namespace Pinetime::Controllers;
void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps) {
// if (this->nbSteps != nbSteps && service != nullptr) {
// service->OnNewStepCountValue(nbSteps);
// }
//
// if (service != nullptr && (this->x != x || this->y != y || this->z != z)) {
// service->OnNewMotionValues(x, y, z);
// }
this->x = x;
this->y = y;
this->z = z;
int32_t deltaSteps = nbSteps - this->nbSteps;
this->nbSteps = nbSteps;
if (deltaSteps > 0) {
currentTripSteps += deltaSteps;
}
}
bool MotionController::Should_RaiseWake(bool isSleeping) {
if ((x + 335) <= 670 && z < 0) {
if (not isSleeping) {
if (y <= 0) {
return false;
} else {
lastYForWakeUp = 0;
return false;
}
}
if (y >= 0) {
lastYForWakeUp = 0;
return false;
}
if (y + 230 < lastYForWakeUp) {
lastYForWakeUp = y;
return true;
}
}
return false;
}
bool MotionController::Should_ShakeWake(uint16_t thresh) {
return false;
// bool wake = false;
// auto diff = xTaskGetTickCount() - lastShakeTime;
// lastShakeTime = xTaskGetTickCount();
// /* Currently Polling at 10hz, If this ever goes faster scalar and EMA might need adjusting */
// int32_t speed = std::abs(z + (y / 2) + (x / 4) - lastYForShake - lastZForShake) / diff * 100;
// //(.2 * speed) + ((1 - .2) * accumulatedspeed);
// // implemented without floats as .25Alpha
// accumulatedspeed = (speed / 5) + ((accumulatedspeed / 5) * 4);
//
// if (accumulatedspeed > thresh) {
// wake = true;
// }
// lastXForShake = x / 4;
// lastYForShake = y / 2;
// lastZForShake = z;
// return wake;
}
int32_t MotionController::currentShakeSpeed() {
return accumulatedspeed;
}
void MotionController::IsSensorOk(bool isOk) {
isSensorOk = isOk;
}
void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) {
switch (types) {
case Drivers::Bma421::DeviceTypes::BMA421:
this->deviceType = DeviceTypes::BMA421;
break;
case Drivers::Bma421::DeviceTypes::BMA425:
this->deviceType = DeviceTypes::BMA425;
break;
default:
this->deviceType = DeviceTypes::Unknown;
break;
}
}
//void MotionController::SetService(Pinetime::Controllers::MotionService* service) {
// this->service = service;
//}

View File

@@ -0,0 +1,72 @@
#pragma once
#include <cstdint>
#include <drivers/Bma421.h>
//#include <components/ble/MotionService.h>
namespace Pinetime {
namespace Controllers {
class MotionController {
public:
enum class DeviceTypes{
Unknown,
BMA421,
BMA425,
};
void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
int16_t X() const {
return x;
}
int16_t Y() const {
return y;
}
int16_t Z() const {
return z;
}
uint32_t NbSteps() const {
return nbSteps;
}
void ResetTrip() {
currentTripSteps = 0;
}
uint32_t GetTripSteps() const {
return currentTripSteps;
}
bool Should_ShakeWake(uint16_t thresh);
bool Should_RaiseWake(bool isSleeping);
int32_t currentShakeSpeed();
void IsSensorOk(bool isOk);
bool IsSensorOk() const {
return isSensorOk;
}
DeviceTypes DeviceType() const {
return deviceType;
}
void Init(Pinetime::Drivers::Bma421::DeviceTypes types);
// void SetService(Pinetime::Controllers::MotionService* service);
private:
uint32_t nbSteps;
uint32_t currentTripSteps = 0;
int16_t x;
int16_t y;
int16_t z;
int16_t lastYForWakeUp = 0;
bool isSensorOk = false;
DeviceTypes deviceType = DeviceTypes::Unknown;
// Pinetime::Controllers::MotionService* service = nullptr;
int16_t lastXForShake = 0;
int16_t lastYForShake = 0;
int16_t lastZForShake = 0;
int32_t accumulatedspeed = 0;
uint32_t lastShakeTime = 0;
};
}
}

View File

@@ -0,0 +1,58 @@
#include "components/motor/MotorController.h"
#include <SDL2/SDL.h>
using namespace Pinetime::Controllers;
void MotorController::Init() {
//nrf_gpio_cfg_output(PinMap::Motor);
//nrf_gpio_pin_set(PinMap::Motor);
//app_timer_init();
//app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, StopMotor);
//app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, Ring);
}
void MotorController::Ring(void* p_context) {
auto* motorController = static_cast<MotorController*>(p_context);
motorController->RunForDuration(50);
}
Uint32 StopMotor_callback(Uint32 interval, void *param)
{
auto* motorController = static_cast<MotorController*>(param);
motorController->motor_is_running = false;
return 0; // cancel timer
}
Uint32 Ring_callback(Uint32 interval, void *param)
{
auto* motorController = static_cast<MotorController*>(param);
motorController->RunForDuration(50);
if (motorController->is_ringing) {
return interval;
}
return 0;
}
void MotorController::RunForDuration(uint8_t motorDuration) {
this->motor_is_running = true;
SDL_AddTimer(motorDuration, StopMotor_callback, this);
//nrf_gpio_pin_clear(PinMap::Motor);
//app_timer_start(shortVibTimer, APP_TIMER_TICKS(motorDuration), nullptr);
}
void MotorController::StartRinging() {
Ring(this);
is_ringing = true;
SDL_AddTimer(1000, Ring_callback, this);
//app_timer_start(longVibTimer, APP_TIMER_TICKS(1000), this);
}
void MotorController::StopRinging() {
is_ringing = false;
}
void MotorController::StopMotor(void* p_context) {
//nrf_gpio_pin_set(PinMap::Motor);
auto* motorController = static_cast<MotorController*>(p_context);
motorController->motor_is_running = false;
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Controllers {
class MotorController {
public:
MotorController() = default;
void Init();
void RunForDuration(uint8_t motorDuration);
void StartRinging();
void StopRinging();
bool motor_is_running = false;
bool is_ringing = false;
private:
static void Ring(void* p_context);
static void StopMotor(void* p_context);
};
}
}