Initial InfiniSim project

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

3
sim/FreeRTOS.cpp Normal file
View File

@@ -0,0 +1,3 @@
#include "FreeRTOS.h"
void NVIC_SystemReset(void) {}

84
sim/FreeRTOS.h Normal file
View File

@@ -0,0 +1,84 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef INC_FREERTOS_H
#define INC_FREERTOS_H
#include "portmacro_cmsis.h"
//#include "app_error.h"
// from nrf_error.h
/** @defgroup NRF_ERRORS_BASE Error Codes Base number definitions
* @{ */
#define NRF_ERROR_BASE_NUM (0x0) ///< Global error base
#define NRF_ERROR_SDM_BASE_NUM (0x1000) ///< SDM error base
#define NRF_ERROR_SOC_BASE_NUM (0x2000) ///< SoC error base
#define NRF_ERROR_STK_BASE_NUM (0x3000) ///< STK error base
/** @} */
#define NRF_SUCCESS (NRF_ERROR_BASE_NUM + 0) ///< Successful command
#define NRF_ERROR_SVC_HANDLER_MISSING (NRF_ERROR_BASE_NUM + 1) ///< SVC handler is missing
#define NRF_ERROR_SOFTDEVICE_NOT_ENABLED (NRF_ERROR_BASE_NUM + 2) ///< SoftDevice has not been enabled
#define NRF_ERROR_INTERNAL (NRF_ERROR_BASE_NUM + 3) ///< Internal Error
#define NRF_ERROR_NO_MEM (NRF_ERROR_BASE_NUM + 4) ///< No Memory for operation
#define NRF_ERROR_NOT_FOUND (NRF_ERROR_BASE_NUM + 5) ///< Not found
#define NRF_ERROR_NOT_SUPPORTED (NRF_ERROR_BASE_NUM + 6) ///< Not supported
#define NRF_ERROR_INVALID_PARAM (NRF_ERROR_BASE_NUM + 7) ///< Invalid Parameter
#define NRF_ERROR_INVALID_STATE (NRF_ERROR_BASE_NUM + 8) ///< Invalid state, operation disallowed in this state
#define NRF_ERROR_INVALID_LENGTH (NRF_ERROR_BASE_NUM + 9) ///< Invalid Length
#define NRF_ERROR_INVALID_FLAGS (NRF_ERROR_BASE_NUM + 10) ///< Invalid Flags
#define NRF_ERROR_INVALID_DATA (NRF_ERROR_BASE_NUM + 11) ///< Invalid Data
#define NRF_ERROR_DATA_SIZE (NRF_ERROR_BASE_NUM + 12) ///< Data size exceeds limit
#define NRF_ERROR_TIMEOUT (NRF_ERROR_BASE_NUM + 13) ///< Operation timed out
#define NRF_ERROR_NULL (NRF_ERROR_BASE_NUM + 14) ///< Null Pointer
#define NRF_ERROR_FORBIDDEN (NRF_ERROR_BASE_NUM + 15) ///< Forbidden Operation
#define NRF_ERROR_INVALID_ADDR (NRF_ERROR_BASE_NUM + 16) ///< Bad Memory Address
#define NRF_ERROR_BUSY (NRF_ERROR_BASE_NUM + 17) ///< Busy
#include <stdexcept>
template<typename T>
void APP_ERROR_HANDLER(T err) {
throw std::runtime_error("APP_ERROR_HANDLER: " + std::to_string(err));
}
struct SCB_t {
unsigned ICSR = 0;
};
static SCB_t SCB_member;
static SCB_t *SCB = &SCB_member;
//#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */
constexpr unsigned SCB_ICSR_VECTACTIVE_Msk = 0x01;
/**
\brief System Reset
\details Initiates a system reset request to reset the MCU.
*/
// copied from nRF5_SDK_15.3.0_59ac345/components/toolchain/cmsis/include/core_cm4.h
void NVIC_SystemReset(void);
#endif /* INC_FREERTOS_H */

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,198 @@
/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "components/ble/MusicService.h"
#include "systemtask/SystemTask.h"
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
//constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
// return ble_uuid128_t{
// .u = {.type = BLE_UUID_TYPE_128},
// .value = { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
// };
//}
// 00000000-78fc-48fe-8e23-433b3a1942d0
//constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
//}
//constexpr ble_uuid128_t msUuid {BaseUuid()};
//constexpr ble_uuid128_t msEventCharUuid {CharUuid(0x01, 0x00)};
//constexpr ble_uuid128_t msStatusCharUuid {CharUuid(0x02, 0x00)};
//constexpr ble_uuid128_t msArtistCharUuid {CharUuid(0x03, 0x00)};
//constexpr ble_uuid128_t msTrackCharUuid {CharUuid(0x04, 0x00)};
//constexpr ble_uuid128_t msAlbumCharUuid {CharUuid(0x05, 0x00)};
//constexpr ble_uuid128_t msPositionCharUuid {CharUuid(0x06, 0x00)};
//constexpr ble_uuid128_t msTotalLengthCharUuid {CharUuid(0x07, 0x00)};
//constexpr ble_uuid128_t msTrackNumberCharUuid {CharUuid(0x08, 0x00)};
//constexpr ble_uuid128_t msTrackTotalCharUuid {CharUuid(0x09, 0x00)};
//constexpr ble_uuid128_t msPlaybackSpeedCharUuid {CharUuid(0x0a, 0x00)};
//constexpr ble_uuid128_t msRepeatCharUuid {CharUuid(0x0b, 0x00)};
//constexpr ble_uuid128_t msShuffleCharUuid {CharUuid(0x0c, 0x00)};
//int MusicCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
// return static_cast<Pinetime::Controllers::MusicService*>(arg)->OnCommand(conn_handle, attr_handle, ctxt);
//}
}
Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask& system) : m_system(system) {
// characteristicDefinition[0] = {.uuid = &msEventCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_NOTIFY,
// .val_handle = &eventHandle};
// characteristicDefinition[1] = {.uuid = &msStatusCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[2] = {.uuid = &msTrackCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[3] = {.uuid = &msArtistCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[4] = {.uuid = &msAlbumCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[5] = {.uuid = &msPositionCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[6] = {.uuid = &msTotalLengthCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[7] = {.uuid = &msTotalLengthCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[8] = {.uuid = &msTrackNumberCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[9] = {.uuid = &msTrackTotalCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[10] = {.uuid = &msPlaybackSpeedCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[11] = {.uuid = &msRepeatCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[12] = {.uuid = &msShuffleCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[13] = {0};
//
// serviceDefinition[0] = {
// .type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &msUuid.u, .characteristics = characteristicDefinition};
// serviceDefinition[1] = {0};
}
void Pinetime::Controllers::MusicService::Init() {
//uint8_t res = 0;
//res = ble_gatts_count_cfg(serviceDefinition);
//ASSERT(res == 0);
//res = ble_gatts_add_svcs(serviceDefinition);
//ASSERT(res == 0);
}
//int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
// char data[notifSize + 1];
// data[notifSize] = '\0';
// os_mbuf_copydata(ctxt->om, 0, notifSize, data);
// char* s = &data[0];
// if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
// artistName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackCharUuid.u) == 0) {
// trackName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msAlbumCharUuid.u) == 0) {
// albumName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msStatusCharUuid.u) == 0) {
// playing = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msRepeatCharUuid.u) == 0) {
// repeat = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msShuffleCharUuid.u) == 0) {
// shuffle = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msPositionCharUuid.u) == 0) {
// trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTotalLengthCharUuid.u) == 0) {
// trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackNumberCharUuid.u) == 0) {
// trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackTotalCharUuid.u) == 0) {
// tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msPlaybackSpeedCharUuid.u) == 0) {
// playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f;
// }
// }
// return 0;
//}
std::string Pinetime::Controllers::MusicService::getAlbum() const {
return albumName;
}
std::string Pinetime::Controllers::MusicService::getArtist() const {
return artistName;
}
std::string Pinetime::Controllers::MusicService::getTrack() const {
return trackName;
}
bool Pinetime::Controllers::MusicService::isPlaying() const {
return playing;
}
float Pinetime::Controllers::MusicService::getPlaybackSpeed() const {
return playbackSpeed;
}
int Pinetime::Controllers::MusicService::getProgress() const {
return trackProgress;
}
int Pinetime::Controllers::MusicService::getTrackLength() const {
return trackLength;
}
void Pinetime::Controllers::MusicService::event(char event) {
std::ignore = event;
//auto* om = ble_hs_mbuf_from_flat(&event, 1);
//uint16_t connectionHandle = m_system.nimble().connHandle();
//if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
//}
//ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}

View File

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

View File

@@ -0,0 +1,111 @@
/* Copyright (C) 2021 Adam Pigg
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "components/ble/NavigationService.h"
#include "systemtask/SystemTask.h"
namespace {
// // 0001yyxx-78fc-48fe-8e23-433b3a1942d0
// constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
// return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128},
// .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x01, 0x00}};
// }
//
// // 00010000-78fc-48fe-8e23-433b3a1942d0
// constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
// }
//
// constexpr ble_uuid128_t navUuid {BaseUuid()};
//
// constexpr ble_uuid128_t navFlagCharUuid {CharUuid(0x01, 0x00)};
// constexpr ble_uuid128_t navNarrativeCharUuid {CharUuid(0x02, 0x00)};
// constexpr ble_uuid128_t navManDistCharUuid {CharUuid(0x03, 0x00)};
// constexpr ble_uuid128_t navProgressCharUuid {CharUuid(0x04, 0x00)};
//
// int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
// auto navService = static_cast<Pinetime::Controllers::NavigationService*>(arg);
// return navService->OnCommand(conn_handle, attr_handle, ctxt);
// }
} // namespace
Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) {
// characteristicDefinition[0] = {
// .uuid = &navFlagCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
//
// characteristicDefinition[1] = {
// .uuid = &navNarrativeCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[2] = {
// .uuid = &navManDistCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[3] = {
// .uuid = &navProgressCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
//
// characteristicDefinition[4] = {0};
//
// serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &navUuid.u, .characteristics = characteristicDefinition};
// serviceDefinition[1] = {0};
m_progress = 0;
}
void Pinetime::Controllers::NavigationService::Init() {
// int res = 0;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
//int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
//
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
// uint8_t data[notifSize + 1];
// data[notifSize] = '\0';
// os_mbuf_copydata(ctxt->om, 0, notifSize, data);
// char* s = (char*) &data[0];
// if (ble_uuid_cmp(ctxt->chr->uuid, &navFlagCharUuid.u) == 0) {
// m_flag = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navNarrativeCharUuid.u) == 0) {
// m_narrative = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navManDistCharUuid.u) == 0) {
// m_manDist = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navProgressCharUuid.u) == 0) {
// m_progress = data[0];
// }
// }
// return 0;
//}
std::string Pinetime::Controllers::NavigationService::getFlag() {
return m_flag;
}
std::string Pinetime::Controllers::NavigationService::getNarrative() {
return m_narrative;
}
std::string Pinetime::Controllers::NavigationService::getManDist() {
return m_manDist;
}
int Pinetime::Controllers::NavigationService::getProgress() {
return m_progress;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

@@ -0,0 +1,134 @@
#pragma once
#include <cstdio>
#include <cstdint>
//#include "drivers/SpiNorFlash.h"
//#include <littlefs/lfs.h>
#include <lvgl/lvgl.h>
using lfs_file_t = FILE*;
// copied from src/libs/littlefs/lfs.h
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs_error {
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -84, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_FBIG = -27, // File too large
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_NOATTR = -61, // No data/attr available
LFS_ERR_NAMETOOLONG = -36, // File name too long
};
enum lfs_open_flags {
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
#ifndef LFS_READONLY
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
#endif
};
// File seek flags
enum lfs_whence_flags {
LFS_SEEK_SET = 0, // Seek relative to an absolute position
LFS_SEEK_CUR = 1, // Seek relative to the current file position
LFS_SEEK_END = 2, // Seek relative to the end of the file
};
typedef int32_t lfs_ssize_t;
namespace Pinetime {
namespace Controllers {
class FS {
public:
//FS(Pinetime::Drivers::SpiNorFlash&);
void Init();
void LVGLFileSystemInit();
int FileOpen(lfs_file_t* file_p, const char* fileName, const int flags);
int FileClose(lfs_file_t* file_p);
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
int FileSeek(lfs_file_t* file_p, uint32_t pos);
int FileDelete(const char* fileName);
//int DirOpen(const char* path, lfs_dir_t* lfs_dir);
//int DirClose(lfs_dir_t* lfs_dir);
//int DirRead(lfs_dir_t* dir, lfs_info* info);
//int DirRewind(lfs_dir_t* dir);
int DirCreate(const char* path);
int DirDelete(const char* path);
lfs_ssize_t GetFSSize();
int Rename(const char* oldPath, const char* newPath);
//int Stat(const char* path, lfs_info* info);
void VerifyResource();
static size_t getSize() {
return size;
}
static size_t getBlockSize() {
return blockSize;
}
private:
//Pinetime::Drivers::SpiNorFlash& flashDriver;
/*
* External Flash MAP (4 MBytes)
*
* 0x000000 +---------------------------------------+
* | Bootloader Assets |
* | 256 KBytes |
* | |
* 0x040000 +---------------------------------------+
* | OTA |
* | 464 KBytes |
* | |
* | |
* | |
* 0x0B4000 +---------------------------------------+
* | File System |
* | |
* | |
* | |
* | |
* 0x400000 +---------------------------------------+
*
*/
static constexpr size_t startAddress = 0x0B4000;
static constexpr size_t size = 0x34C000;
static constexpr size_t blockSize = 4096;
lv_fs_drv_t fs_drv;
bool resourcesValid = false;
//const struct lfs_config lfsConfig;
//lfs_t lfs;
//static int SectorSync(const struct lfs_config* c);
//static int SectorErase(const struct lfs_config* c, lfs_block_t block);
//static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
//static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
};
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,327 @@
#include "displayapp/LittleVgl.h"
#include "displayapp/lv_pinetime_theme.h"
//#include <FreeRTOS.h>
//#include <task.h>
////#include <projdefs.h>
#include "drivers/Cst816s.h"
#include "drivers/St7789.h"
using namespace Pinetime::Components;
//lv_style_t* LabelBigStyle = nullptr;
//
//static void disp_flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p) {
// auto* lvgl = static_cast<LittleVgl*>(disp_drv->user_data);
// lvgl->FlushDisplay(area, color_p);
//}
//
bool touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data) {
auto* lvgl = static_cast<LittleVgl*>(indev_drv->user_data);
return lvgl->GetTouchPadInfo(data);
}
LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel)
: lcd {lcd}, touchPanel {touchPanel}, previousClick {0, 0} {
}
void LittleVgl::Init() {
// lv_init();
// InitDisplay();
// InitTheme();
InitTouchpad();
}
//void LittleVgl::InitDisplay() {
// lv_disp_draw_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 4); /*Initialize the display buffer*/
// lv_disp_drv_init(&disp_drv); /*Basic initialization*/
//
// /*Set up the functions to access to your display*/
//
// /*Set the resolution of the display*/
// disp_drv.hor_res = 240;
// disp_drv.ver_res = 240;
//
// /*Used to copy the buffer's content to the display*/
// disp_drv.flush_cb = disp_flush;
// /*Set a display buffer*/
// disp_drv.draw_buf = &disp_buf_2;
// disp_drv.user_data = this;
//
// /*Finally register the driver*/
// lv_disp_drv_register(&disp_drv);
//}
void LittleVgl::InitTouchpad() {
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_drv.user_data = this;
lv_indev_drv_register(&indev_drv);
}
void LittleVgl::SetFullRefresh(FullRefreshDirections direction) {
if (scrollDirection == FullRefreshDirections::None) {
scrollDirection = direction;
if (scrollDirection == FullRefreshDirections::Down) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
} else if (scrollDirection == FullRefreshDirections::Right) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
} else if (scrollDirection == FullRefreshDirections::Left) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
} else if (scrollDirection == FullRefreshDirections::RightAnim) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
} else if (scrollDirection == FullRefreshDirections::LeftAnim) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
}
}
}
//
//
//void LittleVgl::DisplayDownScroll(){
// // We are controlling the drawing process, disable lvgl timers
// lv_timer_enable(false);
//
// // For each segment, draw the full width, 4 lines at a time starting from the bottom
// // TODO: Should probably calculate this from the size of the draw buffer
// int16_t height = 4;
// int16_t width = 240;
// int16_t y2 = 240;
// int16_t y1 = 240 - height;
//
// lv_area_t area;
// area.x1 = 0;
// area.x2 = width;
//
// // Start from the bottom and create a 4 line high box
// for (y1 = 240 - height; y1 >= 0; y1 -= height) {
// y2 = y1 + height - 1;
//
// // If the box has reached the end of the visible line on the lcd controller...
// if (y2 == visibleNbLines - 1) {
// // move past the non visible lines
// writeOffset += (totalNbLines - visibleNbLines);
// // and wrap around to the start of address space
// writeOffset %= totalNbLines;
// }
// // Set new box
// area.y1 = y1;
// area.y2 = y2;
//
// // Scroll as we draw
// uint16_t toScroll = height;
// if (scrollOffset >= toScroll)
// scrollOffset -= toScroll;
// else { // now we need to wrap the scroll address
// toScroll -= scrollOffset;
// scrollOffset = totalNbLines - toScroll;
// }
// lcd.VerticalScrollStartAddress(scrollOffset);
//
// lv_disp_t* disp = lv_disp_get_default();
// // Clear invalid area list / tells lvgl that nothing on the screen needs to be updated
// _lv_inv_area(disp, nullptr);
// // invalidate only the segment we want to update in this portion of the animation
// _lv_inv_area(disp, &area);
// // cancel any current flushes in the display driver
// // Since we've stopped timers, it will be waiting forever if there is currently a flush
// lv_disp_flush_ready(disp->driver);
// lv_refr_now(disp);
// }
// // Done! clear flags and enable timers
// scrollDirection = FullRefreshDirections::None;
// animating = false;
// lv_timer_enable(true);
//}
//
//void LittleVgl::DisplayHorizAnim() {
// lv_timer_enable(false);
//
// int16_t height, width, x1, x2;
// lv_area_t area;
//
// height = 240;
// width = 4;
// int16_t (*NextStep)(int16_t, int16_t){};
// bool (*CheckEnd)(int16_t){};
//
// area.y1=0;
// area.y2=height;
//
// if (scrollDirection == FullRefreshDirections::RightAnim) {
// x1 = 0;
//
// CheckEnd = [](int16_t x) -> bool {
// return (x < LV_HOR_RES_MAX);
// };
// NextStep = [](int16_t x, int16_t width) -> int16_t {
// auto newx = x + width * 2;
// if (newx < 240) {return newx;};
// return (newx < 240 + width) ? (newx - 240 + width) : newx;
// };
//
// } else if (scrollDirection == FullRefreshDirections::LeftAnim) {
// x1 = 240 - width;
//
// CheckEnd = [](int16_t x) -> bool {
// return (x >= 0);
// };
// NextStep = [](int16_t x, int16_t width) -> int16_t {
// auto newx = x - width * 2;
// if (newx >= 0) {return newx;}
// return (newx >= 0 - width) ? (newx + 240 - width) : newx;
// };
//
// } else {
// // Not set for a horizontal animation!
// lv_timer_enable(true);
// return;
// }
//
// for (; CheckEnd(x1); x1 = NextStep(x1, width)) {
// x2 = x1 + width-1;
//
// if (area.y2 == visibleNbLines - 1) {
// writeOffset += (totalNbLines - visibleNbLines);
// writeOffset %= totalNbLines;
// }
// area.x1 = x1;
// area.x2 = x2;
//
// lv_disp_t* disp = lv_disp_get_default();
// _lv_inv_area(disp, nullptr);
// _lv_inv_area(disp, &area);
// lv_disp_flush_ready(disp->driver);
// lv_refr_now(disp);
// }
// scrollDirection = FullRefreshDirections::None;
// animating = false;
// lv_timer_enable(true);
//}
//
//void LittleVgl::FlushDisplayManually() {
// switch(scrollDirection){
// case FullRefreshDirections::Down:
// DisplayDownScroll();
// break;
// case FullRefreshDirections::RightAnim:
// case FullRefreshDirections::LeftAnim:
// DisplayHorizAnim();
// break;
// default:
// break;
// }
//}
//
void LittleVgl::FlushDisplay(const lv_area_t* area, lv_color_t* color_p) {
// uint16_t y1, y2, width, height = 0;
//
// ulTaskNotifyTake(pdTRUE, 200);
// // NOtification is still needed (even if there is a mutex on SPI) because of the DataCommand pin
// // which cannot be set/clear during a transfert.
//
// if (!animating && (scrollDirection == FullRefreshDirections::Down ||
// scrollDirection == FullRefreshDirections::RightAnim ||
// scrollDirection == FullRefreshDirections::LeftAnim)){
// animating = true;
// FlushDisplayManually();
// return;
// }
//
// if ((scrollDirection == FullRefreshDirections::Up) && (area->y1 == 0)) {
// writeOffset = (writeOffset + visibleNbLines) % totalNbLines;
// }
//
// y1 = (area->y1 + writeOffset) % totalNbLines;
// y2 = (area->y2 + writeOffset) % totalNbLines;
//
// width = (area->x2 - area->x1) + 1;
// height = (area->y2 - area->y1) + 1;
//
// if (scrollDirection == FullRefreshDirections::Up) {
//
// if (area->y1 > 0) {
// if (area->y2 == visibleNbLines - 1) {
// scrollOffset += (height * 2);
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// } else {
// scrollOffset += height;
// }
// scrollOffset = scrollOffset % totalNbLines;
// lcd.VerticalScrollStartAddress(scrollOffset);
// }
// } else if (scrollDirection == FullRefreshDirections::Left or scrollDirection == FullRefreshDirections::LeftAnim) {
// if (area->x2 == visibleNbLines - 1) {
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// }
// } else if (scrollDirection == FullRefreshDirections::Right or scrollDirection == FullRefreshDirections::RightAnim) {
// if (area->x1 == 0) {
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// }
// }
//
// if (y2 < y1) {
// height = totalNbLines - y1;
//
// if (height > 0) {
// lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
// ulTaskNotifyTake(pdTRUE, 100);
// }
//
// uint16_t pixOffset = width * height;
// height = y2 + 1;
// lcd.DrawBuffer(area->x1, 0, width, height, reinterpret_cast<const uint8_t*>(color_p + pixOffset), width * height * 2);
//
// } else {
// lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
// }
//
// // IMPORTANT!!!
// // Inform the graphics library that you are ready with the flushing
// lv_disp_flush_ready(&disp_drv);
lv_disp_t *disp = lv_disp_get_default();
lv_disp_drv_t *disp_drv = &disp->driver;
lv_area_t area_trimmed = *area;
if (area->x1 < 0)
area_trimmed.x1 = 0;
if (area->x2 >= LV_HOR_RES)
area_trimmed.x2 = LV_HOR_RES-1;
if (area->y1 < 0)
area_trimmed.y1 = 0;
if (area->y2 >= LV_VER_RES)
area_trimmed.y2 = LV_VER_RES-1;
// tell flush_cb this is the last thing to flush to get the monitor refreshed
lv_disp_get_buf(disp)->flushing_last = true;
disp_drv->flush_cb(disp_drv, &area_trimmed, color_p);
}
void LittleVgl::SetNewTouchPoint(uint16_t x, uint16_t y, bool contact) {
tap_x = x;
tap_y = y;
tapped = contact;
}
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
ptr->point.x = tap_x;
ptr->point.y = tap_y;
if (tapped) {
ptr->state = LV_INDEV_STATE_PR;
} else {
ptr->state = LV_INDEV_STATE_REL;
}
return false;
}
//void LittleVgl::InitTheme() {
// if (!lv_pinetime_theme_is_inited()) {
// lv_theme_t* th = lv_pinetime_theme_init(lv_disp_get_default(), lv_color_white(), lv_color_hex(0xC0C0C0), &jetbrains_mono_bold_20);
// lv_disp_set_theme(lv_disp_get_default(), th);
// }
//}

