Initial InfiniSim project
This commit is contained in:
95
sim/components/battery/BatteryController.cpp
Normal file
95
sim/components/battery/BatteryController.cpp
Normal 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;
|
||||
}
|
57
sim/components/battery/BatteryController.h
Normal file
57
sim/components/battery/BatteryController.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
125
sim/components/ble/AlertNotificationService.cpp
Normal file
125
sim/components/ble/AlertNotificationService.cpp
Normal 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 = ¬ificationEventUuid.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);
|
||||
}
|
68
sim/components/ble/AlertNotificationService.h
Normal file
68
sim/components/ble/AlertNotificationService.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
198
sim/components/ble/MusicService.cpp
Normal file
198
sim/components/ble/MusicService.cpp
Normal 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);
|
||||
}
|
87
sim/components/ble/MusicService.h
Normal file
87
sim/components/ble/MusicService.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
111
sim/components/ble/NavigationService.cpp
Normal file
111
sim/components/ble/NavigationService.cpp
Normal 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;
|
||||
}
|
63
sim/components/ble/NavigationService.h
Normal file
63
sim/components/ble/NavigationService.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
456
sim/components/ble/NimbleController.cpp
Normal file
456
sim/components/ble/NimbleController.cpp
Normal 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({¤tTimeClient, &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");
|
||||
// }
|
||||
//}
|
132
sim/components/ble/NimbleController.h
Normal file
132
sim/components/ble/NimbleController.h
Normal 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;
|
||||
}
|
||||
}
|
602
sim/components/ble/weather/WeatherService.cpp
Normal file
602
sim/components/ble/weather/WeatherService.cpp
Normal 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);
|
||||
// }
|
172
sim/components/ble/weather/WeatherService.h
Normal file
172
sim/components/ble/weather/WeatherService.h
Normal 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);
|
||||
};
|
||||
}
|
||||
}
|
126
sim/components/brightness/BrightnessController.cpp
Normal file
126
sim/components/brightness/BrightnessController.cpp
Normal 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 "???";
|
||||
}
|
||||
}
|
29
sim/components/brightness/BrightnessController.h
Normal file
29
sim/components/brightness/BrightnessController.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
21
sim/components/firmwarevalidator/FirmwareValidator.cpp
Normal file
21
sim/components/firmwarevalidator/FirmwareValidator.cpp
Normal 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();
|
||||
}
|
19
sim/components/firmwarevalidator/FirmwareValidator.h
Normal file
19
sim/components/firmwarevalidator/FirmwareValidator.h
Normal 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
250
sim/components/fs/FS.cpp
Normal 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
134
sim/components/fs/FS.h
Normal 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);
|
||||
|
||||
};
|
||||
}
|
||||
}
|
35
sim/components/heartrate/HeartRateController.cpp
Normal file
35
sim/components/heartrate/HeartRateController.cpp
Normal 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;
|
||||
//}
|
40
sim/components/heartrate/HeartRateController.h
Normal file
40
sim/components/heartrate/HeartRateController.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
88
sim/components/motion/MotionController.cpp
Normal file
88
sim/components/motion/MotionController.cpp
Normal 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;
|
||||
//}
|
72
sim/components/motion/MotionController.h
Normal file
72
sim/components/motion/MotionController.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
58
sim/components/motor/MotorController.cpp
Normal file
58
sim/components/motor/MotorController.cpp
Normal 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;
|
||||
}
|
25
sim/components/motor/MotorController.h
Normal file
25
sim/components/motor/MotorController.h
Normal 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);
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user