View File

@@ -0,0 +1,64 @@
#pragma once
#include <lvgl/lvgl.h>
namespace Pinetime {
namespace Drivers {
class Cst816S;
class St7789;
}
namespace Components {
class LittleVgl {
public:
enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel);
LittleVgl(const LittleVgl&) = delete;
LittleVgl& operator=(const LittleVgl&) = delete;
LittleVgl(LittleVgl&&) = delete;
LittleVgl& operator=(LittleVgl&&) = delete;
void Init();
void FlushDisplay(const lv_area_t* area, lv_color_t* color_p);
bool GetTouchPadInfo(lv_indev_data_t* ptr);
void SetFullRefresh(FullRefreshDirections direction);
void SetNewTouchPoint(uint16_t x, uint16_t y, bool contact);
//
// private:
// void InitDisplay();
void InitTouchpad();
// void InitTheme();
//
// void FlushDisplayManually();
// void DisplayDownScroll();
// void DisplayHorizAnim();
Pinetime::Drivers::St7789& lcd;
Pinetime::Drivers::Cst816S& touchPanel;
// lv_disp_draw_buf_t disp_buf_2;
// lv_color_t buf2_1[LV_HOR_RES_MAX * 4];
// lv_color_t buf2_2[LV_HOR_RES_MAX * 4];
//
lv_disp_drv_t disp_drv;
lv_point_t previousClick;
bool firstTouch = true;
static constexpr uint8_t nbWriteLines = 4;
static constexpr uint16_t totalNbLines = 320;
static constexpr uint16_t visibleNbLines = 240;
static constexpr uint8_t MaxScrollOffset() {
return LV_VER_RES_MAX - nbWriteLines;
}
FullRefreshDirections scrollDirection = FullRefreshDirections::None;
uint16_t writeOffset = 0;
uint16_t scrollOffset = 0;
uint16_t tap_x = 0;
uint16_t tap_y = 0;
bool tapped = false;
};
}
}

View File

@@ -0,0 +1,131 @@
#include "displayapp/screens/Missing.h"
#include "displayapp/DisplayApp.h"
using namespace Pinetime::Applications::Screens;
Missing::Missing(Pinetime::Applications::DisplayApp* app, Pinetime::Applications::Apps app_key) : Screen(app) {
const char *screen_lbl;
switch (app_key) {
case Pinetime::Applications::Apps::None:
screen_lbl = "None";
break;
case Pinetime::Applications::Apps::Launcher:
screen_lbl = "Launcher";
break;
case Pinetime::Applications::Apps::Clock:
screen_lbl = "Clock";
break;
case Pinetime::Applications::Apps::SysInfo:
screen_lbl = "SysInfo";
break;
case Pinetime::Applications::Apps::FirmwareUpdate:
screen_lbl = "FirmwareUpdate";
break;
case Pinetime::Applications::Apps::FirmwareValidation:
screen_lbl = "FirmwareValidation";
break;
case Pinetime::Applications::Apps::NotificationsPreview:
screen_lbl = "NotificationPreview";
break;
case Pinetime::Applications::Apps::Notifications:
screen_lbl = "Notifications";
break;
case Pinetime::Applications::Apps::Timer:
screen_lbl = "Timer";
break;
case Pinetime::Applications::Apps::Alarm:
screen_lbl = "Alarm";
break;
case Pinetime::Applications::Apps::FlashLight:
screen_lbl = "FlashLight";
break;
case Pinetime::Applications::Apps::BatteryInfo:
screen_lbl = "BatteryInfo";
break;
case Pinetime::Applications::Apps::Music:
screen_lbl = "Music";
break;
case Pinetime::Applications::Apps::Paint:
screen_lbl = "Paint";
break;
case Pinetime::Applications::Apps::Paddle:
screen_lbl = "Paddle";
break;
case Pinetime::Applications::Apps::Twos:
screen_lbl = "Twos";
break;
case Pinetime::Applications::Apps::HeartRate:
screen_lbl = "HeartRate";
break;
case Pinetime::Applications::Apps::Navigation:
screen_lbl = "Navigation";
break;
case Pinetime::Applications::Apps::StopWatch:
screen_lbl = "StopWatch";
break;
case Pinetime::Applications::Apps::Metronome:
screen_lbl = "Metronome";
break;
case Pinetime::Applications::Apps::Motion:
screen_lbl = "Motion";
break;
case Pinetime::Applications::Apps::Steps:
screen_lbl = "Steps";
break;
case Pinetime::Applications::Apps::Weather:
screen_lbl = "Weather";
break;
case Pinetime::Applications::Apps::PassKey:
screen_lbl = "PassKey";
break;
case Pinetime::Applications::Apps::QuickSettings:
screen_lbl = "QuickSettings";
break;
case Pinetime::Applications::Apps::Settings:
screen_lbl = "Settings";
break;
case Pinetime::Applications::Apps::SettingWatchFace:
screen_lbl = "SettingWatchFace";
break;
case Pinetime::Applications::Apps::SettingTimeFormat:
screen_lbl = "SettingTimeFormat";
break;
case Pinetime::Applications::Apps::SettingDisplay:
screen_lbl = "SettingDisplay";
break;
case Pinetime::Applications::Apps::SettingWakeUp:
screen_lbl = "SettingWakeUp";
break;
case Pinetime::Applications::Apps::SettingSteps:
screen_lbl = "SettingSteps";
break;
case Pinetime::Applications::Apps::SettingSetDate:
screen_lbl = "SettingSetDate";
break;
case Pinetime::Applications::Apps::SettingSetTime:
screen_lbl = "SettingSetTime";
break;
case Pinetime::Applications::Apps::SettingChimes:
screen_lbl = "SettingChimes";
break;
case Pinetime::Applications::Apps::SettingShakeThreshold:
screen_lbl = "SettingThreshold";
break;
case Pinetime::Applications::Apps::Error:
screen_lbl = "Error";
break;
//case Pinetime::Applications::Apps::Weather:
// screen_lbl = "Weather";
// break;
default:
screen_lbl = "unkown screen";
}
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(label, screen_lbl);
lv_obj_align(label, nullptr, LV_ALIGN_CENTER, 0, -20);
}
Missing::~Missing() {
lv_obj_clean(lv_scr_act());
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "displayapp/screens/Screen.h"
#include "displayapp/Apps.h"
#include <lvgl/lvgl.h>
namespace Pinetime {
namespace Applications {
namespace Screens {
class Missing : public Screen {
public:
Missing(DisplayApp* app, Pinetime::Applications::Apps app_key);
~Missing() override;
};
}
}
}

134
sim/drivers/Bma421.cpp Normal file
View File

@@ -0,0 +1,134 @@
#include "drivers/Bma421.h"
#include <libraries/delay/nrf_delay.h>
#include <libraries/log/nrf_log.h>
#include "drivers/TwiMaster.h"
#include <drivers/Bma421_C/bma423.h>
using namespace Pinetime::Drivers;
namespace {
// int8_t user_i2c_read(uint8_t reg_addr, uint8_t* reg_data, uint32_t length, void* intf_ptr) {
// auto bma421 = static_cast<Bma421*>(intf_ptr);
// bma421->Read(reg_addr, reg_data, length);
// return 0;
// }
//
// int8_t user_i2c_write(uint8_t reg_addr, const uint8_t* reg_data, uint32_t length, void* intf_ptr) {
// auto bma421 = static_cast<Bma421*>(intf_ptr);
// bma421->Write(reg_addr, reg_data, length);
// return 0;
// }
//
// void user_delay(uint32_t period_us, void* intf_ptr) {
// nrf_delay_us(period_us);
// }
}
Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, deviceAddress {twiAddress} {
// bma.intf = BMA4_I2C_INTF;
// bma.bus_read = user_i2c_read;
// bma.bus_write = user_i2c_write;
// bma.variant = BMA42X_VARIANT;
// bma.intf_ptr = this;
// bma.delay_us = user_delay;
// bma.read_write_len = 16;
}
void Bma421::Init() {
if (not isResetOk)
return; // Call SoftReset (and reset TWI device) first!
// auto ret = bma423_init(&bma);
// if (ret != BMA4_OK)
// return;
switch(bma.chip_id) {
case BMA423_CHIP_ID: deviceType = DeviceTypes::BMA421; break;
case BMA425_CHIP_ID: deviceType = DeviceTypes::BMA425; break;
default: deviceType = DeviceTypes::Unknown; break;
}
// ret = bma423_write_config_file(&bma);
// if (ret != BMA4_OK)
// return;
//
// ret = bma4_set_interrupt_mode(BMA4_LATCH_MODE, &bma);
// if (ret != BMA4_OK)
// return;
//
// ret = bma423_feature_enable(BMA423_STEP_CNTR, 1, &bma);
// if (ret != BMA4_OK)
// return;
//
// ret = bma423_step_detector_enable(0, &bma);
// if (ret != BMA4_OK)
// return;
//
// ret = bma4_set_accel_enable(1, &bma);
// if (ret != BMA4_OK)
// return;
//
// struct bma4_accel_config accel_conf;
// accel_conf.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
// accel_conf.range = BMA4_ACCEL_RANGE_2G;
// accel_conf.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
// accel_conf.perf_mode = BMA4_CIC_AVG_MODE;
// ret = bma4_set_accel_config(&accel_conf, &bma);
// if (ret != BMA4_OK)
// return;
//
isOk = true;
}
void Bma421::Reset() {
uint8_t data = 0xb6;
twiMaster.Write(deviceAddress, 0x7E, &data, 1);
}
void Bma421::Read(uint8_t registerAddress, uint8_t* buffer, size_t size) {
twiMaster.Read(deviceAddress, registerAddress, buffer, size);
}
void Bma421::Write(uint8_t registerAddress, const uint8_t* data, size_t size) {
twiMaster.Write(deviceAddress, registerAddress, data, size);
}
Bma421::Values Bma421::Process() {
if (not isOk)
return {};
// struct bma4_accel data;
// bma4_read_accel_xyz(&data, &bma);
//
// uint32_t steps = 0;
// bma423_step_counter_output(&steps, &bma);
//
// int32_t temperature = 0;
// bma4_get_temperature(&temperature, BMA4_DEG, &bma);
// temperature = temperature / 1000;
//
// uint8_t activity = 0;
// bma423_activity_output(&activity, &bma);
//
// // X and Y axis are swapped because of the way the sensor is mounted in the PineTime
// return {steps, data.y, data.x, data.z};
return {steps, 0, 0, 0};
}
bool Bma421::IsOk() const {
return isOk;
}
void Bma421::ResetStepCounter() {
// bma423_reset_step_counter(&bma);
steps = 0;
}
void Bma421::SoftReset() {
// auto ret = bma4_soft_reset(&bma);
// if (ret == BMA4_OK) {
// isResetOk = true;
// nrf_delay_ms(1);
// }
}
Bma421::DeviceTypes Bma421::DeviceType() const {
return deviceType;
}

56
sim/drivers/Bma421.h Normal file
View File

@@ -0,0 +1,56 @@
#pragma once
#include <drivers/Bma421_C/bma4_defs.h>
#include <cstdint>
#include <cstddef>
#include <memory>
namespace Pinetime {
namespace Drivers {
class TwiMaster;
class Bma421 {
public:
enum class DeviceTypes : uint8_t {
Unknown,
BMA421,
BMA425
};
struct Values {
uint32_t steps;
int16_t x;
int16_t y;
int16_t z;
};
Bma421(TwiMaster& twiMaster, uint8_t twiAddress);
Bma421(const Bma421&) = delete;
Bma421& operator=(const Bma421&) = delete;
Bma421(Bma421&&) = delete;
Bma421& operator=(Bma421&&) = delete;
/// The chip freezes the TWI bus after the softreset operation. Softreset is separated from the
/// Init() method to allow the caller to uninit and then reinit the TWI device after the softreset.
void SoftReset();
void Init();
Values Process();
void ResetStepCounter();
void Read(uint8_t registerAddress, uint8_t* buffer, size_t size);
void Write(uint8_t registerAddress, const uint8_t* data, size_t size);
bool IsOk() const;
DeviceTypes DeviceType() const;
// lv_sim: returned by Process(), public to be modified by main.cpp
uint32_t steps = 0;
private:
void Reset();
TwiMaster& twiMaster;
uint8_t deviceAddress = 0x18;
bma4_dev bma;
bool isOk = true;
bool isResetOk = false;
DeviceTypes deviceType = DeviceTypes::Unknown;
};
}
}

112
sim/drivers/Cst816s.cpp Normal file
View File

@@ -0,0 +1,112 @@
#include "drivers/Cst816s.h"
#include <SDL2/SDL.h>
#include <libraries/log/nrf_log.h>
#include <cmath>
using namespace Pinetime::Drivers;
/* References :
* This implementation is based on this article :
* https://medium.com/@ly.lee/building-a-rust-driver-for-pinetimes-touch-controller-cbc1a5d5d3e9 Touch panel datasheet (weird chinese
* translation) : https://wiki.pine64.org/images/5/51/CST816S%E6%95%B0%E6%8D%AE%E6%89%8B%E5%86%8CV1.1.en.pdf
*
* TODO : we need a complete datasheet and protocol reference!
* */
//Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} {
//}
Cst816S::Cst816S() {
}
bool Cst816S::Init() {
return true;
}
Cst816S::TouchInfos Cst816S::GetTouchInfo() {
int x, y;
uint32_t buttons = SDL_GetMouseState(&x, &y);
Cst816S::TouchInfos info;
info.x = x;
info.y = y;
info.touching = (buttons & SDL_BUTTON_LMASK) != 0;
//info.gesture = gesture;
info.isValid = x > 0 && x <= maxX && y > 0 && y <= maxY;
if(info.isValid) {
if(!is_pressed && info.touching) {
// start klick
pressed_since = std::chrono::steady_clock::now();
is_pressed = true;
is_long_press = false;
is_swipe = false;
is_stationary = true;
x_start = info.x;
y_start = info.y;
} else if(is_pressed && info.touching) {
// is it long press?
if (is_stationary) { // check if while touching we moved away from the start coordinates
double x_diff = static_cast<double>(info.x) - x_start;
double y_diff = static_cast<double>(info.y) - y_start;
double norm = hypot(x_diff, y_diff);
if(norm > 20) { // we moved out of start area
is_stationary = false;
}
}
if (!is_long_press && !is_swipe) { // check for long-press only if it's not yet a long-press and didn't move
std::chrono::duration<double> press_duration = std::chrono::steady_clock::now() - pressed_since;
if(is_stationary && press_duration.count() > 1.0) {
// longer than 1 second pressed, then it is long-press
is_long_press = true;
info.gesture = Gestures::LongPress;
} else if(!is_stationary) {
// moved mouse fast enough to not be a long-press
is_swipe = true;
double x_diff = static_cast<double>(info.x) - x_start;
double y_diff = static_cast<double>(info.y) - y_start;
if (fabs(x_diff) > fabs(y_diff)) {
// x-swipe
if (x_diff < 0) {
info.gesture = Gestures::SlideLeft;
} else {
info.gesture = Gestures::SlideRight;
}
} else {
// y-swipe
if (y_diff < 0) {
info.gesture = Gestures::SlideUp;
} else {
info.gesture = Gestures::SlideDown;
}
}
}
}
} else if(is_pressed && !info.touching) {
// end klick
is_pressed = false;
double x_diff = static_cast<double>(info.x) - x_start;
double y_diff = static_cast<double>(info.y) - y_start;
double norm = hypot(x_diff, y_diff);
if(norm < 20) {
if(is_stationary && !is_long_press && !is_swipe) {
// no swipe with less than 5 pixel mouse movement
info.gesture = Gestures::SingleTap;
}
}
}
}
return info;
}
void Cst816S::Sleep() {
NRF_LOG_INFO("[TOUCHPANEL] Sleep");
}
void Cst816S::Wakeup() {
Init();
NRF_LOG_INFO("[TOUCHPANEL] Wakeup");
}
bool Cst816S::CheckDeviceIds() {
return chipId == 0xb4 && vendorId == 0 && fwVersion == 1;
}

88
sim/drivers/Cst816s.h Normal file
View File

@@ -0,0 +1,88 @@
#pragma once
//#include "drivers/TwiMaster.h"
#include <chrono>
#include <cstdint>
namespace Pinetime {
namespace Drivers {
class Cst816S {
public:
enum class Gestures : uint8_t {
None = 0x00,
SlideDown = 0x01,
SlideUp = 0x02,
SlideLeft = 0x03,
SlideRight = 0x04,
SingleTap = 0x05,
DoubleTap = 0x0B,
LongPress = 0x0C
};
struct TouchInfos {
uint16_t x = 0;
uint16_t y = 0;
Gestures gesture = Gestures::None;
bool touching = false;
bool isValid = false;
};
Cst816S();
// Cst816S(TwiMaster& twiMaster, uint8_t twiAddress);
Cst816S(const Cst816S&) = delete;
Cst816S& operator=(const Cst816S&) = delete;
Cst816S(Cst816S&&) = delete;
Cst816S& operator=(Cst816S&&) = delete;
bool Init();
TouchInfos GetTouchInfo();
void Sleep();
void Wakeup();
uint8_t GetChipId() const {
return chipId;
}
uint8_t GetVendorId() const {
return vendorId;
}
uint8_t GetFwVersion() const {
return fwVersion;
}
private:
bool CheckDeviceIds();
// Unused/Unavailable commented out
static constexpr uint8_t gestureIndex = 1;
static constexpr uint8_t touchPointNumIndex = 2;
//static constexpr uint8_t touchEventIndex = 3;
static constexpr uint8_t touchXHighIndex = 3;
static constexpr uint8_t touchXLowIndex = 4;
//static constexpr uint8_t touchIdIndex = 5;
static constexpr uint8_t touchYHighIndex = 5;
static constexpr uint8_t touchYLowIndex = 6;
//static constexpr uint8_t touchStep = 6;
//static constexpr uint8_t touchXYIndex = 7;
//static constexpr uint8_t touchMiscIndex = 8;
static constexpr uint8_t maxX = 240;
static constexpr uint8_t maxY = 240;
// TwiMaster& twiMaster;
// uint8_t twiAddress;
const uint8_t chipId = 0xb4;
const uint8_t vendorId = 0;
const uint8_t fwVersion = 1;
// simulation members for swipe detection from mouse
std::chrono::time_point<std::chrono::steady_clock> pressed_since;
bool is_pressed = false;
bool is_long_press = false;
bool is_stationary = true;
bool is_swipe = false;
uint8_t x_start;
uint8_t y_start;
};
}
}

105
sim/drivers/Hrs3300.cpp Normal file
View File

@@ -0,0 +1,105 @@
/*
SPDX-License-Identifier: LGPL-3.0-or-later
Original work Copyright (C) 2020 Daniel Thompson
C++ port Copyright (C) 2021 Jean-François Milants
*/
#include "drivers/Hrs3300.h"
#include <algorithm>
#include <nrf_gpio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <nrf_log.h>
using namespace Pinetime::Drivers;
/** Driver for the HRS3300 heart rate sensor.
* Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/drivers/hrs3300.py
*/
Hrs3300::Hrs3300(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} {
}
void Hrs3300::Init() {
nrf_gpio_cfg_input(30, NRF_GPIO_PIN_NOPULL);
Disable();
// vTaskDelay(100);
// HRS disabled, 12.5 ms wait time between cycles, (partly) 20mA drive
WriteRegister(static_cast<uint8_t>(Registers::Enable), 0x60);
// (partly) 20mA drive, power on, "magic" (datasheet says both
// "reserved" and "set low nibble to 8" but 0xe gives better results
// and is used by at least two other HRS3300 drivers
WriteRegister(static_cast<uint8_t>(Registers::PDriver), 0x6E);
// HRS and ALS both in 16-bit mode
WriteRegister(static_cast<uint8_t>(Registers::Res), 0x88);
// 64x gain
WriteRegister(static_cast<uint8_t>(Registers::Hgain), 0x10);
}
void Hrs3300::Enable() {
NRF_LOG_INFO("ENABLE");
auto value = ReadRegister(static_cast<uint8_t>(Registers::Enable));
value |= 0x80;
WriteRegister(static_cast<uint8_t>(Registers::Enable), value);
}
void Hrs3300::Disable() {
NRF_LOG_INFO("DISABLE");
auto value = ReadRegister(static_cast<uint8_t>(Registers::Enable));
value &= ~0x80;
WriteRegister(static_cast<uint8_t>(Registers::Enable), value);
}
uint16_t Hrs3300::ReadHrs() {
auto m = ReadRegister(static_cast<uint8_t>(Registers::C0DataM));
auto h = ReadRegister(static_cast<uint8_t>(Registers::C0DataH));
auto l = ReadRegister(static_cast<uint8_t>(Registers::C0dataL));
return (m << 8) | ((h & 0x0f) << 4) | (l & 0x0f) | ((l & 0x30) << 12);
}
uint16_t Hrs3300::ReadAls() {
auto m = ReadRegister(static_cast<uint8_t>(Registers::C1dataM));
auto h = ReadRegister(static_cast<uint8_t>(Registers::C1dataH));
auto l = ReadRegister(static_cast<uint8_t>(Registers::C1dataL));
return (m << 3) | ((h & 0x3f) << 11) | (l & 0x07);
}
void Hrs3300::SetGain(uint8_t gain) {
constexpr uint8_t maxGain = 64U;
gain = std::min(gain, maxGain);
uint8_t hgain = 0;
while ((1 << hgain) < gain) {
++hgain;
}
WriteRegister(static_cast<uint8_t>(Registers::Hgain), hgain << 2);
}
void Hrs3300::SetDrive(uint8_t drive) {
auto en = ReadRegister(static_cast<uint8_t>(Registers::Enable));
auto pd = ReadRegister(static_cast<uint8_t>(Registers::PDriver));
en = (en & 0xf7) | ((drive & 2) << 2);
pd = (pd & 0xbf) | ((drive & 1) << 6);
WriteRegister(static_cast<uint8_t>(Registers::Enable), en);
WriteRegister(static_cast<uint8_t>(Registers::PDriver), pd);
}
void Hrs3300::WriteRegister(uint8_t reg, uint8_t data) {
auto ret = twiMaster.Write(twiAddress, reg, &data, 1);
if (ret != TwiMaster::ErrorCodes::NoError)
NRF_LOG_INFO("WRITE ERROR");
}
uint8_t Hrs3300::ReadRegister(uint8_t reg) {
uint8_t value;
auto ret = twiMaster.Read(twiAddress, reg, &value, 1);
if (ret != TwiMaster::ErrorCodes::NoError)
NRF_LOG_INFO("READ ERROR");
return value;
}

46
sim/drivers/Hrs3300.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
#include "drivers/TwiMaster.h"
namespace Pinetime {
namespace Drivers {
class Hrs3300 {
public:
enum class Registers : uint8_t {
Id = 0x00,
Enable = 0x01,
EnableHen = 0x80,
C1dataM = 0x08,
C0DataM = 0x09,
C0DataH = 0x0a,
PDriver = 0x0c,
C1dataH = 0x0d,
C1dataL = 0x0e,
C0dataL = 0x0f,
Res = 0x16,
Hgain = 0x17
};
Hrs3300(TwiMaster& twiMaster, uint8_t twiAddress);
Hrs3300(const Hrs3300&) = delete;
Hrs3300& operator=(const Hrs3300&) = delete;
Hrs3300(Hrs3300&&) = delete;
Hrs3300& operator=(Hrs3300&&) = delete;
void Init();
void Enable();
void Disable();
uint16_t ReadHrs();
uint16_t ReadAls();
void SetGain(uint8_t gain);
void SetDrive(uint8_t drive);
private:
TwiMaster& twiMaster;
uint8_t twiAddress;
void WriteRegister(uint8_t reg, uint8_t data);
uint8_t ReadRegister(uint8_t reg);
};
}
}

296
sim/drivers/SpiMaster.cpp Normal file
View File

@@ -0,0 +1,296 @@
#include "drivers/SpiMaster.h"
//#include <hal/nrf_gpio.h>
//#include <hal/nrf_spim.h>
#include <nrfx_log.h>
#include <algorithm>
using namespace Pinetime::Drivers;
SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters& params) : spi {spi}, params {params} {
}
bool SpiMaster::Init() {
// if(mutex == nullptr) {
// mutex = xSemaphoreCreateBinary();
// ASSERT(mutex != nullptr);
// }
//
// /* Configure GPIO pins used for pselsck, pselmosi, pselmiso and pselss for SPI0 */
// nrf_gpio_pin_set(params.pinSCK);
// nrf_gpio_cfg_output(params.pinSCK);
// nrf_gpio_pin_clear(params.pinMOSI);
// nrf_gpio_cfg_output(params.pinMOSI);
// nrf_gpio_cfg_input(params.pinMISO, NRF_GPIO_PIN_NOPULL);
// // nrf_gpio_cfg_output(params.pinCSN);
// // pinCsn = params.pinCSN;
//
// switch (spi) {
// case SpiModule::SPI0:
// spiBaseAddress = NRF_SPIM0;
// break;
// case SpiModule::SPI1:
// spiBaseAddress = NRF_SPIM1;
// break;
// default:
// return false;
// }
//
// /* Configure pins, frequency and mode */
// spiBaseAddress->PSELSCK = params.pinSCK;
// spiBaseAddress->PSELMOSI = params.pinMOSI;
// spiBaseAddress->PSELMISO = params.pinMISO;
//
// uint32_t frequency;
// switch (params.Frequency) {
// case Frequencies::Freq8Mhz:
// frequency = 0x80000000;
// break;
// default:
// return false;
// }
// spiBaseAddress->FREQUENCY = frequency;
//
// uint32_t regConfig = 0;
// switch (params.bitOrder) {
// case BitOrder::Msb_Lsb:
// break;
// case BitOrder::Lsb_Msb:
// regConfig = 1;
// break;
// default:
// return false;
// }
// switch (params.mode) {
// case Modes::Mode0:
// break;
// case Modes::Mode1:
// regConfig |= (0x01 << 1);
// break;
// case Modes::Mode2:
// regConfig |= (0x02 << 1);
// break;
// case Modes::Mode3:
// regConfig |= (0x03 << 1);
// break;
// default:
// return false;
// }
//
// spiBaseAddress->CONFIG = regConfig;
// spiBaseAddress->EVENTS_ENDRX = 0;
// spiBaseAddress->EVENTS_ENDTX = 0;
// spiBaseAddress->EVENTS_END = 0;
//
// spiBaseAddress->INTENSET = ((unsigned) 1 << (unsigned) 6);
// spiBaseAddress->INTENSET = ((unsigned) 1 << (unsigned) 1);
// spiBaseAddress->INTENSET = ((unsigned) 1 << (unsigned) 19);
//
// spiBaseAddress->ENABLE = (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos);
//
// NRFX_IRQ_PRIORITY_SET(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn, 2);
// NRFX_IRQ_ENABLE(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn);
//
// xSemaphoreGive(mutex);
return true;
}
//void SpiMaster::SetupWorkaroundForFtpan58(NRF_SPIM_Type* spim, uint32_t ppi_channel, uint32_t gpiote_channel) {
// // Create an event when SCK toggles.
// NRF_GPIOTE->CONFIG[gpiote_channel] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) | (spim->PSEL.SCK << GPIOTE_CONFIG_PSEL_Pos) |
// (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
//
// // Stop the spim instance when SCK toggles.
// NRF_PPI->CH[ppi_channel].EEP = (uint32_t) &NRF_GPIOTE->EVENTS_IN[gpiote_channel];
// NRF_PPI->CH[ppi_channel].TEP = (uint32_t) &spim->TASKS_STOP;
// NRF_PPI->CHENSET = 1U << ppi_channel;
// spiBaseAddress->EVENTS_END = 0;
//
// // Disable IRQ
// spim->INTENCLR = (1 << 6);
// spim->INTENCLR = (1 << 1);
// spim->INTENCLR = (1 << 19);
//}
//void SpiMaster::DisableWorkaroundForFtpan58(NRF_SPIM_Type* spim, uint32_t ppi_channel, uint32_t gpiote_channel) {
// NRF_GPIOTE->CONFIG[gpiote_channel] = 0;
// NRF_PPI->CH[ppi_channel].EEP = 0;
// NRF_PPI->CH[ppi_channel].TEP = 0;
// NRF_PPI->CHENSET = ppi_channel;
// spiBaseAddress->EVENTS_END = 0;
// spim->INTENSET = (1 << 6);
// spim->INTENSET = (1 << 1);
// spim->INTENSET = (1 << 19);
//}
void SpiMaster::OnEndEvent() {
if (currentBufferAddr == 0) {
return;
}
// auto s = currentBufferSize;
// if (s > 0) {
// auto currentSize = std::min((size_t) 255, s);
// PrepareTx(currentBufferAddr, currentSize);
// currentBufferAddr += currentSize;
// currentBufferSize -= currentSize;
//
// spiBaseAddress->TASKS_START = 1;
// } else {
// BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// if (taskToNotify != nullptr) {
// vTaskNotifyGiveFromISR(taskToNotify, &xHigherPriorityTaskWoken);
// portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
// }
//
// nrf_gpio_pin_set(this->pinCsn);
// currentBufferAddr = 0;
// BaseType_t xHigherPriorityTaskWoken2 = pdFALSE;
// xSemaphoreGiveFromISR(mutex, &xHigherPriorityTaskWoken2);
// portYIELD_FROM_ISR(xHigherPriorityTaskWoken | xHigherPriorityTaskWoken2);
// }
}
void SpiMaster::OnStartedEvent() {
}
//void SpiMaster::PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size) {
// spiBaseAddress->TXD.PTR = bufferAddress;
// spiBaseAddress->TXD.MAXCNT = size;
// spiBaseAddress->TXD.LIST = 0;
// spiBaseAddress->RXD.PTR = 0;
// spiBaseAddress->RXD.MAXCNT = 0;
// spiBaseAddress->RXD.LIST = 0;
// spiBaseAddress->EVENTS_END = 0;
//}
//void SpiMaster::PrepareRx(const volatile uint32_t cmdAddress,
// const volatile size_t cmdSize,
// const volatile uint32_t bufferAddress,
// const volatile size_t size) {
// spiBaseAddress->TXD.PTR = 0;
// spiBaseAddress->TXD.MAXCNT = 0;
// spiBaseAddress->TXD.LIST = 0;
// spiBaseAddress->RXD.PTR = bufferAddress;
// spiBaseAddress->RXD.MAXCNT = size;
// spiBaseAddress->RXD.LIST = 0;
// spiBaseAddress->EVENTS_END = 0;
//}
bool SpiMaster::Write(uint8_t pinCsn, const uint8_t* data, size_t size) {
// if (data == nullptr)
// return false;
// auto ok = xSemaphoreTake(mutex, portMAX_DELAY);
// ASSERT(ok == true);
// taskToNotify = xTaskGetCurrentTaskHandle();
//
// this->pinCsn = pinCsn;
//
// if (size == 1) {
// SetupWorkaroundForFtpan58(spiBaseAddress, 0, 0);
// } else {
// DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
// }
//
// nrf_gpio_pin_clear(this->pinCsn);
//
// currentBufferAddr = (uint32_t) data;
// currentBufferSize = size;
//
// auto currentSize = std::min((size_t) 255, (size_t) currentBufferSize);
// PrepareTx(currentBufferAddr, currentSize);
// currentBufferSize -= currentSize;
// currentBufferAddr += currentSize;
// spiBaseAddress->TASKS_START = 1;
//
// if (size == 1) {
// while (spiBaseAddress->EVENTS_END == 0)
// ;
// nrf_gpio_pin_set(this->pinCsn);
// currentBufferAddr = 0;
// xSemaphoreGive(mutex);
// }
return true;
}
bool SpiMaster::Read(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t* data, size_t dataSize) {
// xSemaphoreTake(mutex, portMAX_DELAY);
//
// taskToNotify = nullptr;
//
// this->pinCsn = pinCsn;
// DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
// spiBaseAddress->INTENCLR = (1 << 6);
// spiBaseAddress->INTENCLR = (1 << 1);
// spiBaseAddress->INTENCLR = (1 << 19);
//
// nrf_gpio_pin_clear(this->pinCsn);
//
// currentBufferAddr = 0;
// currentBufferSize = 0;
//
// PrepareTx((uint32_t) cmd, cmdSize);
// spiBaseAddress->TASKS_START = 1;
// while (spiBaseAddress->EVENTS_END == 0)
// ;
//
// PrepareRx((uint32_t) cmd, cmdSize, (uint32_t) data, dataSize);
// spiBaseAddress->TASKS_START = 1;
//
// while (spiBaseAddress->EVENTS_END == 0)
// ;
// nrf_gpio_pin_set(this->pinCsn);
//
// xSemaphoreGive(mutex);
return true;
}
void SpiMaster::Sleep() {
// while (spiBaseAddress->ENABLE != 0) {
// spiBaseAddress->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos);
// }
// nrf_gpio_cfg_default(params.pinSCK);
// nrf_gpio_cfg_default(params.pinMOSI);
// nrf_gpio_cfg_default(params.pinMISO);
NRF_LOG_INFO("[SPIMASTER] sleep");
}
void SpiMaster::Wakeup() {
Init();
NRF_LOG_INFO("[SPIMASTER] Wakeup");
}
bool SpiMaster::WriteCmdAndBuffer(uint8_t pinCsn, const uint8_t* cmd, size_t cmdSize, const uint8_t* data, size_t dataSize) {
// xSemaphoreTake(mutex, portMAX_DELAY);
//
// taskToNotify = nullptr;
//
// this->pinCsn = pinCsn;
// DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
// spiBaseAddress->INTENCLR = (1 << 6);
// spiBaseAddress->INTENCLR = (1 << 1);
// spiBaseAddress->INTENCLR = (1 << 19);
//
// nrf_gpio_pin_clear(this->pinCsn);
//
// currentBufferAddr = 0;
// currentBufferSize = 0;
//
// PrepareTx((uint32_t) cmd, cmdSize);
// spiBaseAddress->TASKS_START = 1;
// while (spiBaseAddress->EVENTS_END == 0)
// ;
//
// PrepareTx((uint32_t) data, dataSize);
// spiBaseAddress->TASKS_START = 1;
//
// while (spiBaseAddress->EVENTS_END == 0)
// ;
// nrf_gpio_pin_set(this->pinCsn);
//
// xSemaphoreGive(mutex);
return true;
}

65
sim/drivers/SpiMaster.h Normal file
View File

@@ -0,0 +1,65 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <FreeRTOS.h>
//#include <semphr.h>
//#include <task.h>
namespace Pinetime {
namespace Drivers {
class SpiMaster {
public:
enum class SpiModule : uint8_t { SPI0, SPI1 };
enum class BitOrder : uint8_t { Msb_Lsb, Lsb_Msb };
enum class Modes : uint8_t { Mode0, Mode1, Mode2, Mode3 };
enum class Frequencies : uint8_t { Freq8Mhz };
struct Parameters {
BitOrder bitOrder;
Modes mode;
Frequencies Frequency;
uint8_t pinSCK;
uint8_t pinMOSI;
uint8_t pinMISO;
};
SpiMaster(const SpiModule spi, const Parameters& params);
SpiMaster(const SpiMaster&) = delete;
SpiMaster& operator=(const SpiMaster&) = delete;
SpiMaster(SpiMaster&&) = delete;
SpiMaster& operator=(SpiMaster&&) = delete;
bool Init();
bool Write(uint8_t pinCsn, const uint8_t* data, size_t size);
bool Read(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t* data, size_t dataSize);
bool WriteCmdAndBuffer(uint8_t pinCsn, const uint8_t* cmd, size_t cmdSize, const uint8_t* data, size_t dataSize);
void OnStartedEvent();
void OnEndEvent();
void Sleep();
void Wakeup();
private:
// void SetupWorkaroundForFtpan58(NRF_SPIM_Type* spim, uint32_t ppi_channel, uint32_t gpiote_channel);
// void DisableWorkaroundForFtpan58(NRF_SPIM_Type* spim, uint32_t ppi_channel, uint32_t gpiote_channel);
// void PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size);
// void PrepareRx(const volatile uint32_t cmdAddress,
// const volatile size_t cmdSize,
// const volatile uint32_t bufferAddress,
// const volatile size_t size);
// NRF_SPIM_Type* spiBaseAddress;
uint8_t pinCsn;
SpiMaster::SpiModule spi;
SpiMaster::Parameters params;
volatile uint32_t currentBufferAddr = 0;
volatile size_t currentBufferSize = 0;
// volatile TaskHandle_t taskToNotify;
// SemaphoreHandle_t mutex = nullptr;
};
}
}

144
sim/drivers/SpiNorFlash.cpp Normal file
View File

@@ -0,0 +1,144 @@
#include "drivers/SpiNorFlash.h"
#include <hal/nrf_gpio.h>
#include <libraries/delay/nrf_delay.h>
#include <libraries/log/nrf_log.h>
#include "drivers/Spi.h"
using namespace Pinetime::Drivers;
SpiNorFlash::SpiNorFlash(Spi& spi) : spi {spi} {
}
void SpiNorFlash::Init() {
device_id = ReadIdentificaion();
NRF_LOG_INFO(
"[SpiNorFlash] Manufacturer : %d, Memory type : %d, memory density : %d", device_id.manufacturer, device_id.type, device_id.density);
}
void SpiNorFlash::Uninit() {
}
void SpiNorFlash::Sleep() {
auto cmd = static_cast<uint8_t>(Commands::DeepPowerDown);
spi.Write(&cmd, sizeof(uint8_t));
NRF_LOG_INFO("[SpiNorFlash] Sleep")
}
void SpiNorFlash::Wakeup() {
// send Commands::ReleaseFromDeepPowerDown then 3 dummy bytes before reading Device ID
// static constexpr uint8_t cmdSize = 4;
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::ReleaseFromDeepPowerDown), 0x01, 0x02, 0x03};
// uint8_t id = 0;
// spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, &id, 1);
// auto devId = device_id = ReadIdentificaion();
// if (devId.type != device_id.type) {
// NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: Failed");
// } else {
// NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: %d", id);
// }
NRF_LOG_INFO("[SpiNorFlash] Wakeup")
}
SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() {
// auto cmd = static_cast<uint8_t>(Commands::ReadIdentification);
// Identification identification;
// spi.Read(&cmd, 1, reinterpret_cast<uint8_t*>(&identification), sizeof(Identification));
// return identification;
return {};
}
uint8_t SpiNorFlash::ReadStatusRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadStatusRegister);
uint8_t status;
spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t));
return status;
}
bool SpiNorFlash::WriteInProgress() {
// return (ReadStatusRegister() & 0x01u) == 0x01u;
return false;
}
bool SpiNorFlash::WriteEnabled() {
// return (ReadStatusRegister() & 0x02u) == 0x02u;
return false;
}
uint8_t SpiNorFlash::ReadConfigurationRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadConfigurationRegister);
uint8_t status;
spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t));
return status;
}
void SpiNorFlash::Read(uint32_t address, uint8_t* buffer, size_t size) {
static constexpr uint8_t cmdSize = 4;
uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::Read), (uint8_t) (address >> 16U), (uint8_t) (address >> 8U), (uint8_t) address};
spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, buffer, size);
}
void SpiNorFlash::WriteEnable() {
auto cmd = static_cast<uint8_t>(Commands::WriteEnable);
spi.Read(&cmd, sizeof(cmd), nullptr, 0);
}
void SpiNorFlash::SectorErase(uint32_t sectorAddress) {
// static constexpr uint8_t cmdSize = 4;
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::SectorErase),
// (uint8_t) (sectorAddress >> 16U),
// (uint8_t) (sectorAddress >> 8U),
// (uint8_t) sectorAddress};
//
// WriteEnable();
// while (!WriteEnabled())
// vTaskDelay(1);
//
// spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, nullptr, 0);
//
// while (WriteInProgress())
// vTaskDelay(1);
}
uint8_t SpiNorFlash::ReadSecurityRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadSecurityRegister);
uint8_t status;
spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t));
return status;
}
bool SpiNorFlash::ProgramFailed() {
// return (ReadSecurityRegister() & 0x20u) == 0x20u;
return false;
}
bool SpiNorFlash::EraseFailed() {
// return (ReadSecurityRegister() & 0x40u) == 0x40u;
return false;
}
void SpiNorFlash::Write(uint32_t address, const uint8_t* buffer, size_t size) {
// static constexpr uint8_t cmdSize = 4;
//
// size_t len = size;
// uint32_t addr = address;
// const uint8_t* b = buffer;
// while (len > 0) {
// uint32_t pageLimit = (addr & ~(pageSize - 1u)) + pageSize;
// uint32_t toWrite = pageLimit - addr > len ? len : pageLimit - addr;
//
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::PageProgram), (uint8_t) (addr >> 16U), (uint8_t) (addr >> 8U), (uint8_t) addr};
//
// WriteEnable();
// while (!WriteEnabled())
// vTaskDelay(1);
//
// spi.WriteCmdAndBuffer(cmd, cmdSize, b, toWrite);
//
// while (WriteInProgress())
// vTaskDelay(1);
//
// addr += toWrite;
// b += toWrite;
// len -= toWrite;
// }
}

60
sim/drivers/SpiNorFlash.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
#include <cstddef>
#include <cstdint>
namespace Pinetime {
namespace Drivers {
class Spi;
class SpiNorFlash {
public:
explicit SpiNorFlash(Spi& spi);
SpiNorFlash(const SpiNorFlash&) = delete;
SpiNorFlash& operator=(const SpiNorFlash&) = delete;
SpiNorFlash(SpiNorFlash&&) = delete;
SpiNorFlash& operator=(SpiNorFlash&&) = delete;
typedef struct __attribute__((packed)) {
uint8_t manufacturer = 0;
uint8_t type = 0;
uint8_t density = 0;
} Identification;
Identification ReadIdentificaion();
uint8_t ReadStatusRegister();
bool WriteInProgress();
bool WriteEnabled();
uint8_t ReadConfigurationRegister();
void Read(uint32_t address, uint8_t* buffer, size_t size);
void Write(uint32_t address, const uint8_t* buffer, size_t size);
void WriteEnable();
void SectorErase(uint32_t sectorAddress);
uint8_t ReadSecurityRegister();
bool ProgramFailed();
bool EraseFailed();
void Init();
void Uninit();
void Sleep();
void Wakeup();
private:
enum class Commands : uint8_t {
PageProgram = 0x02,
Read = 0x03,
ReadStatusRegister = 0x05,
WriteEnable = 0x06,
ReadConfigurationRegister = 0x15,
SectorErase = 0x20,
ReadSecurityRegister = 0x2B,
ReadIdentification = 0x9F,
ReleaseFromDeepPowerDown = 0xAB,
DeepPowerDown = 0xB9
};
static constexpr uint16_t pageSize = 256;
Spi& spi;
Identification device_id;
};
}
}

187
sim/drivers/TwiMaster.cpp Normal file
View File

@@ -0,0 +1,187 @@
#include "drivers/TwiMaster.h"
#include <cstring>
#include <hal/nrf_gpio.h>
#include <nrfx_log.h>
using namespace Pinetime::Drivers;
// TODO use shortcut to automatically send STOP when receive LastTX, for example
// TODO use DMA/IRQ
TwiMaster::TwiMaster(NRF_TWIM_Type* module, uint32_t frequency, uint8_t pinSda, uint8_t pinScl)
: module {module}, frequency {frequency}, pinSda {pinSda}, pinScl {pinScl} {
}
//void TwiMaster::ConfigurePins() const {
// NRF_GPIO->PIN_CNF[pinScl] =
// (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
// (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
// (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
// (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
// (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
//
// NRF_GPIO->PIN_CNF[pinSda] =
// (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
// (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
// (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
// (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
// (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
//}
void TwiMaster::Init() {
// if (mutex == nullptr) {
// mutex = xSemaphoreCreateBinary();
// }
//
// ConfigurePins();
//
// twiBaseAddress = module;
//
// twiBaseAddress->FREQUENCY = frequency;
//
// twiBaseAddress->PSEL.SCL = pinScl;
// twiBaseAddress->PSEL.SDA = pinSda;
// twiBaseAddress->EVENTS_LASTRX = 0;
// twiBaseAddress->EVENTS_STOPPED = 0;
// twiBaseAddress->EVENTS_LASTTX = 0;
// twiBaseAddress->EVENTS_ERROR = 0;
// twiBaseAddress->EVENTS_RXSTARTED = 0;
// twiBaseAddress->EVENTS_SUSPENDED = 0;
// twiBaseAddress->EVENTS_TXSTARTED = 0;
//
// twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos);
//
// xSemaphoreGive(mutex);
}
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* data, size_t size) {
// xSemaphoreTake(mutex, portMAX_DELAY);
// Wakeup();
// auto ret = Write(deviceAddress, &registerAddress, 1, false);
// ret = Read(deviceAddress, data, size, true);
// Sleep();
// xSemaphoreGive(mutex);
// return ret;
return TwiMaster::ErrorCodes::NoError;
}
TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size) {
// ASSERT(size <= maxDataSize);
// xSemaphoreTake(mutex, portMAX_DELAY);
// Wakeup();
// internalBuffer[0] = registerAddress;
// std::memcpy(internalBuffer + 1, data, size);
// auto ret = Write(deviceAddress, internalBuffer, size + 1, true);
// Sleep();
// xSemaphoreGive(mutex);
// return ret;
return TwiMaster::ErrorCodes::NoError;
}
//TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop) {
// twiBaseAddress->ADDRESS = deviceAddress;
// twiBaseAddress->TASKS_RESUME = 0x1UL;
// twiBaseAddress->RXD.PTR = (uint32_t) buffer;
// twiBaseAddress->RXD.MAXCNT = size;
//
// twiBaseAddress->TASKS_STARTRX = 1;
//
// while (!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR)
// ;
// twiBaseAddress->EVENTS_RXSTARTED = 0x0UL;
//
// txStartedCycleCount = DWT->CYCCNT;
// uint32_t currentCycleCount;
// while (!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR) {
// currentCycleCount = DWT->CYCCNT;
// if ((currentCycleCount - txStartedCycleCount) > HwFreezedDelay) {
// FixHwFreezed();
// return ErrorCodes::TransactionFailed;
// }
// }
// twiBaseAddress->EVENTS_LASTRX = 0x0UL;
//
// if (stop || twiBaseAddress->EVENTS_ERROR) {
// twiBaseAddress->TASKS_STOP = 0x1UL;
// while (!twiBaseAddress->EVENTS_STOPPED)
// ;
// twiBaseAddress->EVENTS_STOPPED = 0x0UL;
// } else {
// twiBaseAddress->TASKS_SUSPEND = 0x1UL;
// while (!twiBaseAddress->EVENTS_SUSPENDED)
// ;
// twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
// }
//
// if (twiBaseAddress->EVENTS_ERROR) {
// twiBaseAddress->EVENTS_ERROR = 0x0UL;
// }
// return ErrorCodes::NoError;
//}
//TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop) {
// twiBaseAddress->ADDRESS = deviceAddress;
// twiBaseAddress->TASKS_RESUME = 0x1UL;
// twiBaseAddress->TXD.PTR = (uint32_t) data;
// twiBaseAddress->TXD.MAXCNT = size;
//
// twiBaseAddress->TASKS_STARTTX = 1;
//
// while (!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR)
// ;
// twiBaseAddress->EVENTS_TXSTARTED = 0x0UL;
//
// txStartedCycleCount = DWT->CYCCNT;
// uint32_t currentCycleCount;
// while (!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR) {
// currentCycleCount = DWT->CYCCNT;
// if ((currentCycleCount - txStartedCycleCount) > HwFreezedDelay) {
// FixHwFreezed();
// return ErrorCodes::TransactionFailed;
// }
// }
// twiBaseAddress->EVENTS_LASTTX = 0x0UL;
//
// if (stop || twiBaseAddress->EVENTS_ERROR) {
// twiBaseAddress->TASKS_STOP = 0x1UL;
// while (!twiBaseAddress->EVENTS_STOPPED)
// ;
// twiBaseAddress->EVENTS_STOPPED = 0x0UL;
// } else {
// twiBaseAddress->TASKS_SUSPEND = 0x1UL;
// while (!twiBaseAddress->EVENTS_SUSPENDED)
// ;
// twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
// }
//
// if (twiBaseAddress->EVENTS_ERROR) {
// twiBaseAddress->EVENTS_ERROR = 0x0UL;
// uint32_t error = twiBaseAddress->ERRORSRC;
// twiBaseAddress->ERRORSRC = error;
// }
//
// return ErrorCodes::NoError;
//}
void TwiMaster::Sleep() {
// twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos);
}
void TwiMaster::Wakeup() {
// twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos);
}
/* Sometimes, the TWIM device just freeze and never set the event EVENTS_LASTTX.
* This method disable and re-enable the peripheral so that it works again.
* This is just a workaround, and it would be better if we could find a way to prevent
* this issue from happening.
* */
//void TwiMaster::FixHwFreezed() {
// NRF_LOG_INFO("I2C device frozen, reinitializing it!");
//
// uint32_t twi_state = NRF_TWI1->ENABLE;
//
// Sleep();
//
// twiBaseAddress->ENABLE = twi_state;
//}

41
sim/drivers/TwiMaster.h Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#include <FreeRTOS.h>
//#include <semphr.h>
#include <drivers/include/nrfx_twi.h> // NRF_TWIM_Type
#include <cstdint>
namespace Pinetime {
namespace Drivers {
class TwiMaster {
public:
enum class ErrorCodes { NoError, TransactionFailed };
TwiMaster(NRF_TWIM_Type* module, uint32_t frequency, uint8_t pinSda, uint8_t pinScl);
void Init();
ErrorCodes Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
ErrorCodes Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
void Sleep();
void Wakeup();
private:
// ErrorCodes Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
// ErrorCodes Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
// void FixHwFreezed();
// void ConfigurePins() const;
NRF_TWIM_Type* twiBaseAddress;
// SemaphoreHandle_t mutex = nullptr;
NRF_TWIM_Type* module;
uint32_t frequency;
uint8_t pinSda;
uint8_t pinScl;
static constexpr uint8_t maxDataSize {16};
static constexpr uint8_t registerSize {1};
uint8_t internalBuffer[maxDataSize + registerSize];
uint32_t txStartedCycleCount = 0;
static constexpr uint32_t HwFreezedDelay {161000};
};
}
}

41
sim/drivers/Watchdog.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "drivers/Watchdog.h"
using namespace Pinetime::Drivers;
void Watchdog::Setup(uint8_t timeoutSeconds) {
resetReason = ActualResetReason();
}
void Watchdog::Start() {
}
void Watchdog::Kick() {
}
Watchdog::ResetReasons Watchdog::ActualResetReason() const {
return ResetReasons::ResetPin;
}
const char* Watchdog::ResetReasonToString(Watchdog::ResetReasons reason) {
switch (reason) {
case ResetReasons::ResetPin:
return "Reset pin";
case ResetReasons::Watchdog:
return "Watchdog";
case ResetReasons::DebugInterface:
return "Debug interface";
case ResetReasons::LpComp:
return "LPCOMP";
case ResetReasons::SystemOff:
return "System OFF";
case ResetReasons::CpuLockup:
return "CPU Lock-up";
case ResetReasons::SoftReset:
return "Soft reset";
case ResetReasons::NFC:
return "NFC";
case ResetReasons::HardReset:
return "Hard reset";
default:
return "Unknown";
}
}

34
sim/drivers/Watchdog.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Drivers {
class Watchdog {
public:
enum class ResetReasons { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset };
void Setup(uint8_t timeoutSeconds);
void Start();
void Kick();
ResetReasons ResetReason() const {
return resetReason;
}
static const char* ResetReasonToString(ResetReasons reason);
private:
ResetReasons resetReason;
ResetReasons ActualResetReason() const;
};
class WatchdogView {
public:
WatchdogView(const Watchdog& watchdog) : watchdog {watchdog} {
}
Watchdog::ResetReasons ResetReason() const {
return watchdog.ResetReason();
}
private:
const Watchdog& watchdog;
};
}
}

View File

@@ -0,0 +1,101 @@
#include "heartratetask/HeartRateTask.h"
#include <drivers/Hrs3300.h>
#include <components/heartrate/HeartRateController.h>
#include <nrf_log.h>
using namespace Pinetime::Applications;
HeartRateTask::HeartRateTask(Drivers::Hrs3300& heartRateSensor, Controllers::HeartRateController& controller)
: heartRateSensor {heartRateSensor}, controller {controller} { //, ppg{} {
}
void HeartRateTask::Start() {
messageQueue = xQueueCreate(10, 1);
controller.SetHeartRateTask(this);
// if (pdPASS != xTaskCreate(HeartRateTask::Process, "Heartrate", 500, this, 0, &taskHandle))
// APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
//void HeartRateTask::Process(void* instance) {
// auto* app = static_cast<HeartRateTask*>(instance);
// app->Work();
//}
void HeartRateTask::Work() {
// int lastBpm = 0;
// while (true) {
// Messages msg;
// uint32_t delay;
// if (state == States::Running) {
// if (measurementStarted)
// delay = 40;
// else
// delay = 100;
// } else
// delay = portMAX_DELAY;
//
// if (xQueueReceive(messageQueue, &msg, delay)) {
// switch (msg) {
// case Messages::GoToSleep:
// StopMeasurement();
// state = States::Idle;
// break;
// case Messages::WakeUp:
// state = States::Running;
// if (measurementStarted) {
// lastBpm = 0;
// StartMeasurement();
// }
// break;
// case Messages::StartMeasurement:
// if (measurementStarted)
// break;
// lastBpm = 0;
// StartMeasurement();
// measurementStarted = true;
// break;
// case Messages::StopMeasurement:
// if (!measurementStarted)
// break;
// StopMeasurement();
// measurementStarted = false;
// break;
// }
// }
//
// if (measurementStarted) {
// auto hrs = heartRateSensor.ReadHrs();
// ppg.Preprocess(hrs);
// auto bpm = ppg.HeartRate();
//
// if (lastBpm == 0 && bpm == 0)
// controller.Update(Controllers::HeartRateController::States::NotEnoughData, 0);
// if (bpm != 0) {
// lastBpm = bpm;
// controller.Update(Controllers::HeartRateController::States::Running, lastBpm);
// }
// }
// }
}
void HeartRateTask::PushMessage(HeartRateTask::Messages msg) {
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(messageQueue, &msg, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
/* Actual macro used here is port specific. */
// TODO : should I do something here?
}
}
//void HeartRateTask::StartMeasurement() {
// heartRateSensor.Enable();
// vTaskDelay(100);
// ppg.SetOffset(static_cast<float>(heartRateSensor.ReadHrs()));
//}
//
//void HeartRateTask::StopMeasurement() {
// heartRateSensor.Disable();
// vTaskDelay(100);
//}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <FreeRTOS.h>
//#include <task.h>
#include <queue.h>
//#include <components/heartrate/Ppg.h>
namespace Pinetime {
namespace Drivers {
class Hrs3300;
}
namespace Controllers {
class HeartRateController;
}
namespace Applications {
class HeartRateTask {
public:
enum class Messages : uint8_t { GoToSleep, WakeUp, StartMeasurement, StopMeasurement };
enum class States { Idle, Running };
explicit HeartRateTask(Drivers::Hrs3300& heartRateSensor, Controllers::HeartRateController& controller);
void Start();
void Work();
void PushMessage(Messages msg);
private:
//static void Process(void* instance);
//void StartMeasurement();
//void StopMeasurement();
// TaskHandle_t taskHandle;
QueueHandle_t messageQueue;
States state = States::Running;
Drivers::Hrs3300& heartRateSensor;
Controllers::HeartRateController& controller;
// Controllers::Ppg ppg;
bool measurementStarted = false;
};
}
}

View File

@@ -0,0 +1,7 @@
#include "libraries/delay/nrf_delay.h"
#include <SDL2/SDL.h>
void nrf_delay_ms(uint32_t ms_time)
{
SDL_Delay(ms_time);
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2011 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _NRF_DELAY_H
#define _NRF_DELAY_H
#include <cstdint>
/**
* @brief Function for delaying execution for a number of milliseconds.
*
* @param ms_time Number of milliseconds to wait.
*/
void nrf_delay_ms(uint32_t ms_time);
#endif

View File

@@ -0,0 +1,2 @@
#pragma once
#include "hal/nrf_gpio.h"

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup nrf_log Logger module
* @{
* @ingroup app_common
*
* @brief The nrf_log module interface.
*/
#ifndef NRF_LOG_H_
#define NRF_LOG_H_
#include <cstdio>
#define NRF_LOG_ERROR(...) do {printf("error: "); printf( __VA_ARGS__ ); printf("\n"); } while(false);
#define NRF_LOG_WARNING(...) do {printf("warn: "); printf( __VA_ARGS__ ); printf("\n"); } while(false);
#define NRF_LOG_INFO(...) do {printf("info: "); printf( __VA_ARGS__ ); printf("\n"); } while(false);
#define NRF_LOG_DEBUG(...) do {printf("debug: "); printf( __VA_ARGS__ ); printf("\n"); } while(false);
#endif // NRF_LOG_H_

View File

@@ -0,0 +1,79 @@
// original files copied from nRF5_SDK_15.3.0_59ac345/components/libraries/timer/app_timer.c
// modified to be simulated using SDL2 Timers by NeroBurner
/**
* Copyright (c) 2012 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "app_timer.h"
#include <stdexcept>
#include <cstdlib>
#include <cstdint>
//uint32_t constexpr APP_TIMER_TICKS(uint32_t ms) {
// return static_cast<uint32_t>(
// static_cast<uint64_t>(ms) * configTICK_RATE_HZ / 1000
// );
//}
ret_code_t app_timer_init(void) {
return 0;
}
ret_code_t app_timer_create(app_timer_t *p_timer_id,
app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler) {
if (mode != APP_TIMER_MODE_SINGLE_SHOT) {
throw std::runtime_error("only mode 'APP_TIMER_MODE_SINGLE_SHOT' implemented");
}
p_timer_id->handler = timeout_handler;
return 0;
}
Uint32 timeout_callback_wrapper(Uint32 interval, void *param)
{
auto* timer_id = static_cast<app_timer_t*>(param);
timer_id->handler(timer_id->p_context);
return 0; // cancel timer
}
ret_code_t app_timer_start(app_timer_t &timer_id, uint32_t timeout_ticks, void * p_context) {
timer_id.p_context = p_context;
timer_id.sdl_timer_id = SDL_AddTimer(timeout_ticks, timeout_callback_wrapper, (void*)(&timer_id));
return 0;
}
ret_code_t app_timer_stop(app_timer_t &timer_id) {
return (SDL_RemoveTimer(timer_id.sdl_timer_id) == SDL_TRUE);
}

View File

@@ -0,0 +1,253 @@
// original files copied from nRF5_SDK_15.3.0_59ac345/components/libraries/timer/app_timer.h
// modified to be simulated using SDL2 Timers by NeroBurner
/**
* Copyright (c) 2012 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup app_timer Application Timer
* @{
* @ingroup app_common
*
* @brief Application timer functionality.
*
* @details This module enables the application to create multiple timer instances based on the RTC1
* peripheral. Checking for time-outs and invocation of user time-out handlers is performed
* in the RTC1 interrupt handler. List handling is done using a software interrupt (SWI0).
* Both interrupt handlers are running in APP_LOW priority level.
*
* @details When calling app_timer_start() or app_timer_stop(), the timer operation is just queued,
* and the software interrupt is triggered. The actual timer start/stop operation is
* executed by the SWI0 interrupt handler. Since the SWI0 interrupt is running in APP_LOW,
* if the application code calling the timer function is running in APP_LOW or APP_HIGH,
* the timer operation will not be performed until the application handler has returned.
* This will be the case, for example, when stopping a timer from a time-out handler when not using
* the scheduler.
*
* @details Use the USE_SCHEDULER parameter of the APP_TIMER_INIT() macro to select if the
* @ref app_scheduler should be used or not. Even if the scheduler is
* not used, app_timer.h will include app_scheduler.h, so when
* compiling, app_scheduler.h must be available in one of the compiler include paths.
*/
#ifndef APP_TIMER_H__
#define APP_TIMER_H__
#include <SDL2/SDL.h>
#include "task.h" // configTICK_RATE_HZ
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
// copied from sdk_errors.h
typedef uint32_t ret_code_t;
/** @brief Name of the module used for logger messaging.
*/
#define APP_TIMER_LOG_NAME app_timer
//#define APP_TIMER_CLOCK_FREQ 32768 /**< Clock frequency of the RTC timer used to implement the app timer module. */
//#define APP_TIMER_MIN_TIMEOUT_TICKS 5 /**< Minimum value of the timeout_ticks parameter of app_timer_start(). */
/**@brief Application time-out handler type. */
typedef void (*app_timer_timeout_handler_t)(void * p_context);
struct app_timer_t
{
//nrf_sortlist_item_t list_item; /**< Token used by sortlist. */
//volatile uint32_t end_val; /**< RTC counter value when timer expires. */
SDL_TimerID sdl_timer_id = 0;
uint32_t repeat_period; /**< Repeat period (0 if single shot mode). */
app_timer_timeout_handler_t handler; /**< User handler. */
void * p_context; /**< User context. */
//NRF_LOG_INSTANCE_PTR_DECLARE(p_log) /**< Pointer to instance of the logger object (Conditionally compiled). */
//volatile bool active; /**< Flag indicating that timer is active. */
};
/**
* @brief Create a timer identifier and statically allocate memory for the timer.
*
* @param timer_id Name of the timer identifier variable that will be used to control the timer.
*/
#define APP_TIMER_DEF(timer_id) static app_timer_t timer_id;
/**@brief Convert milliseconds to timer ticks.
*
* This macro uses 64-bit integer arithmetic, but as long as the macro parameters are
* constants (i.e. defines), the computation will be done by the preprocessor.
*
* @param[in] MS Milliseconds.
*
* @return Number of timer ticks.
*/
//#define APP_TIMER_TICKS(MS) \
// ((uint32_t)ROUNDED_DIV( \
// (MS) * (uint64_t)APP_TIMER_CLOCK_FREQ, \
// 1000 * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1)))
uint32_t constexpr APP_TIMER_TICKS(uint32_t ms) {
return static_cast<uint32_t>(
static_cast<uint64_t>(ms) * configTICK_RATE_HZ / 1000
);
}
/**@brief Timer modes. */
typedef enum
{
APP_TIMER_MODE_SINGLE_SHOT, /**< The timer will expire only once. */
APP_TIMER_MODE_REPEATED /**< The timer will restart each time it expires. */
} app_timer_mode_t;
/**@brief Function for initializing the timer module.
*
* @retval NRF_SUCCESS If the module was initialized successfully.
*/
ret_code_t app_timer_init(void);
/**@brief Function for creating a timer instance.
*
* @param[in] p_timer_id Pointer to timer identifier.
* @param[in] mode Timer mode.
* @param[in] timeout_handler Function to be executed when the timer expires.
*
* @retval NRF_SUCCESS If the timer was successfully created.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or
* the timer is running.
*
* @note This function does the timer allocation in the caller's context. It is also not protected
* by a critical region. Therefore care must be taken not to call it from several interrupt
* levels simultaneously.
* @note The function can be called again on the timer instance and will re-initialize the instance if
* the timer is not running.
* @attention The FreeRTOS and RTX app_timer implementation does not allow app_timer_create to
* be called on the previously initialized instance.
*/
ret_code_t app_timer_create(app_timer_t *p_timer_id,
app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler);
/**@brief Function for starting a timer.
*
* @param[in] timer_id Timer identifier.
* @param[in] timeout_ticks Number of ticks (of RTC1, including prescaling) to time-out event
* (minimum 5 ticks).
* @param[in] p_context General purpose pointer. Will be passed to the time-out handler when
* the timer expires.
*
* @retval NRF_SUCCESS If the timer was successfully started.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer
* has not been created.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*
* @note The minimum timeout_ticks value is 5.
* @note For multiple active timers, time-outs occurring in close proximity to each other (in the
* range of 1 to 3 ticks) will have a positive jitter of maximum 3 ticks.
* @note When calling this method on a timer that is already running, the second start operation
* is ignored.
*/
ret_code_t app_timer_start(app_timer_t &timer_id, uint32_t timeout_ticks, void * p_context);
/**@brief Function for stopping the specified timer.
*
* @param[in] timer_id Timer identifier.
*
* @retval NRF_SUCCESS If the timer was successfully stopped.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer
* has not been created.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*/
ret_code_t app_timer_stop(app_timer_t &timer_id);
/**@brief Function for stopping all running timers.
*
* @retval NRF_SUCCESS If all timers were successfully stopped.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*/
//ret_code_t app_timer_stop_all(void);
/**@brief Function for returning the current value of the RTC1 counter.
*
* @return Current value of the RTC1 counter.
*/
//uint32_t app_timer_cnt_get(void);
/**@brief Function for computing the difference between two RTC1 counter values.
*
* @param[in] ticks_to Value returned by app_timer_cnt_get().
* @param[in] ticks_from Value returned by app_timer_cnt_get().
*
* @return Number of ticks from ticks_from to ticks_to.
*/
//uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to,
// uint32_t ticks_from);
/**@brief Function for getting the maximum observed operation queue utilization.
*
* Function for tuning the module and determining OP_QUEUE_SIZE value and thus module RAM usage.
*
* @note APP_TIMER_WITH_PROFILER must be enabled to use this functionality.
*
* @return Maximum number of events in queue observed so far.
*/
//uint8_t app_timer_op_queue_utilization_get(void);
/**
* @brief Function for pausing RTC activity which drives app_timer.
*
* @note This function can be used for debugging purposes to ensure
* that application is halted when entering a breakpoint.
*/
//void app_timer_pause(void);
/**
* @brief Function for resuming RTC activity which drives app_timer.
*
* @note This function can be used for debugging purposes to resume
* application activity.
*/
//void app_timer_resume(void);
#endif // APP_TIMER_H__
/** @} */

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRFX_TWI_H__
#define NRFX_TWI_H__
#include <cstdint>
/**
* @brief I2C compatible Two-Wire Master Interface with EasyDMA 0 (TWIM0)
*/
struct NRF_TWIM_Type { /*!< (@ 0x40003000) TWIM0 Structure */
uint32_t TASKS_STARTRX; /*!< (@ 0x00000000) Start TWI receive sequence */
uint32_t RESERVED;
uint32_t TASKS_STARTTX; /*!< (@ 0x00000008) Start TWI transmit sequence */
uint32_t RESERVED1[2];
uint32_t TASKS_STOP; /*!< (@ 0x00000014) Stop TWI transaction. Must be issued while the
TWI master is not suspended. */
uint32_t RESERVED2;
uint32_t TASKS_SUSPEND; /*!< (@ 0x0000001C) Suspend TWI transaction */
uint32_t TASKS_RESUME; /*!< (@ 0x00000020) Resume TWI transaction */
uint32_t RESERVED3[56];
uint32_t EVENTS_STOPPED; /*!< (@ 0x00000104) TWI stopped */
uint32_t RESERVED4[7];
uint32_t EVENTS_ERROR; /*!< (@ 0x00000124) TWI error */
uint32_t RESERVED5[8];
uint32_t EVENTS_SUSPENDED; /*!< (@ 0x00000148) Last byte has been sent out after the SUSPEND
task has been issued, TWI traffic is now
suspended. */
uint32_t EVENTS_RXSTARTED; /*!< (@ 0x0000014C) Receive sequence started */
uint32_t EVENTS_TXSTARTED; /*!< (@ 0x00000150) Transmit sequence started */
uint32_t RESERVED6[2];
uint32_t EVENTS_LASTRX; /*!< (@ 0x0000015C) Byte boundary, starting to receive the last byte */
uint32_t EVENTS_LASTTX; /*!< (@ 0x00000160) Byte boundary, starting to transmit the last
byte */
uint32_t RESERVED7[39];
uint32_t SHORTS; /*!< (@ 0x00000200) Shortcut register */
uint32_t RESERVED8[63];
uint32_t INTEN; /*!< (@ 0x00000300) Enable or disable interrupt */
uint32_t INTENSET; /*!< (@ 0x00000304) Enable interrupt */
uint32_t INTENCLR; /*!< (@ 0x00000308) Disable interrupt */
uint32_t RESERVED9[110];
uint32_t ERRORSRC; /*!< (@ 0x000004C4) Error source */
uint32_t RESERVED10[14];
uint32_t ENABLE; /*!< (@ 0x00000500) Enable TWIM */
uint32_t RESERVED11;
/// TWIM_PSEL_Type PSEL; /*!< (@ 0x00000508) Unspecified */
uint32_t RESERVED12[5];
uint32_t FREQUENCY; /*!< (@ 0x00000524) TWI frequency */
uint32_t RESERVED13[3];
// TWIM_RXD_Type RXD; /*!< (@ 0x00000534) RXD EasyDMA channel */
// TWIM_TXD_Type TXD; /*!< (@ 0x00000544) TXD EasyDMA channel */
uint32_t RESERVED14[13];
uint32_t ADDRESS; /*!< (@ 0x00000588) Address used in the TWI transfer */
}; /*!< Size = 1420 (0x58c) */
#endif // NRFX_TWI_H__

33
sim/nrfx/hal/nrf_gpio.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include "hal/nrf_gpio.h"
#include "drivers/PinMap.h"
#include <SDL2/SDL.h>
#include <stdexcept>
void nrf_gpio_cfg_default(uint32_t pin_number) {}
void nrf_gpio_pin_set(uint32_t pin_number) {}
uint32_t nrf_gpio_pin_read(uint32_t pin_number)
{
if (pin_number == Pinetime::PinMap::Button) {
int x, y;
uint32_t buttons = SDL_GetMouseState(&x, &y);
bool right_click = (buttons & SDL_BUTTON_RMASK) != 0;
return right_click;
}
throw std::runtime_error("nrf_gpio_pin_read: unhandled pin_number: " + std::to_string(pin_number));
return 0;
}
void nrf_gpio_cfg_output(uint32_t pin_number) {}
void nrf_gpio_pin_clear(uint32_t pin_number) {}
void nrf_gpio_range_cfg_input(uint32_t pin_range_start,
uint32_t pin_range_end,
nrf_gpio_pin_pull_t pull_config) {}
void nrf_gpio_cfg_input(uint32_t pin_number, nrf_gpio_pin_pull_t pull_config) {}
void nrfx_gpiote_in_init(uint32_t pin_number, nrfx_gpiote_in_config_t *config, nrfx_gpiote_event_handler_t) {}
void nrfx_gpiote_in_event_enable(uint32_t pin_number, bool enable) {}
void nrf_gpio_cfg_sense_input(uint32_t pin_number, nrf_gpio_pin_pull_t pin_pull, nrf_gpio_pin_sense_t sense) {}
void APP_GPIOTE_INIT(uint32_t max_users) {}

150
sim/nrfx/hal/nrf_gpio.h Normal file
View File

@@ -0,0 +1,150 @@
/**
* Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRF_GPIO_H__
#define NRF_GPIO_H__
#include <cstdint>
/**
* @brief Enumerator used for selecting the pin to be pulled down or up at the time of pin configuration.
*/
// lv_sim: values copied from nrf52810_bifiellds.h
enum nrf_gpio_pin_pull_t
{
NRF_GPIO_PIN_NOPULL = 0, // GPIO_PIN_CNF_PULL_Disabled, ///< Pin pull-up resistor disabled.
NRF_GPIO_PIN_PULLDOWN = 1, // GPIO_PIN_CNF_PULL_Pulldown, ///< Pin pull-down resistor enabled.
NRF_GPIO_PIN_PULLUP = 3, // GPIO_PIN_CNF_PULL_Pullup, ///< Pin pull-up resistor enabled.
};
constexpr uint16_t GPIO_PIN_CNF_PULL_Pulldown = 1;
constexpr uint16_t GPIO_PIN_CNF_PULL_Pullup = 3;
using nrf_gpiote_polarity_t = uint32_t;
constexpr uint16_t NRF_GPIOTE_POLARITY_HITOLO = 2;
constexpr uint16_t NRF_GPIOTE_POLARITY_TOGGLE = 3;
using nrfx_gpiote_pin_t = uint32_t;
typedef void (*nrfx_gpiote_event_handler_t)(nrfx_gpiote_pin_t, nrf_gpiote_polarity_t);
using nrf_gpio_pin_sense_t = uint32_t;
constexpr uint16_t GPIO_PIN_CNF_SENSE_Low = 3;
struct nrfx_gpiote_in_config_t {
bool skip_gpio_setup = false;
bool hi_accuracy = false;
bool is_watcher = false;
nrf_gpiote_polarity_t sense;
nrf_gpio_pin_pull_t pull;
};
/**
* @brief Function for resetting pin configuration to its default state.
*
* @param pin_number Specifies the pin number.
*/
void nrf_gpio_cfg_default(uint32_t pin_number);
/**
* @brief Function for setting a GPIO pin.
*
* Note that the pin must be configured as an output for this function to have any effect.
*
* @param pin_number Specifies the pin number to set.
*/
void nrf_gpio_pin_set(uint32_t pin_number);
// read pin stub, intended to forward right mouse button as PinMap::Button
uint32_t nrf_gpio_pin_read(uint32_t pin_number);
/**
* @brief Function for configuring the given GPIO pin number as output, hiding inner details.
* This function can be used to configure a pin as simple output with gate driving GPIO_PIN_CNF_DRIVE_S0S1 (normal cases).
*
* @param pin_number Specifies the pin number.
*
* @note Sense capability on the pin is disabled and input is disconnected from the buffer as the pins are configured as output.
*/
void nrf_gpio_cfg_output(uint32_t pin_number);
/**
* @brief Function for clearing a GPIO pin.
*
* Note that the pin must be configured as an output for this
* function to have any effect.
*
* @param pin_number Specifies the pin number to clear.
*/
void nrf_gpio_pin_clear(uint32_t pin_number);
/**
* @brief Function for configuring the GPIO pin range as input pins with given initial value set, hiding inner details.
* This function can be used to configure pin range as simple input.
*
* @param pin_range_start Specifies the start number (inclusive) in the range of pin numbers to be configured (allowed values 0-30).
*
* @param pin_range_end Specifies the end number (inclusive) in the range of pin numbers to be configured (allowed values 0-30).
*
* @param pull_config State of the pin range pull resistor (no pull, pulled down, or pulled high).
*
* @note For configuring only one pin as input, use @ref nrf_gpio_cfg_input.
* Sense capability on the pin is disabled and input is connected to buffer so that the GPIO->IN register is readable.
*/
void nrf_gpio_range_cfg_input(uint32_t pin_range_start,
uint32_t pin_range_end,
nrf_gpio_pin_pull_t pull_config);
/**
* @brief Function for configuring the given GPIO pin number as input, hiding inner details.
* This function can be used to configure a pin as simple input.
*
* @param pin_number Specifies the pin number.
* @param pull_config State of the pin range pull resistor (no pull, pulled down, or pulled high).
*
* @note Sense capability on the pin is disabled and input is connected to buffer so that the GPIO->IN register is readable.
*/
void nrf_gpio_cfg_input(uint32_t pin_number, nrf_gpio_pin_pull_t pull_config);
void nrfx_gpiote_in_init(uint32_t pin_number, nrfx_gpiote_in_config_t *config, nrfx_gpiote_event_handler_t);
void nrfx_gpiote_in_event_enable(uint32_t pin_number, bool enable);
void nrf_gpio_cfg_sense_input(uint32_t pin_number, nrf_gpio_pin_pull_t pin_pull, nrf_gpio_pin_sense_t sense);
void APP_GPIOTE_INIT(uint32_t max_users);
#endif // NRF_GPIO_H__

15
sim/nrfx/hal/nrf_rtc.cpp Normal file
View File

@@ -0,0 +1,15 @@
#include "hal/nrf_rtc.h"
#include "task.h"
#include <chrono>
#include <stdexcept>
uint32_t nrf_rtc_counter_get(NRF_RTC_Type p_reg)
{
if (p_reg == portNRF_RTC_REG) {
TickType_t ticks_now = xTaskGetTickCount();
return ticks_now;
}
throw std::runtime_error("nrf_rtc_counter_get: unhandled NRF_RTC_Type: " + std::to_string(p_reg));
return 0;
}

7
sim/nrfx/hal/nrf_rtc.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include "portmacro_cmsis.h"
// normal version has pointer to register 'NRF_RTC_Type * p_reg', but we just simulate
// the return value according to known register pointer
uint32_t nrf_rtc_counter_get(NRF_RTC_Type p_reg);

View File

@@ -0,0 +1,3 @@
#pragma once
#include <hal/nrf_gpio.h>

54
sim/nrfx/nrfx_log.h Normal file
View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup nrf_log Logger module
* @{
* @ingroup app_common
*
* @brief The nrfx_log module interface.
*/
#ifndef NRFX_LOG_H_
#define NRFX_LOG_H_
#include "libraries/log/nrf_log.h"
#endif // NRFX_LOG_H_

3
sim/portmacro_cmsis.cpp Normal file
View File

@@ -0,0 +1,3 @@
#include "portmacro_cmsis.h"
void portYIELD_FROM_ISR(BaseType_t) {}

49
sim/portmacro_cmsis.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef PORTMACRO_CMSIS_H
#define PORTMACRO_CMSIS_H
#include <cstdint>
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
typedef long BaseType_t;
typedef unsigned long UBaseType_t;
#define pdFALSE ( ( BaseType_t ) 0 )
#define pdTRUE ( ( BaseType_t ) 1 )
#define pdPASS pdTRUE
/* RTC register */
using NRF_RTC_Type = uint32_t;
constexpr NRF_RTC_Type portNRF_RTC_REG = 1;
void portYIELD_FROM_ISR(BaseType_t);
#endif /* PORTMACRO_CMSIS_H */

46
sim/queue.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include "queue.h"
#include <stdexcept>
#include <SDL2/SDL.h>
QueueHandle_t xQueueCreate(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize)
{
QueueHandle_t xQueue;
if (uxItemSize != 1) {
throw std::runtime_error("uxItemSize must be 1");
}
xQueue.queue.reserve(uxQueueLength);
return xQueue;
}
BaseType_t xQueueSend(QueueHandle_t &xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait)
{
std::lock_guard<std::mutex> guard(xQueue.mutex);
xQueue.queue.push_back(*reinterpret_cast<const uint8_t *const>(pvItemToQueue));
return true;
}
BaseType_t xQueueSendFromISR(QueueHandle_t &xQueue, const void * const pvItemToQueue, BaseType_t *xHigherPriorityTaskWoken)
{
TickType_t xTicksToWait = 0;
*xHigherPriorityTaskWoken = pdFALSE;
return xQueueSend(xQueue, pvItemToQueue, 0.0);
}
BaseType_t xQueueReceive(QueueHandle_t &xQueue, void * const pvBuffer, TickType_t xTicksToWait)
{
while (xQueue.queue.empty()) {
if (xTicksToWait <= 25) {
return false;
}
SDL_Delay(25);
xTicksToWait -= 25;
}
if (xQueue.queue.empty()) {
return false;
}
std::lock_guard<std::mutex> guard(xQueue.mutex);
uint8_t *buf = reinterpret_cast<uint8_t * const>(pvBuffer);
*buf = xQueue.queue.at(0);
xQueue.queue.erase(xQueue.queue.begin());
return true;
}

31
sim/queue.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include "portmacro_cmsis.h"
#include <mutex>
#include <vector>
/**
* Type by which queues are referenced. For example, a call to xQueueCreate()
* returns an QueueHandle_t variable that can then be used as a parameter to
* xQueueSend(), xQueueReceive(), etc.
*/
//typedef void * QueueHandle_t;
struct QueueHandle_t {
std::mutex mutex;
std::vector<uint8_t> queue;
QueueHandle_t() {}
QueueHandle_t(const QueueHandle_t &o) {
queue=o.queue;
}
QueueHandle_t &operator=(const QueueHandle_t &o) {
queue=o.queue;
return *this;
}
};
//using QueueHandle_t = std::vector<uint8_t>;
QueueHandle_t xQueueCreate(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize);
BaseType_t xQueueSend(QueueHandle_t &xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait);
BaseType_t xQueueSendFromISR(QueueHandle_t &xQueue, const void * const pvItemToQueue, BaseType_t *xHigherPriorityTaskWoken);
BaseType_t xQueueReceive(QueueHandle_t &xQueue, void * const pvBuffer, TickType_t xTicksToWait );

87
sim/task.cpp Normal file
View File

@@ -0,0 +1,87 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#include <SDL2/SDL.h>
/* Standard includes. */
#include <chrono>
/* FreeRTOS includes. */
//#include "FreeRTOS.h"
#include "task.h"
//#include "timers.h"
//#include "stack_macros.h"
TickType_t xTaskGetTickCount()
{
static auto start = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::duration<TickType_t, std::ratio<1, configTICK_RATE_HZ>>>(now - start);
return diff.count();
}
/*-----------------------------------------------------------*/
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime) {
return 0;
}
void vTaskDelay( const TickType_t xTicksToDelay ) {
SDL_Delay(xTicksToDelay);
}
int sdl_function_wrapper(void *instance)
{
TaskHandle_t * task_handle = static_cast<TaskHandle_t*>(instance);
task_handle->task_fn(task_handle->instance);
return 0;
}
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
pxCreatedTask->task_fn = pxTaskCode;
pxCreatedTask->instance = pvParameters;
pxCreatedTask->thread_handle = SDL_CreateThread(sdl_function_wrapper, pcName, pxCreatedTask);
return pxCreatedTask->thread_handle != nullptr;
}
BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify) {
return pdPASS;
}
TaskHandle_t xTaskGetCurrentTaskHandle() {
return {};
}
BaseType_t xTaskGetSchedulerState() {
return taskSCHEDULER_NOT_STARTED;
}

326
sim/task.h Normal file
View File

@@ -0,0 +1,326 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef INC_TASK_H
#define INC_TASK_H
#include "portmacro_cmsis.h"
#include <cstdint>
// copied from InfiniTime/src/FreeRTOSConfig.h
#define configTICK_RATE_HZ 1024
#define configSTACK_DEPTH_TYPE uint16_t
/*-----------------------------------------------------------
* TASK UTILITIES
*----------------------------------------------------------*/
/**
* task. h
* <PRE>TickType_t xTaskGetTickCount( void );</PRE>
*
* @return The count of ticks since vTaskStartScheduler was called.
*
* \defgroup xTaskGetTickCount xTaskGetTickCount
* \ingroup TaskUtils
*/
TickType_t xTaskGetTickCount();
typedef void (*TaskFunction_t)(void *instance);
/**
* task. h
*
* Type by which tasks are referenced. For example, a call to xTaskCreate
* returns (via a pointer parameter) an TaskHandle_t variable that can then
* be used as a parameter to vTaskDelete to delete the task.
*
* \defgroup TaskHandle_t TaskHandle_t
* \ingroup Tasks
*/
//typedef void * TaskHandle_t;
struct TaskHandle_t {
void *thread_handle = nullptr;
TaskFunction_t task_fn;
void *instance = nullptr;
};
/* Task states returned by eTaskGetState. */
enum eTaskState
{
eRunning = 0, /* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
};
/* Actions that can be performed when vTaskNotify() is called. */
enum eNotifyAction
{
eNoAction = 0, /* Notify the task without updating its notify value. */
eSetBits, /* Set bits in the task's notification value. */
eIncrement, /* Increment the task's notification value. */
eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */
eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */
};
/* Definitions returned by xTaskGetSchedulerState(). taskSCHEDULER_SUSPENDED is
0 to generate more optimal code when configASSERT() is defined as the constant
is used in assert() statements. */
#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 )
#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 )
#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 )
/* Used with the uxTaskGetSystemState() function to return the state of each task
in the system. */
struct TaskStatus_t {
TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */
const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
UBaseType_t xTaskNumber; /* A number unique to the task. */
eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */
UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */
UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */
uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */
//StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */
uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */
};
/**
* configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for
* uxTaskGetSystemState() to be available.
*
* uxTaskGetSystemState() populates an TaskStatus_t structure for each task in
* the system. TaskStatus_t structures contain, among other things, members
* for the task handle, task name, task priority, task state, and total amount
* of run time consumed by the task. See the TaskStatus_t structure
* definition in this file for the full member list.
*
* NOTE: This function is intended for debugging use only as its use results in
* the scheduler remaining suspended for an extended period.
*
* @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures.
* The array must contain at least one TaskStatus_t structure for each task
* that is under the control of the RTOS. The number of tasks under the control
* of the RTOS can be determined using the uxTaskGetNumberOfTasks() API function.
*
* @param uxArraySize The size of the array pointed to by the pxTaskStatusArray
* parameter. The size is specified as the number of indexes in the array, or
* the number of TaskStatus_t structures contained in the array, not by the
* number of bytes in the array.
*
* @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in
* FreeRTOSConfig.h then *pulTotalRunTime is set by uxTaskGetSystemState() to the
* total run time (as defined by the run time stats clock, see
* http://www.freertos.org/rtos-run-time-stats.html) since the target booted.
* pulTotalRunTime can be set to NULL to omit the total run time information.
*
* @return The number of TaskStatus_t structures that were populated by
* uxTaskGetSystemState(). This should equal the number returned by the
* uxTaskGetNumberOfTasks() API function, but will be zero if the value passed
* in the uxArraySize parameter was too small.
*/
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime);
/**
* task. h
* <pre>void vTaskDelay( const TickType_t xTicksToDelay );</pre>
*
* Delay a task for a given number of ticks. The actual time that the
* task remains blocked depends on the tick rate. The constant
* portTICK_PERIOD_MS can be used to calculate real time from the tick
* rate - with the resolution of one tick period.
*
* INCLUDE_vTaskDelay must be defined as 1 for this function to be available.
* See the configuration section for more information.
*
*
* vTaskDelay() specifies a time at which the task wishes to unblock relative to
* the time at which vTaskDelay() is called. For example, specifying a block
* period of 100 ticks will cause the task to unblock 100 ticks after
* vTaskDelay() is called. vTaskDelay() does not therefore provide a good method
* of controlling the frequency of a periodic task as the path taken through the
* code, as well as other task and interrupt activity, will effect the frequency
* at which vTaskDelay() gets called and therefore the time at which the task
* next executes. See vTaskDelayUntil() for an alternative API function designed
* to facilitate fixed frequency execution. It does this by specifying an
* absolute time (rather than a relative time) at which the calling task should
* unblock.
*
* @param xTicksToDelay The amount of time, in tick periods, that
* the calling task should block.
*
* Example usage:
void vTaskFunction( void * pvParameters )
{
// Block for 500ms.
const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
for( ;; )
{
// Simply toggle the LED every 500ms, blocking between each toggle.
vToggleLED();
vTaskDelay( xDelay );
}
}
* \defgroup vTaskDelay vTaskDelay
* \ingroup TaskCtrl
*/
void vTaskDelay( const TickType_t xTicksToDelay );
/**
* task. h
*<pre>
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pvCreatedTask
);</pre>
*
* Create a new task and add it to the list of tasks that are ready to run.
*
* Internally, within the FreeRTOS implementation, tasks use two blocks of
* memory. The first block is used to hold the task's data structures. The
* second block is used by the task as its stack. If a task is created using
* xTaskCreate() then both blocks of memory are automatically dynamically
* allocated inside the xTaskCreate() function. (see
* http://www.freertos.org/a00111.html). If a task is created using
* xTaskCreateStatic() then the application writer must provide the required
* memory. xTaskCreateStatic() therefore allows a task to be created without
* using any dynamic memory allocation.
*
* See xTaskCreateStatic() for a version that does not use any dynamic memory
* allocation.
*
* xTaskCreate() can only be used to create a task that has unrestricted
* access to the entire microcontroller memory map. Systems that include MPU
* support can alternatively create an MPU constrained task using
* xTaskCreateRestricted().
*
* @param pvTaskCode Pointer to the task entry function. Tasks
* must be implemented to never return (i.e. continuous loop).
*
* @param pcName A descriptive name for the task. This is mainly used to
* facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default
* is 16.
*
* @param usStackDepth The size of the task stack specified as the number of
* variables the stack can hold - not the number of bytes. For example, if
* the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes
* will be allocated for stack storage.
*
* @param pvParameters Pointer that will be used as the parameter for the task
* being created.
*
* @param uxPriority The priority at which the task should run. Systems that
* include MPU support can optionally create tasks in a privileged (system)
* mode by setting bit portPRIVILEGE_BIT of the priority parameter. For
* example, to create a privileged task at priority 2 the uxPriority parameter
* should be set to ( 2 | portPRIVILEGE_BIT ).
*
* @param pvCreatedTask Used to pass back a handle by which the created task
* can be referenced.
*
* @return pdPASS if the task was successfully created and added to a ready
* list, otherwise an error code defined in the file projdefs.h
*/
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask );
/**
* task. h
* <PRE>BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );</PRE>
*
* configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro
* to be available.
*
* When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private
* "notification value", which is a 32-bit unsigned integer (uint32_t).
*
* Events can be sent to a task using an intermediary object. Examples of such
* objects are queues, semaphores, mutexes and event groups. Task notifications
* are a method of sending an event directly to a task without the need for such
* an intermediary object.
*
* A notification sent to a task can optionally perform an action, such as
* update, overwrite or increment the task's notification value. In that way
* task notifications can be used to send data to a task, or be used as light
* weight and fast binary or counting semaphores.
*
* xTaskNotifyGive() is a helper macro intended for use when task notifications
* are used as light weight and faster binary or counting semaphore equivalents.
* Actual FreeRTOS semaphores are given using the xSemaphoreGive() API function,
* the equivalent action that instead uses a task notification is
* xTaskNotifyGive().
*
* When task notifications are being used as a binary or counting semaphore
* equivalent then the task being notified should wait for the notification
* using the ulTaskNotificationTake() API function rather than the
* xTaskNotifyWait() API function.
*
* See http://www.FreeRTOS.org/RTOS-task-notifications.html for more details.
*
* @param xTaskToNotify The handle of the task being notified. The handle to a
* task can be returned from the xTaskCreate() API function used to create the
* task, and the handle of the currently running task can be obtained by calling
* xTaskGetCurrentTaskHandle().
*
* @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the
* eAction parameter set to eIncrement - so pdPASS is always returned.
*
* \defgroup xTaskNotifyGive xTaskNotifyGive
* \ingroup TaskNotifications
*/
BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify);
//#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL )
//BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue );
/*
* Return the handle of the calling task.
*/
TaskHandle_t xTaskGetCurrentTaskHandle();
/*
* Returns the scheduler state as taskSCHEDULER_RUNNING,
* taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED.
*/
BaseType_t xTaskGetSchedulerState();
#endif /* INC_TASK_H */

69
sim/timers.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include "timers.h"
#include <stdexcept>
uint32_t timer_callback_wrapper(uint32_t interval, void *param) {
TimerHandle_t *xTimer = static_cast<TimerHandle_t*>(param);
if (!xTimer->running) {
return 0;
}
xTimer->pxCallbackFunction(*xTimer);
if (xTimer->auto_reload) {
return xTimer->xTimerPeriodInTicks;
}
xTimer->running = false;
return 0; // cancel timer
}
void *pvTimerGetTimerID(const TimerHandle_t &xTimer) { // return pvTimerID from xTimerCreate
return xTimer.pvTimerID;
}
void vTimerSetTimerID(TimerHandle_t &xTimer, void *pvNewID) {
xTimer.pvTimerID = pvNewID;
}
TimerHandle_t xTimerCreate(const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload, // false=one-shot, true=recurring
void * const pvTimerID, // pointer passed to callback
TimerCallbackFunction_t pxCallbackFunction)
{
TimerHandle_t xTimer;
xTimer.xTimerPeriodInTicks = xTimerPeriodInTicks;
xTimer.auto_reload = uxAutoReload == pdTRUE;
xTimer.timer_name = pcTimerName;
xTimer.pvTimerID = pvTimerID;
xTimer.pxCallbackFunction = pxCallbackFunction;
return xTimer;
}
bool xTimerStart(TimerHandle_t &xTimer, TickType_t xTicksToWait) {
xTimer.running = true;
xTimer.timer_id = SDL_AddTimer(xTimer.xTimerPeriodInTicks, timer_callback_wrapper, &xTimer);
if (xTimer.pxCallbackFunction == nullptr) {
throw std::runtime_error("xTimerStart called before xTimerCreate");
}
return xTimer.timer_id != 0;
}
bool xTimerChangePeriod(TimerHandle_t &xTimer, TickType_t xNewPeriod, TickType_t xTicksToWait) {
if (xTimer.running) {
xTimerStop(xTimer, xTicksToWait);
xTimer.xTimerPeriodInTicks = xNewPeriod;
xTimerStart(xTimer, xTicksToWait);
} else {
xTimer.xTimerPeriodInTicks = xNewPeriod;
}
return true;
}
bool xTimerReset(TimerHandle_t &xTimer, TickType_t xTicksToWait) {
if (xTimer.running) {
xTimerStop(xTimer, xTicksToWait);
}
return xTimerStart(xTimer, xTicksToWait);
}
bool xTimerStop(TimerHandle_t &xTimer, TickType_t xTicksToWait) {
xTimer.running = false;
return SDL_RemoveTimer(xTimer.timer_id);
}

150
sim/timers.h Normal file
View File

@@ -0,0 +1,150 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#pragma once
#include "portmacro_cmsis.h" // TickType_t
#include <SDL2/SDL.h>
#include <chrono>
#include <cstdint>
#include <string>
class TimerHandle_t;
/*
* Defines the prototype to which timer callback functions must conform.
*/
typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer );
struct TimerHandle_t {
bool running = false;
bool auto_reload = false;
SDL_TimerID timer_id = 0;
TickType_t xTimerPeriodInTicks;
std::string timer_name;
void * pvTimerID;
TimerCallbackFunction_t pxCallbackFunction;
};
constexpr uint32_t pdMS_TO_TICKS(uint32_t ticks) {
return ticks;
}
/**
* void *pvTimerGetTimerID( TimerHandle_t xTimer );
*
* Returns the ID assigned to the timer.
*
* IDs are assigned to timers using the pvTimerID parameter of the call to
* xTimerCreated() that was used to create the timer, and by calling the
* vTimerSetTimerID() API function.
*
* If the same callback function is assigned to multiple timers then the timer
* ID can be used as time specific (timer local) storage.
*
* @param xTimer The timer being queried.
*
* @return The ID assigned to the timer being queried.
*
* Example usage:
*
* See the xTimerCreate() API function example usage scenario.
*/
void *pvTimerGetTimerID(const TimerHandle_t &xTimer ); // return pvTimerID from xTimerCreate
/**
* void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );
*
* Sets the ID assigned to the timer.
*
* IDs are assigned to timers using the pvTimerID parameter of the call to
* xTimerCreated() that was used to create the timer.
*
* If the same callback function is assigned to multiple timers then the timer
* ID can be used as time specific (timer local) storage.
*
* @param xTimer The timer being updated.
*
* @param pvNewID The ID to assign to the timer.
*
* Example usage:
*
* See the xTimerCreate() API function example usage scenario.
*/
void vTimerSetTimerID(TimerHandle_t &xTimer, void *pvNewID);
TimerHandle_t xTimerCreate(const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload, // false=one-shot, true=recurring
void * const pvTimerID, // pointer passed to callback
TimerCallbackFunction_t pxCallbackFunction);
/**
* @param xTicksToWait Specifies the time, in ticks, that the calling task should
* be held in the Blocked state to wait for the stop command to be successfully
* sent to the timer command queue, should the queue already be full when
* xTimerStop() was called. xTicksToWait is ignored if xTimerStop() is called
* before the scheduler is started.
*/
bool xTimerStart(TimerHandle_t &xTimer, TickType_t xTicksToWait);
/*
* xTimerChangePeriod() changes the period of a timer that was previously
* created using the xTimerCreate() API function.
*
* xTimerChangePeriod() can be called to change the period of an active or
* dormant state timer.
*
* The configUSE_TIMERS configuration constant must be set to 1 for
* xTimerChangePeriod() to be available.
*/
bool xTimerChangePeriod(TimerHandle_t &xTimer, TickType_t xNewPeriod, TickType_t xTicksToWait);
/**
* xTimerReset() re-starts a timer that was previously created using the
* xTimerCreate() API function. If the timer had already been started and was
* already in the active state, then xTimerReset() will cause the timer to
* re-evaluate its expiry time so that it is relative to when xTimerReset() was
* called. If the timer was in the dormant state then xTimerReset() has
* equivalent functionality to the xTimerStart() API function.
*
* Resetting a timer ensures the timer is in the active state. If the timer
* is not stopped, deleted, or reset in the mean time, the callback function
* associated with the timer will get called 'n' ticks after xTimerReset() was
* called, where 'n' is the timers defined period.
*
* It is valid to call xTimerReset() before the scheduler has been started, but
* when this is done the timer will not actually start until the scheduler is
* started, and the timers expiry time will be relative to when the scheduler is
* started, not relative to when xTimerReset() was called.
*/
bool xTimerReset(TimerHandle_t &xTimer, TickType_t xTicksToWait);
bool xTimerStop(TimerHandle_t &xTimer, TickType_t xTicksToWait);