From 6ba28786055e0538cf0b7631cd33b36f02d46deb Mon Sep 17 00:00:00 2001 From: Avamander Date: Mon, 17 May 2021 13:57:21 +0300 Subject: [PATCH 01/41] Added QCBOR dependency --- src/CMakeLists.txt | 30 ++++++++++++++++++++++++++++-- src/libs/QCBOR | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) create mode 160000 src/libs/QCBOR diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e727b2b0..4ab303d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -357,6 +357,14 @@ set(LVGL_SRC libs/lvgl/src/lv_widgets/lv_win.c ) +set(QCBOR_SRC + libs/QCBOR/src/ieee754.c + libs/QCBOR/src/qcbor_decode.c + libs/QCBOR/src/qcbor_encode.c + libs/QCBOR/src/qcbor_err_to_str.c + libs/QCBOR/src/UsefulBuf.c + ) + list(APPEND IMAGE_FILES displayapp/icons/battery/os_battery_error.c displayapp/icons/battery/os_battery_100.c @@ -835,6 +843,24 @@ target_compile_options(lvgl PRIVATE $<$: -MP -MD -x assembler-with-cpp> ) +# QCBOR +add_library(QCBOR STATIC ${QCBOR_SRC}) +target_include_directories(QCBOR SYSTEM PUBLIC libs/QCBOR/inc) +# This is required with the current configuration +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_FLOAT_HW_USE) +# These are for space-saving +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_PREFERRED_FLOAT) +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_EXP_AND_MANTISSA) +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS) +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS) +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_UNCOMMON_TAGS) +set_target_properties(QCBOR PROPERTIES LINKER_LANGUAGE C) +target_compile_options(QCBOR PRIVATE + $<$,$>: ${COMMON_FLAGS} -O0 -g3> + $<$,$>: ${COMMON_FLAGS} -O3> + $<$: -MP -MD -x assembler-with-cpp> + ) + # LITTLEFS_SRC add_library(littlefs STATIC ${LITTLEFS_SRC}) target_include_directories(littlefs SYSTEM PUBLIC . ../) @@ -853,7 +879,7 @@ set(EXECUTABLE_FILE_NAME ${EXECUTABLE_NAME}-${pinetime_VERSION_MAJOR}.${pinetime set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld") add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES}) set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME}) -target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs) +target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs QCBOR) target_compile_options(${EXECUTABLE_NAME} PUBLIC $<$,$>: ${COMMON_FLAGS} -Og -g3> $<$,$>: ${COMMON_FLAGS} -Os> @@ -882,7 +908,7 @@ set(IMAGE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERSION_ set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld") add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES}) -target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs) +target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs QCBOR) set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME}) target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC $<$,$>: ${COMMON_FLAGS} -Og -g3> diff --git a/src/libs/QCBOR b/src/libs/QCBOR new file mode 160000 index 00000000..9e2f7080 --- /dev/null +++ b/src/libs/QCBOR @@ -0,0 +1 @@ +Subproject commit 9e2f70804393823cc6d16f9f1035ef7223faca04 From bda96dc595aecb56739cc02de7e7d2d825927b7f Mon Sep 17 00:00:00 2001 From: Avamander Date: Thu, 10 Jun 2021 00:44:49 +0300 Subject: [PATCH 02/41] Initial Weather service skeleton --- .gitmodules | 3 + src/components/ble/NimbleController.cpp | 2 + src/components/ble/NimbleController.h | 2 + src/components/ble/weather/WeatherData.h | 338 ++++++++++++++++++ src/components/ble/weather/WeatherService.cpp | 208 +++++++++++ src/components/ble/weather/WeatherService.h | 139 +++++++ src/displayapp/screens/Weather.cpp | 246 +++++++++++++ src/displayapp/screens/Weather.h | 54 +++ 8 files changed, 992 insertions(+) create mode 100644 src/components/ble/weather/WeatherData.h create mode 100644 src/components/ble/weather/WeatherService.cpp create mode 100644 src/components/ble/weather/WeatherService.h create mode 100644 src/displayapp/screens/Weather.cpp create mode 100644 src/displayapp/screens/Weather.h diff --git a/.gitmodules b/.gitmodules index 815fc022..8d302ae7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "src/libs/littlefs"] path = src/libs/littlefs url = https://github.com/littlefs-project/littlefs.git +[submodule "src/libs/QCBOR"] + path = src/libs/QCBOR + url = https://github.com/laurencelundblade/QCBOR.git diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 43a8b0d6..9ef2d057 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -36,6 +36,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, alertNotificationClient {systemTask, notificationManager}, currentTimeService {dateTimeController}, musicService {systemTask}, + weatherService {systemTask, dateTimeController}, navService {systemTask}, batteryInformationService {batteryController}, immediateAlertService {systemTask, notificationManager}, @@ -77,6 +78,7 @@ void NimbleController::Init() { currentTimeClient.Init(); currentTimeService.Init(); musicService.Init(); + weatherService.Init(); navService.Init(); anService.Init(); dfuService.Init(); diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index 895b87f2..a21cbe81 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -20,6 +20,7 @@ #include "components/ble/ServiceDiscovery.h" #include "components/ble/HeartRateService.h" #include "components/ble/MotionService.h" +#include "components/ble/weather/WeatherService.h" namespace Pinetime { namespace Drivers { @@ -93,6 +94,7 @@ namespace Pinetime { AlertNotificationClient alertNotificationClient; CurrentTimeService currentTimeService; MusicService musicService; + WeatherService weatherService; NavigationService navService; BatteryInformationService batteryInformationService; ImmediateAlertService immediateAlertService; diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h new file mode 100644 index 00000000..c1d53f4e --- /dev/null +++ b/src/components/ble/weather/WeatherData.h @@ -0,0 +1,338 @@ +/* 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 . +*/ +#pragma once + +/** + * Different weather events, weather data structures used by {@link WeatherService.h} + * + * + * Implemented based on and other material: + * https://en.wikipedia.org/wiki/METAR + * https://www.weather.gov/jetstream/obscurationtypes + * http://www.faraim.org/aim/aim-4-03-14-493.html + */ + +namespace Pinetime { + namespace Controllers { + class WeatherData { + public: + /** + * Visibility obscuration types + */ + enum class obscurationtype { + /** No obscuration */ + None = 0, + /** Water particles suspended in the air; low visibility; does not fall */ + Fog = 1, + /** Extremely small, dry particles in the air; invisible to the eye; opalescent */ + Haze = 2, + /** Small fire-created particles suspended in the air */ + Smoke = 3, + /** Fine rock powder, from for example volcanoes */ + Ash = 4, + /** Fine particles of earth suspended in the air by the wind */ + Dust = 5, + /** Fine particles of sand suspended in the air by the wind */ + Sand = 6, + /** Water particles suspended in the air; low-ish visibility; temperature is near dewpoint */ + Mist = 7, + }; + + /** + * Types of precipitation + */ + enum class precipitationtype { + /** + * No precipitation + * + * Theoretically we could just _not_ send the event, but then + * how do we differentiate between no precipitation and + * no information about precipitation + */ + None = 0, + /** Drops larger than a drizzle; also widely separated drizzle */ + Rain = 1, + /** Fairly uniform rain consisting of fine drops */ + Drizzle = 2, + /** Rain that freezes upon contact with objects and ground */ + FreezingRain = 3, + /** Rain + hail; ice pellets; small translucent frozen raindrops */ + Sleet = 4, + /** Larger ice pellets; falling separately or in irregular clumps */ + Hail = 5, + /** Hail with smaller grains of ice; mini-snowballs */ + SmallHail = 6, + /** Snow... */ + Snow = 7, + /** Frozen drizzle; very small snow crystals */ + SnowGrains = 8, + /** Needles; columns or plates of ice. Sometimes described as "diamond dust". In very cold regions */ + IceCrystals = 9 + }; + + /** + * These are special events that can "enhance" the "experience" of existing weather events + */ + enum class specialtype { + /** Strong wind with a sudden onset that lasts at least a minute */ + Squall = 0, + /** Series of waves in a water body caused by the displacement of a large volume of water */ + Tsunami = 1, + /** Violent; rotating column of air */ + Tornado = 2, + /** Unplanned; unwanted; uncontrolled fire in an area */ + Fire = 3, + /** Thunder and/or lightning */ + Thunder = 4, + }; + + /** + * These are used for weather timeline manipulation + * that isn't just adding to the stack of weather events + */ + enum class controlcodes { + /** How much is stored already */ + GetLength = 0, + /** This wipes the entire timeline */ + DelTimeline = 1, + /** There's a currently valid timeline event with the given type */ + HasValidEvent = 3 + }; + + /** + * Events have types + * then they're easier to parse after sending them over the air + */ + enum class eventtype { + /** @see obscuration */ + Obscuration = 0, + /** @see precipitation */ + Precipitation = 1, + /** @see wind */ + Wind = 2, + /** @see temperature */ + Temperature = 3, + /** @see airquality */ + AirQuality = 4, + /** @see special */ + Special = 5, + /** @see pressure */ + Pressure = 6, + /** @see location */ + Location = 7, + /** @see cloud */ + Clouds = 8, + }; + + /** + * Valid event query + */ + class valideventquery { + public: + static constexpr controlcodes code = controlcodes::HasValidEvent; + eventtype eventType; + }; + + /** The header used for further parsing */ + class timelineheader { + public: + /** UNIX timestamp */ + uint64_t timestamp; + /** + * Time in seconds until the event expires + * + * 32 bits ought to be enough for everyone + * + * If there's a newer event of the same type then it overrides this one, even if it hasn't expired + */ + uint32_t expires; + /** + * What type of weather-related event + */ + eventtype eventType; + }; + + /** Specifies how cloudiness is stored */ + class clouds : public timelineheader { + public: + /** Cloud coverage in percentage, 0-100% */ + uint8_t amount; + }; + + /** Specifies how obscuration is stored */ + class obscuration : public timelineheader { + public: + /** Type */ + obscurationtype type; + /** Visibility distance in meters */ + uint8_t amount; + }; + + /** Specifies how precipitation is stored */ + class precipitation : public timelineheader { + public: + /** Type */ + precipitationtype type; + /** How much is it going to rain? In millimeters */ + uint8_t amount; + }; + + /** + * How wind speed is stored + * + * In order to represent bursts of wind instead of constant wind, + * you have minimum and maximum speeds. + * + * As direction can fluctuate wildly and some watchfaces might wish to display it nicely, + * we're following the aerospace industry weather report option of specifying a range. + */ + class wind : public timelineheader { + public: + /** Meters per second */ + uint8_t speedMin; + /** Meters per second */ + uint8_t speedMax; + /** Unitless direction between 0-255; approximately 1 unit per 0.71 degrees */ + uint8_t directionMin; + /** Unitless direction between 0-255; approximately 1 unit per 0.71 degrees */ + uint8_t directionMax; + }; + + /** + * How temperature is stored + * + * As it's annoying to figure out the dewpoint on the watch, + * please send it from the companion + * + * We don't do floats, microdegrees are not useful. Make sure to multiply. + */ + class temperature : public timelineheader { + public: + /** Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250) */ + int16_t temperature; + /** Dewpoint °C but multiplied by 100 (e.g. -12.50°C becomes -1250) */ + int16_t dewPoint; + }; + + /** + * How location info is stored + * + * This can be mostly static with long expiration, + * as it usually is, but it could change during a trip for ex. + * so we allow changing it dynamically. + * + * Location info can be for some kind of map watchface + * or daylight calculations, should those be required. + * + */ + class location : public timelineheader { + public: + /** Location name */ + std::string location; + /** Altitude relative to sea level in meters */ + int16_t altitude; + /** Latitude, EPSG:3857 (Google Maps, Openstreetmaps datum) */ + int32_t latitude; + /** Longitude, EPSG:3857 (Google Maps, Openstreetmaps datum) */ + int32_t longitude; + }; + + /** + * How humidity is stored + */ + class humidity : public timelineheader { + public: + /** Relative humidity, 0-100% */ + uint8_t humidity; + }; + + /** + * How air pressure is stored + */ + class pressure : public timelineheader { + public: + /** Air pressure in hectopascals (hPa) */ + int16_t pressure; + }; + + /** + * How special events are stored + */ + class special : public timelineheader { + public: + /** Special event's type */ + specialtype type; + }; + + /** + * How air quality is stored + * + * These events are a bit more complex because the topic is not simple, + * the intention is to heavy-lift the annoying preprocessing from the watch + * this allows watchface or watchapp makers to generate accurate alerts and graphics + * + * If this needs further enforced standardization, pull requests are welcome + */ + class airquality : public timelineheader { + public: + /** + * The name of the pollution + * + * for the sake of better compatibility with watchapps + * that might want to use this data for say visuals + * don't localize the name. + * + * Ideally watchapp itself localizes the name, if it's at all needed. + * + * E.g. + * For generic ones use "PM0.1", "PM5", "PM10" + * For chemical compounds use the molecular formula e.g. "NO2", "CO2", "O3" + * For pollen use the genus, e.g. "Betula" for birch or "Alternaria" for that mold's spores + */ + std::string polluter; + /** + * Amount of the pollution in SI units, + * otherwise it's going to be difficult to create UI, alerts + * and so on and for. + * + * See more: + * https://ec.europa.eu/environment/air/quality/standards.htm + * http://www.ourair.org/wp-content/uploads/2012-aaqs2.pdf + * + * Example units: + * count/m³ for pollen + * µgC/m³ for micrograms of organic carbon + * µg/m³ sulfates, PM0.1, PM1, PM2, PM10 and so on, dust + * mg/m³ CO2, CO + * ng/m³ for heavy metals + * + * List is not comprehensive, should be improved. + * The current ones are what watchapps assume. + * + * Note: ppb and ppm to concentration should be calculated on the companion, using + * the correct formula (taking into account temperature and air pressure) + * + * Note2: The amount is off by times 100, for two decimal places of precision. + * E.g. 54.32µg/m³ is 5432 + * + */ + uint32_t amount; + }; + }; + } +} \ No newline at end of file diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp new file mode 100644 index 00000000..006fc6c1 --- /dev/null +++ b/src/components/ble/weather/WeatherService.cpp @@ -0,0 +1,208 @@ +/* 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 . +*/ +#include +#include "WeatherService.h" +#include "libs/QCBOR/inc/qcbor/qcbor.h" +#include "systemtask/SystemTask.h" + +int WeatherCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { + return static_cast(arg)->OnCommand(conn_handle, attr_handle, ctxt); +} + +namespace Pinetime { + namespace Controllers { + WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController) + : system(system), dateTimeController(dateTimeController) { + } + + 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 conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) { + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + getCurrentPressure(); + tidyTimeline(); + getTimelineLength(); + const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); + if (packetLen <= 0) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + // Decode + QCBORDecodeContext decodeContext; + UsefulBufC EncodedCBOR; + // TODO: Check uninit fine + QCBORDecode_Init(&decodeContext, EncodedCBOR, QCBOR_DECODE_MODE_NORMAL); + QCBORDecode_EnterMap(&decodeContext, nullptr); + WeatherData::timelineheader timelineHeader {}; + // Always encodes to the smallest number of bytes based on the value + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", reinterpret_cast(&(timelineHeader.timestamp))); + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", reinterpret_cast(&(timelineHeader.expires))); + QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", reinterpret_cast(&(timelineHeader.eventType))); + switch (timelineHeader.eventType) { + // TODO: Populate + case WeatherData::eventtype::AirQuality: { + break; + } + case WeatherData::eventtype::Obscuration: { + break; + } + case WeatherData::eventtype::Precipitation: { + break; + } + case WeatherData::eventtype::Wind: { + break; + } + case WeatherData::eventtype::Temperature: { + break; + } + case WeatherData::eventtype::Special: { + break; + } + case WeatherData::eventtype::Pressure: { + break; + } + case WeatherData::eventtype::Location: { + break; + } + case WeatherData::eventtype::Clouds: { + break; + } + default: { + break; + } + } + QCBORDecode_ExitMap(&decodeContext); + + auto uErr = QCBORDecode_Finish(&decodeContext); + if (uErr != 0) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + // TODO: Detect control messages + + // Encode + uint8_t buffer[64]; + QCBOREncodeContext encodeContext; + 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; + } + + WeatherData::location WeatherService::getCurrentLocation() const { + return WeatherData::location(); + } + WeatherData::clouds WeatherService::getCurrentClouds() const { + return WeatherData::clouds(); + } + WeatherData::obscuration WeatherService::getCurrentObscuration() const { + return WeatherData::obscuration(); + } + WeatherData::precipitation WeatherService::getCurrentPrecipitation() const { + return WeatherData::precipitation(); + } + WeatherData::wind WeatherService::getCurrentWind() const { + return WeatherData::wind(); + } + WeatherData::temperature WeatherService::getCurrentTemperature() const { + return WeatherData::temperature(); + } + WeatherData::humidity WeatherService::getCurrentHumidity() const { + return WeatherData::humidity(); + } + WeatherData::pressure WeatherService::getCurrentPressure() const { + uint64_t currentTimestamp = getCurrentUNIXTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::Pressure && header->timestamp + header->expires <= currentTimestamp) { + return WeatherData::pressure(); + } + } + return WeatherData::pressure(); + } + + WeatherData::airquality WeatherService::getCurrentQuality() const { + return WeatherData::airquality(); + } + + size_t WeatherService::getTimelineLength() const { + return timeline.size(); + } + + bool WeatherService::addEventToTimeline(std::unique_ptr 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 && header->timestamp + header->expires <= currentTimestamp) { + // TODO: Check if its currently valid + return true; + } + } + return false; + } + + void WeatherService::tidyTimeline() { + uint64_t timeCurrent = 0; + timeline.erase(std::remove_if(std::begin(timeline), + std::end(timeline), + [&](std::unique_ptr const& header) { + return header->timestamp + header->expires > timeCurrent; + }), + std::end(timeline)); + + std::sort(std::begin(timeline), std::end(timeline), compareTimelineEvents); + } + + bool WeatherService::compareTimelineEvents(const std::unique_ptr& first, + const std::unique_ptr& second) { + return first->timestamp > second->timestamp; + } + + uint64_t WeatherService::getCurrentUNIXTimestamp() const { + return std::chrono::duration_cast(dateTimeController.CurrentDateTime().time_since_epoch()).count(); + } + } +} diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h new file mode 100644 index 00000000..ef99db86 --- /dev/null +++ b/src/components/ble/weather/WeatherService.h @@ -0,0 +1,139 @@ +/* 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 . +*/ +#pragma once + +#include +#include +#include +#include + +#define min // workaround: nimble's min/max macros conflict with libstdc++ +#define max +#include +#include +#undef max +#undef min + +#include "WeatherData.h" +#include + +// 00030000-78fc-48fe-8e23-433b3a1942d0 +#define WEATHER_SERVICE_UUID_BASE \ + { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x03, 0x00 } +#define WEATHER_SERVICE_CHAR_UUID(y, x) \ + { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, (x), (y), 0x03, 0x00 } + +int WeatherCallback(uint16_t conn_handle, uint16_t attr_handle, 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 conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt); + + /* + * Helper functions for quick access to currently valid data + */ + WeatherData::location getCurrentLocation() const; + WeatherData::clouds getCurrentClouds() const; + WeatherData::obscuration getCurrentObscuration() const; + WeatherData::precipitation getCurrentPrecipitation() const; + WeatherData::wind getCurrentWind() const; + WeatherData::temperature getCurrentTemperature() const; + WeatherData::humidity getCurrentHumidity() const; + WeatherData::pressure getCurrentPressure() const; + WeatherData::airquality getCurrentQuality() const; + + /* + * Management functions + */ + /** + * Adds an event to the timeline + * @return + */ + bool addEventToTimeline(std::unique_ptr event); + /** + * Gets the current timeline length + */ + size_t getTimelineLength() const; + /** + * Checks if an event of a certain type exists in the timeline + * @return + */ + bool hasTimelineEventOfType(WeatherData::eventtype type) const; + + private: + ble_uuid128_t msUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_UUID_BASE}; + + /** + * Just write timeline data here + */ + ble_uuid128_t wDataCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x01)}; + /** + * This doesn't take timeline data + * but provides some control over it + */ + ble_uuid128_t wControlCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x02)}; + + const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = reinterpret_cast(&wDataCharUuid), + .access_cb = WeatherCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_NOTIFY, + .val_handle = &eventHandle}, + {.uuid = reinterpret_cast(&wControlCharUuid), + .access_cb = WeatherCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}}; + const struct ble_gatt_svc_def serviceDefinition[2] = { + {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = reinterpret_cast(&msUuid), .characteristics = characteristicDefinition}, + {0}}; + + uint16_t eventHandle {}; + + Pinetime::System::SystemTask& system; + Pinetime::Controllers::DateTime& dateTimeController; + + std::vector> timeline; + + /** + * Cleans up the timeline of expired events + * @return result code + */ + void tidyTimeline(); + + /** + * Compares two timeline events + */ + static bool compareTimelineEvents(const std::unique_ptr& first, + const std::unique_ptr& second); + + /** + * + */ + uint64_t getCurrentUNIXTimestamp() const; + }; + } +} diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp new file mode 100644 index 00000000..014761bf --- /dev/null +++ b/src/displayapp/screens/Weather.cpp @@ -0,0 +1,246 @@ +#include "Weather.h" +#include +#include "../DisplayApp.h" +#include "Label.h" +#include "Version.h" +#include "components/battery/BatteryController.h" +#include "components/ble/BleController.h" +#include "components/brightness/BrightnessController.h" +#include "components/datetime/DateTimeController.h" +#include "drivers/Watchdog.h" +#include "components/ble/weather/WeatherData.h" + +using namespace Pinetime::Applications::Screens; + +Weather::Weather(Pinetime::Applications::DisplayApp* app, + Pinetime::Controllers::DateTime& dateTimeController, + Pinetime::Controllers::Battery& batteryController, + Pinetime::Controllers::BrightnessController& brightnessController, + Pinetime::Controllers::Ble& bleController, + Pinetime::Drivers::WatchdogView& watchdog) + : Screen(app), + dateTimeController {dateTimeController}, + batteryController {batteryController}, + brightnessController {brightnessController}, + bleController {bleController}, + watchdog {watchdog}, + screens {app, + 0, + {[this]() -> std::unique_ptr { + return CreateScreen1(); + }, + [this]() -> std::unique_ptr { + return CreateScreen2(); + }, + [this]() -> std::unique_ptr { + return CreateScreen3(); + }, + [this]() -> std::unique_ptr { + return CreateScreen4(); + }, + [this]() -> std::unique_ptr { + return CreateScreen5(); + }}, + Screens::ScreenListModes::UpDown} { +} + +Weather::~Weather() { + lv_obj_clean(lv_scr_act()); +} + +bool Weather::Refresh() { + if (running) { + screens.Refresh(); + } + return running; +} + +bool Weather::OnButtonPushed() { + running = false; + return true; +} + +bool Weather::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + return screens.OnTouchEvent(event); +} + +std::unique_ptr Weather::CreateScreen1() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + lv_label_set_text_fmt(label, + "#FFFF00 InfiniTime#\n\n" + "#444444 Version# %ld.%ld.%ld\n\n" + "#444444 Build date#\n" + "%s\n" + "%s\n", + Version::Major(), + Version::Minor(), + Version::Patch(), + __DATE__, + __TIME__); + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr(new Screens::Label(0, 5, app, label)); +} + +std::unique_ptr Weather::CreateScreen2() { + auto batteryPercent = static_cast(batteryController.PercentRemaining()); + float batteryVoltage = batteryController.Voltage(); + + auto resetReason = [this]() { + switch (watchdog.ResetReason()) { + case Drivers::Watchdog::ResetReasons::Watchdog: + return "wtdg"; + case Drivers::Watchdog::ResetReasons::HardReset: + return "hardr"; + case Drivers::Watchdog::ResetReasons::NFC: + return "nfc"; + case Drivers::Watchdog::ResetReasons::SoftReset: + return "softr"; + case Drivers::Watchdog::ResetReasons::CpuLockup: + return "cpulock"; + case Drivers::Watchdog::ResetReasons::SystemOff: + return "off"; + case Drivers::Watchdog::ResetReasons::LpComp: + return "lpcomp"; + case Drivers::Watchdog::ResetReasons::DebugInterface: + return "dbg"; + case Drivers::Watchdog::ResetReasons::ResetPin: + return "rst"; + default: + return "?"; + } + }(); + + // uptime + static constexpr uint32_t secondsInADay = 60 * 60 * 24; + static constexpr uint32_t secondsInAnHour = 60 * 60; + static constexpr uint32_t secondsInAMinute = 60; + uint32_t uptimeSeconds = dateTimeController.Uptime().count(); + uint32_t uptimeDays = (uptimeSeconds / secondsInADay); + uptimeSeconds = uptimeSeconds % secondsInADay; + uint32_t uptimeHours = uptimeSeconds / secondsInAnHour; + uptimeSeconds = uptimeSeconds % secondsInAnHour; + uint32_t uptimeMinutes = uptimeSeconds / secondsInAMinute; + uptimeSeconds = uptimeSeconds % secondsInAMinute; + // TODO handle more than 100 days of uptime + + if (batteryPercent == -1) + batteryPercent = 0; + + // hack to not use the flot functions from printf + uint8_t batteryVoltageBytes[2]; + batteryVoltageBytes[1] = static_cast(batteryVoltage); // truncate whole numbers + batteryVoltageBytes[0] = + static_cast((batteryVoltage - batteryVoltageBytes[1]) * 100); // remove whole part of flt and shift 2 places over + // + + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + lv_label_set_text_fmt(label, + "#444444 Date# %02d/%02d/%04d\n" + "#444444 Time# %02d:%02d:%02d\n" + "#444444 Uptime#\n %02lud %02lu:%02lu:%02lu\n" + "#444444 Battery# %d%%/%1i.%02iv\n" + "#444444 Backlight# %s\n" + "#444444 Last reset# %s\n", + dateTimeController.Day(), + static_cast(dateTimeController.Month()), + dateTimeController.Year(), + dateTimeController.Hours(), + dateTimeController.Minutes(), + dateTimeController.Seconds(), + uptimeDays, + uptimeHours, + uptimeMinutes, + uptimeSeconds, + batteryPercent, + batteryVoltageBytes[1], + batteryVoltageBytes[0], + brightnessController.ToString(), + resetReason); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr(new Screens::Label(1, 4, app, label)); +} + +std::unique_ptr Weather::CreateScreen3() { + lv_mem_monitor_t mon; + lv_mem_monitor(&mon); + + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + auto& bleAddr = bleController.Address(); + lv_label_set_text_fmt(label, + "#444444 BLE MAC#\n" + " %02x:%02x:%02x:%02x:%02x:%02x" + "\n" + "#444444 Memory#\n" + " #444444 used# %d (%d%%)\n" + " #444444 frag# %d%%\n" + " #444444 free# %d" + "\n" + "#444444 Steps# %li", + bleAddr[5], + bleAddr[4], + bleAddr[3], + bleAddr[2], + bleAddr[1], + bleAddr[0], + (int) mon.total_size - mon.free_size, + mon.used_pct, + mon.frag_pct, + (int) mon.free_biggest_size, + 0); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr(new Screens::Label(2, 5, app, label)); +} + +bool sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs) { + return lhs.xTaskNumber < rhs.xTaskNumber; +} + +std::unique_ptr Weather::CreateScreen4() { + TaskStatus_t tasksStatus[7]; + lv_obj_t* infoTask = lv_table_create(lv_scr_act(), NULL); + lv_table_set_col_cnt(infoTask, 3); + lv_table_set_row_cnt(infoTask, 8); + lv_obj_set_pos(infoTask, 10, 10); + + lv_table_set_cell_value(infoTask, 0, 0, "#"); + lv_table_set_col_width(infoTask, 0, 50); + lv_table_set_cell_value(infoTask, 0, 1, "Task"); + lv_table_set_col_width(infoTask, 1, 80); + lv_table_set_cell_value(infoTask, 0, 2, "Free"); + lv_table_set_col_width(infoTask, 2, 90); + + auto nb = uxTaskGetSystemState(tasksStatus, 7, nullptr); + std::sort(tasksStatus, tasksStatus + nb, sortById); + for (uint8_t i = 0; i < nb; i++) { + + lv_table_set_cell_value(infoTask, i + 1, 0, std::to_string(tasksStatus[i].xTaskNumber).c_str()); + lv_table_set_cell_value(infoTask, i + 1, 1, tasksStatus[i].pcTaskName); + if (tasksStatus[i].usStackHighWaterMark < 20) { + std::string str1 = std::to_string(tasksStatus[i].usStackHighWaterMark) + " low"; + lv_table_set_cell_value(infoTask, i + 1, 2, str1.c_str()); + } else { + lv_table_set_cell_value(infoTask, i + 1, 2, std::to_string(tasksStatus[i].usStackHighWaterMark).c_str()); + } + } + return std::unique_ptr(new Screens::Label(3, 5, app, infoTask)); +} + +std::unique_ptr Weather::CreateScreen5() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + lv_label_set_text_static(label, + "Software Licensed\n" + "under the terms of\n" + "the GNU General\n" + "Public License v3\n" + "#444444 Source code#\n" + "#FFFF00 https://github.com/#\n" + "#FFFF00 JF002/InfiniTime#"); + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr(new Screens::Label(4, 5, app, label)); +} diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h new file mode 100644 index 00000000..8b393ca1 --- /dev/null +++ b/src/displayapp/screens/Weather.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include "Screen.h" +#include "ScreenList.h" + +namespace Pinetime { + namespace Controllers { + class DateTime; + class Battery; + class BrightnessController; + class Ble; + } + + namespace Drivers { + class WatchdogView; + } + + namespace Applications { + class DisplayApp; + + namespace Screens { + class Weather : public Screen { + public: + explicit Weather(DisplayApp* app, + Pinetime::Controllers::DateTime& dateTimeController, + Pinetime::Controllers::Battery& batteryController, + Pinetime::Controllers::BrightnessController& brightnessController, + Pinetime::Controllers::Ble& bleController, + Pinetime::Drivers::WatchdogView& watchdog); + ~Weather() override; + bool Refresh() override; + bool OnButtonPushed() override; + bool OnTouchEvent(TouchEvents event) override; + + private: + bool running = true; + + Pinetime::Controllers::DateTime& dateTimeController; + Pinetime::Controllers::Battery& batteryController; + Pinetime::Controllers::BrightnessController& brightnessController; + Pinetime::Controllers::Ble& bleController; + Pinetime::Drivers::WatchdogView& watchdog; + + ScreenList<5> screens; + std::unique_ptr CreateScreen1(); + std::unique_ptr CreateScreen2(); + std::unique_ptr CreateScreen3(); + std::unique_ptr CreateScreen4(); + std::unique_ptr CreateScreen5(); + }; + } + } +} \ No newline at end of file From 1d3f0dfa9eadf490b6804052ea76e79d29ecda43 Mon Sep 17 00:00:00 2001 From: Avamander Date: Thu, 10 Jun 2021 00:45:39 +0300 Subject: [PATCH 03/41] Tidied up and added Weather to CMakeLists.txt --- src/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ab303d2..fb5e1d1e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -415,6 +415,7 @@ list(APPEND SOURCE_FILES displayapp/screens/Label.cpp displayapp/screens/FirmwareUpdate.cpp displayapp/screens/Music.cpp + displayapp/screens/Weather.cpp displayapp/screens/Navigation.cpp displayapp/screens/Metronome.cpp displayapp/screens/Motion.cpp @@ -479,6 +480,7 @@ list(APPEND SOURCE_FILES components/ble/CurrentTimeService.cpp components/ble/AlertNotificationService.cpp components/ble/MusicService.cpp + components/ble/weather/WeatherService.cpp components/ble/NavigationService.cpp displayapp/fonts/lv_font_navi_80.c components/ble/BatteryInformationService.cpp @@ -653,6 +655,9 @@ set(INCLUDE_FILES components/datetime/DateTimeController.h components/brightness/BrightnessController.h components/motion/MotionController.h + components/firmwarevalidator/FirmwareValidator.h + components/ble/BleController.h + components/ble/NotificationManager.h components/ble/NimbleController.h components/ble/DeviceInformationService.h components/ble/CurrentTimeClient.h @@ -665,6 +670,7 @@ set(INCLUDE_FILES components/ble/BleClient.h components/ble/HeartRateService.h components/ble/MotionService.h + components/ble/weather/WeatherService.h components/settings/Settings.h components/timer/TimerController.h components/alarm/AlarmController.h From eb27813c1839ff2edcce3176e11b1258167af229 Mon Sep 17 00:00:00 2001 From: Avamander Date: Thu, 10 Jun 2021 00:47:11 +0300 Subject: [PATCH 04/41] Removed redundant comments from NrfLogger --- src/logging/NrfLogger.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/logging/NrfLogger.cpp b/src/logging/NrfLogger.cpp index ab54afe9..f8d95a63 100644 --- a/src/logging/NrfLogger.cpp +++ b/src/logging/NrfLogger.cpp @@ -19,14 +19,13 @@ void NrfLogger::Init() { void NrfLogger::Process(void*) { NRF_LOG_INFO("Logger task started!"); -// Suppress endless loop diagnostic + #pragma clang diagnostic push #pragma ide diagnostic ignored "EndlessLoop" while (true) { NRF_LOG_FLUSH(); vTaskDelay(100); // Not good for power consumption, it will wake up every 100ms... } -// Clear diagnostic suppression #pragma clang diagnostic pop } From 6e165848161b72d1afa43af6807c654d3fc23d03 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 16 Jun 2021 23:31:17 +0300 Subject: [PATCH 05/41] Skeleton of the receiving logic --- src/components/ble/weather/WeatherData.h | 37 +++-- src/components/ble/weather/WeatherService.cpp | 139 +++++++++++++----- src/components/ble/weather/WeatherService.h | 45 +++--- 3 files changed, 145 insertions(+), 76 deletions(-) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index c1d53f4e..7cf68418 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -39,7 +39,7 @@ namespace Pinetime { None = 0, /** Water particles suspended in the air; low visibility; does not fall */ Fog = 1, - /** Extremely small, dry particles in the air; invisible to the eye; opalescent */ + /** Tiny, dry particles in the air; invisible to the eye; opalescent */ Haze = 2, /** Small fire-created particles suspended in the air */ Smoke = 3, @@ -51,6 +51,7 @@ namespace Pinetime { Sand = 6, /** Water particles suspended in the air; low-ish visibility; temperature is near dewpoint */ Mist = 7, + Length }; /** @@ -82,7 +83,8 @@ namespace Pinetime { /** Frozen drizzle; very small snow crystals */ SnowGrains = 8, /** Needles; columns or plates of ice. Sometimes described as "diamond dust". In very cold regions */ - IceCrystals = 9 + IceCrystals = 9, + Length }; /** @@ -99,6 +101,7 @@ namespace Pinetime { Fire = 3, /** Thunder and/or lightning */ Thunder = 4, + Length }; /** @@ -111,7 +114,8 @@ namespace Pinetime { /** This wipes the entire timeline */ DelTimeline = 1, /** There's a currently valid timeline event with the given type */ - HasValidEvent = 3 + HasValidEvent = 3, + Length }; /** @@ -137,19 +141,20 @@ namespace Pinetime { Location = 7, /** @see cloud */ Clouds = 8, + Length }; /** * Valid event query */ - class valideventquery { + class ValidEventQuery { public: static constexpr controlcodes code = controlcodes::HasValidEvent; eventtype eventType; }; /** The header used for further parsing */ - class timelineheader { + class TimelineHeader { public: /** UNIX timestamp */ uint64_t timestamp; @@ -168,23 +173,23 @@ namespace Pinetime { }; /** Specifies how cloudiness is stored */ - class clouds : public timelineheader { + class Clouds : public TimelineHeader { public: /** Cloud coverage in percentage, 0-100% */ uint8_t amount; }; /** Specifies how obscuration is stored */ - class obscuration : public timelineheader { + class Obscuration : public TimelineHeader { public: /** Type */ obscurationtype type; /** Visibility distance in meters */ - uint8_t amount; + uint16_t amount; }; /** Specifies how precipitation is stored */ - class precipitation : public timelineheader { + class Precipitation : public TimelineHeader { public: /** Type */ precipitationtype type; @@ -201,7 +206,7 @@ namespace Pinetime { * As direction can fluctuate wildly and some watchfaces might wish to display it nicely, * we're following the aerospace industry weather report option of specifying a range. */ - class wind : public timelineheader { + class Wind : public TimelineHeader { public: /** Meters per second */ uint8_t speedMin; @@ -221,7 +226,7 @@ namespace Pinetime { * * We don't do floats, microdegrees are not useful. Make sure to multiply. */ - class temperature : public timelineheader { + class Temperature : public TimelineHeader { public: /** Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250) */ int16_t temperature; @@ -240,7 +245,7 @@ namespace Pinetime { * or daylight calculations, should those be required. * */ - class location : public timelineheader { + class Location : public TimelineHeader { public: /** Location name */ std::string location; @@ -255,7 +260,7 @@ namespace Pinetime { /** * How humidity is stored */ - class humidity : public timelineheader { + class Humidity : public TimelineHeader { public: /** Relative humidity, 0-100% */ uint8_t humidity; @@ -264,7 +269,7 @@ namespace Pinetime { /** * How air pressure is stored */ - class pressure : public timelineheader { + class Pressure : public TimelineHeader { public: /** Air pressure in hectopascals (hPa) */ int16_t pressure; @@ -273,7 +278,7 @@ namespace Pinetime { /** * How special events are stored */ - class special : public timelineheader { + class Special : public TimelineHeader { public: /** Special event's type */ specialtype type; @@ -288,7 +293,7 @@ namespace Pinetime { * * If this needs further enforced standardization, pull requests are welcome */ - class airquality : public timelineheader { + class AirQuality : public TimelineHeader { public: /** * The name of the pollution diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 006fc6c1..60e608e7 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -40,58 +40,125 @@ namespace Pinetime { } int WeatherService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) { + // TODO: Detect control messages if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { - getCurrentPressure(); - tidyTimeline(); - getTimelineLength(); const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); if (packetLen <= 0) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } // Decode QCBORDecodeContext decodeContext; - UsefulBufC EncodedCBOR; - // TODO: Check uninit fine - QCBORDecode_Init(&decodeContext, EncodedCBOR, QCBOR_DECODE_MODE_NORMAL); + UsefulBufC encodedCbor; + // TODO: Check, uninit fine? + + QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL); QCBORDecode_EnterMap(&decodeContext, nullptr); - WeatherData::timelineheader timelineHeader {}; // Always encodes to the smallest number of bytes based on the value - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", reinterpret_cast(&(timelineHeader.timestamp))); - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", reinterpret_cast(&(timelineHeader.expires))); - QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", reinterpret_cast(&(timelineHeader.eventType))); - switch (timelineHeader.eventType) { - // TODO: Populate + int64_t tmpVersion = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Version", &tmpVersion); + if (tmpVersion != 1) { + // TODO: Return better error? + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + int64_t tmpTimestamp = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp); + int64_t tmpExpires = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires); + if (tmpExpires > 4294967295) { + // TODO: Return better error? + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + int64_t tmpEventType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType); + if (tmpEventType > static_cast(WeatherData::eventtype::Length)) { + // TODO: Return better error? + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + switch (static_cast(tmpEventType)) { + // TODO: Populate case WeatherData::eventtype::AirQuality: { + std::unique_ptr airquality = std::make_unique(); + airquality->timestamp = tmpTimestamp; + airquality->eventType = static_cast(tmpEventType); + airquality->expires = tmpExpires; + + timeline.push_back(std::move(airquality)); break; } case WeatherData::eventtype::Obscuration: { + std::unique_ptr obscuration = std::make_unique(); + obscuration->timestamp = tmpTimestamp; + obscuration->eventType = static_cast(tmpEventType); + obscuration->expires = tmpExpires; + + timeline.push_back(std::move(obscuration)); break; } case WeatherData::eventtype::Precipitation: { + std::unique_ptr precipitation = std::make_unique(); + precipitation->timestamp = tmpTimestamp; + precipitation->eventType = static_cast(tmpEventType); + precipitation->expires = tmpExpires; + timeline.push_back(std::move(precipitation)); break; } case WeatherData::eventtype::Wind: { + std::unique_ptr wind = std::make_unique(); + wind->timestamp = tmpTimestamp; + wind->eventType = static_cast(tmpEventType); + wind->expires = tmpExpires; + timeline.push_back(std::move(wind)); break; } case WeatherData::eventtype::Temperature: { + std::unique_ptr temperature = std::make_unique(); + temperature->timestamp = tmpTimestamp; + temperature->eventType = static_cast(tmpEventType); + temperature->expires = tmpExpires; + timeline.push_back(std::move(temperature)); break; } case WeatherData::eventtype::Special: { + std::unique_ptr special = std::make_unique(); + special->timestamp = tmpTimestamp; + special->eventType = static_cast(tmpEventType); + special->expires = tmpExpires; + timeline.push_back(std::move(special)); break; } case WeatherData::eventtype::Pressure: { + std::unique_ptr pressure = std::make_unique(); + pressure->timestamp = tmpTimestamp; + pressure->eventType = static_cast(tmpEventType); + pressure->expires = tmpExpires; + timeline.push_back(std::move(pressure)); break; } case WeatherData::eventtype::Location: { + std::unique_ptr location = std::make_unique(); + location->timestamp = tmpTimestamp; + location->eventType = static_cast(tmpEventType); + location->expires = tmpExpires; + timeline.push_back(std::move(location)); break; } case WeatherData::eventtype::Clouds: { + std::unique_ptr clouds = std::make_unique(); + clouds->timestamp = tmpTimestamp; + clouds->eventType = static_cast(tmpEventType); + clouds->expires = tmpExpires; + timeline.push_back(std::move(clouds)); break; } default: { break; } } + + getCurrentPressure(); + tidyTimeline(); + getTimelineLength(); QCBORDecode_ExitMap(&decodeContext); auto uErr = QCBORDecode_Finish(&decodeContext); @@ -99,8 +166,6 @@ namespace Pinetime { return BLE_ATT_ERR_INSUFFICIENT_RES; } } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { - // TODO: Detect control messages - // Encode uint8_t buffer[64]; QCBOREncodeContext encodeContext; @@ -125,46 +190,46 @@ namespace Pinetime { return 0; } - WeatherData::location WeatherService::getCurrentLocation() const { - return WeatherData::location(); + WeatherData::Location WeatherService::getCurrentLocation() const { + return WeatherData::Location(); } - WeatherData::clouds WeatherService::getCurrentClouds() const { - return WeatherData::clouds(); + WeatherData::Clouds WeatherService::getCurrentClouds() const { + return WeatherData::Clouds(); } - WeatherData::obscuration WeatherService::getCurrentObscuration() const { - return WeatherData::obscuration(); + WeatherData::Obscuration WeatherService::getCurrentObscuration() const { + return WeatherData::Obscuration(); } - WeatherData::precipitation WeatherService::getCurrentPrecipitation() const { - return WeatherData::precipitation(); + WeatherData::Precipitation WeatherService::getCurrentPrecipitation() const { + return WeatherData::Precipitation(); } - WeatherData::wind WeatherService::getCurrentWind() const { - return WeatherData::wind(); + WeatherData::Wind WeatherService::getCurrentWind() const { + return WeatherData::Wind(); } - WeatherData::temperature WeatherService::getCurrentTemperature() const { - return WeatherData::temperature(); + WeatherData::Temperature WeatherService::getCurrentTemperature() const { + return WeatherData::Temperature(); } - WeatherData::humidity WeatherService::getCurrentHumidity() const { - return WeatherData::humidity(); + WeatherData::Humidity WeatherService::getCurrentHumidity() const { + return WeatherData::Humidity(); } - WeatherData::pressure WeatherService::getCurrentPressure() const { + WeatherData::Pressure WeatherService::getCurrentPressure() const { uint64_t currentTimestamp = getCurrentUNIXTimestamp(); for (auto&& header : timeline) { if (header->eventType == WeatherData::eventtype::Pressure && header->timestamp + header->expires <= currentTimestamp) { - return WeatherData::pressure(); + return WeatherData::Pressure(); } } - return WeatherData::pressure(); + return WeatherData::Pressure(); } - WeatherData::airquality WeatherService::getCurrentQuality() const { - return WeatherData::airquality(); + WeatherData::AirQuality WeatherService::getCurrentQuality() const { + return WeatherData::AirQuality(); } size_t WeatherService::getTimelineLength() const { return timeline.size(); } - bool WeatherService::addEventToTimeline(std::unique_ptr event) { + bool WeatherService::addEventToTimeline(std::unique_ptr event) { if (timeline.size() == timeline.max_size()) { return false; } @@ -188,7 +253,7 @@ namespace Pinetime { uint64_t timeCurrent = 0; timeline.erase(std::remove_if(std::begin(timeline), std::end(timeline), - [&](std::unique_ptr const& header) { + [&](std::unique_ptr const& header) { return header->timestamp + header->expires > timeCurrent; }), std::end(timeline)); @@ -196,8 +261,8 @@ namespace Pinetime { std::sort(std::begin(timeline), std::end(timeline), compareTimelineEvents); } - bool WeatherService::compareTimelineEvents(const std::unique_ptr& first, - const std::unique_ptr& second) { + bool WeatherService::compareTimelineEvents(const std::unique_ptr& first, + const std::unique_ptr& second) { return first->timestamp > second->timestamp; } diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index ef99db86..64a8213a 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -57,15 +57,15 @@ namespace Pinetime { /* * Helper functions for quick access to currently valid data */ - WeatherData::location getCurrentLocation() const; - WeatherData::clouds getCurrentClouds() const; - WeatherData::obscuration getCurrentObscuration() const; - WeatherData::precipitation getCurrentPrecipitation() const; - WeatherData::wind getCurrentWind() const; - WeatherData::temperature getCurrentTemperature() const; - WeatherData::humidity getCurrentHumidity() const; - WeatherData::pressure getCurrentPressure() const; - WeatherData::airquality getCurrentQuality() const; + WeatherData::Location getCurrentLocation() const; + WeatherData::Clouds getCurrentClouds() const; + WeatherData::Obscuration getCurrentObscuration() const; + WeatherData::Precipitation getCurrentPrecipitation() const; + WeatherData::Wind getCurrentWind() const; + WeatherData::Temperature getCurrentTemperature() const; + WeatherData::Humidity getCurrentHumidity() const; + WeatherData::Pressure getCurrentPressure() const; + WeatherData::AirQuality getCurrentQuality() const; /* * Management functions @@ -74,7 +74,7 @@ namespace Pinetime { * Adds an event to the timeline * @return */ - bool addEventToTimeline(std::unique_ptr event); + bool addEventToTimeline(std::unique_ptr event); /** * Gets the current timeline length */ @@ -86,37 +86,36 @@ namespace Pinetime { bool hasTimelineEventOfType(WeatherData::eventtype type) const; private: - ble_uuid128_t msUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_UUID_BASE}; + ble_uuid128_t weatherUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_UUID_BASE}; /** * Just write timeline data here */ - ble_uuid128_t wDataCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x01)}; + ble_uuid128_t weatherDataCharUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x01)}; /** - * This doesn't take timeline data - * but provides some control over it + * This doesn't take timeline data, + * provides some control over it */ - ble_uuid128_t wControlCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x02)}; + ble_uuid128_t weatherControlCharUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x02)}; - const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = reinterpret_cast(&wDataCharUuid), + const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = &weatherDataCharUUID.u, .access_cb = WeatherCallback, .arg = this, .flags = BLE_GATT_CHR_F_NOTIFY, .val_handle = &eventHandle}, - {.uuid = reinterpret_cast(&wControlCharUuid), + {.uuid = &weatherControlCharUUID.u, .access_cb = WeatherCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}}; const struct ble_gatt_svc_def serviceDefinition[2] = { - {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = reinterpret_cast(&msUuid), .characteristics = characteristicDefinition}, - {0}}; + {.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> timeline; + std::vector> timeline; /** * Cleans up the timeline of expired events @@ -127,11 +126,11 @@ namespace Pinetime { /** * Compares two timeline events */ - static bool compareTimelineEvents(const std::unique_ptr& first, - const std::unique_ptr& second); + static bool compareTimelineEvents(const std::unique_ptr& first, + const std::unique_ptr& second); /** - * + * Returns current UNIX timestamp */ uint64_t getCurrentUNIXTimestamp() const; }; From 4349657f799bed04538d95c8d54653586100e82e Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 16 Jun 2021 23:31:40 +0300 Subject: [PATCH 06/41] Minor style improvement --- src/components/ble/AlertNotificationService.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ble/AlertNotificationService.cpp b/src/components/ble/AlertNotificationService.cpp index f616cce8..04819122 100644 --- a/src/components/ble/AlertNotificationService.cpp +++ b/src/components/ble/AlertNotificationService.cpp @@ -53,8 +53,9 @@ int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle // Ignore notifications with empty message const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); - if (packetLen <= headerSize) + if (packetLen <= headerSize) { return 0; + } size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize); auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize)); From 4b2dcbb4f053a89faab50c03083c71fabf9f288a Mon Sep 17 00:00:00 2001 From: Avamander Date: Sun, 20 Jun 2021 21:37:53 +0300 Subject: [PATCH 07/41] Fixed a few bugs, enabled UsefulBuf library optimizations --- src/CMakeLists.txt | 1 + src/components/ble/NimbleController.h | 3 ++ src/components/ble/weather/WeatherData.h | 4 +- src/components/ble/weather/WeatherService.cpp | 19 +++++++-- src/components/ble/weather/WeatherService.h | 40 ++++++++++--------- 5 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fb5e1d1e..4273becf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -860,6 +860,7 @@ target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_EXP_AND_MANTISSA) target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS) target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS) target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_UNCOMMON_TAGS) +target_compile_definitions(QCBOR PUBLIC USEFULBUF_CONFIG_LITTLE_ENDIAN) set_target_properties(QCBOR PROPERTIES LINKER_LANGUAGE C) target_compile_options(QCBOR PRIVATE $<$,$>: ${COMMON_FLAGS} -O0 -g3> diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index a21cbe81..34f00e4e 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -71,6 +71,9 @@ namespace Pinetime { Pinetime::Controllers::AlertNotificationService& alertService() { return anService; }; + Pinetime::Controllers::WeatherService& weather() { + return weatherService; + }; uint16_t connHandle(); void NotifyBatteryLevel(uint8_t level); diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index 7cf68418..ee2a364d 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -248,7 +248,7 @@ namespace Pinetime { class Location : public TimelineHeader { public: /** Location name */ - std::string location; + std::unique_ptr location; /** Altitude relative to sea level in meters */ int16_t altitude; /** Latitude, EPSG:3857 (Google Maps, Openstreetmaps datum) */ @@ -309,7 +309,7 @@ namespace Pinetime { * For chemical compounds use the molecular formula e.g. "NO2", "CO2", "O3" * For pollen use the genus, e.g. "Betula" for birch or "Alternaria" for that mold's spores */ - std::string polluter; + std::unique_ptr polluter; /** * Amount of the pollution in SI units, * otherwise it's going to be difficult to create UI, alerts diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 60e608e7..30d274b2 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -33,7 +33,7 @@ namespace Pinetime { void WeatherService::Init() { uint8_t res = 0; res = ble_gatts_count_cfg(serviceDefinition); - ASSERT(res == 0) + ASSERT(res == 0); res = ble_gatts_add_svcs(serviceDefinition); ASSERT(res == 0); @@ -64,13 +64,13 @@ namespace Pinetime { QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp); int64_t tmpExpires = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires); - if (tmpExpires > 4294967295) { + if (tmpExpires < 0 || tmpExpires > 4294967295) { // TODO: Return better error? return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } int64_t tmpEventType = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType); - if (tmpEventType > static_cast(WeatherData::eventtype::Length)) { + if (tmpEventType < 0 || tmpEventType > static_cast(WeatherData::eventtype::Length)) { // TODO: Return better error? return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } @@ -82,7 +82,18 @@ namespace Pinetime { airquality->timestamp = tmpTimestamp; airquality->eventType = static_cast(tmpEventType); airquality->expires = tmpExpires; - + UsefulBufC String; + QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &String); + if (UsefulBuf_IsNULLOrEmptyC(String) != 0) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + airquality->polluter = std::make_unique(static_cast(String.ptr), String.len); + int64_t tmpAmount = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); + if (tmpAmount < 0 || tmpAmount > 4294967295) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + airquality->amount = tmpAmount; timeline.push_back(std::move(airquality)); break; } diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 64a8213a..43002dc1 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -32,12 +32,6 @@ #include "WeatherData.h" #include -// 00030000-78fc-48fe-8e23-433b3a1942d0 -#define WEATHER_SERVICE_UUID_BASE \ - { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x03, 0x00 } -#define WEATHER_SERVICE_CHAR_UUID(y, x) \ - { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, (x), (y), 0x03, 0x00 } - int WeatherCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg); namespace Pinetime { @@ -86,27 +80,37 @@ namespace Pinetime { bool hasTimelineEventOfType(WeatherData::eventtype type) const; private: - ble_uuid128_t weatherUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_UUID_BASE}; + // 00030000-78fc-48fe-8e23-433b3a1942d0 + static constexpr ble_uuid128_t BaseUUID() { + return CharUUID(0x00, 0x00); + } + + // 0003yyxx-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, x, y, 0x03, 0x00}}; + } + + ble_uuid128_t weatherUUID {BaseUUID()}; /** * Just write timeline data here */ - ble_uuid128_t weatherDataCharUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x01)}; + ble_uuid128_t weatherDataCharUUID {CharUUID(0x00, 0x01)}; /** * This doesn't take timeline data, * provides some control over it */ - ble_uuid128_t weatherControlCharUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x02)}; + ble_uuid128_t weatherControlCharUUID {CharUUID(0x00, 0x02)}; - const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = &weatherDataCharUUID.u, - .access_cb = WeatherCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_NOTIFY, - .val_handle = &eventHandle}, - {.uuid = &weatherControlCharUUID.u, - .access_cb = WeatherCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}}; + 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}}; From b6e9e4171d3ecb417b7fbae61285474036542508 Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 25 Jun 2021 00:39:50 +0300 Subject: [PATCH 08/41] Switched to non-deprecated math header --- src/components/battery/BatteryController.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/battery/BatteryController.cpp b/src/components/battery/BatteryController.cpp index c875cb8d..300d0978 100644 --- a/src/components/battery/BatteryController.cpp +++ b/src/components/battery/BatteryController.cpp @@ -3,6 +3,7 @@ #include #include #include +#include using namespace Pinetime::Controllers; From 3a09b3614c19fda8f90af28b596a6359064ad0fb Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 25 Jun 2021 00:43:30 +0300 Subject: [PATCH 09/41] Brace style and whitespace fixes --- src/CMakeLists.txt | 2 +- src/components/ble/weather/WeatherService.cpp | 2 +- src/components/ble/weather/WeatherService.h | 2 +- src/displayapp/screens/SystemInfo.cpp | 2 +- src/displayapp/screens/Weather.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4273becf..d83c467a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1153,4 +1153,4 @@ elseif (USE_OPENOCD) COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex" ) endif () -endif () +endif () \ No newline at end of file diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 30d274b2..ae7370b5 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -281,4 +281,4 @@ namespace Pinetime { return std::chrono::duration_cast(dateTimeController.CurrentDateTime().time_since_epoch()).count(); } } -} +} \ No newline at end of file diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 43002dc1..53dbebfb 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -139,4 +139,4 @@ namespace Pinetime { uint64_t getCurrentUNIXTimestamp() const; }; } -} +} \ No newline at end of file diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index c363e2dd..07626260 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -274,4 +274,4 @@ std::unique_ptr SystemInfo::CreateScreen5() { lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::make_unique(4, 5, app, label); -} +} \ No newline at end of file diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index 014761bf..0ba53bea 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -243,4 +243,4 @@ std::unique_ptr Weather::CreateScreen5() { lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::unique_ptr(new Screens::Label(4, 5, app, label)); -} +} \ No newline at end of file From 2736fa57bb0fd802222f5989584eac64c371b118 Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 25 Jun 2021 01:10:35 +0300 Subject: [PATCH 10/41] Added autodetection for clang-format version --- src/displayapp/screens/settings/SettingSteps.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/displayapp/screens/settings/SettingSteps.cpp b/src/displayapp/screens/settings/SettingSteps.cpp index 149840df..5ca3eecd 100644 --- a/src/displayapp/screens/settings/SettingSteps.cpp +++ b/src/displayapp/screens/settings/SettingSteps.cpp @@ -6,8 +6,8 @@ using namespace Pinetime::Applications::Screens; namespace { - static void event_handler(lv_obj_t * obj, lv_event_t event) { - SettingSteps* screen = static_cast(obj->user_data); + void event_handler(lv_obj_t* obj, lv_event_t event) { + SettingSteps* screen = static_cast(obj->user_data); screen->UpdateSelected(obj, event); } } @@ -30,33 +30,32 @@ SettingSteps::SettingSteps( lv_obj_set_height(container1, LV_VER_RES - 60); lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); - lv_obj_t * title = lv_label_create(lv_scr_act(), NULL); + lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text_static(title,"Daily steps goal"); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); - lv_obj_t * icon = lv_label_create(lv_scr_act(), NULL); + lv_obj_t* icon = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); lv_label_set_text_static(icon, Symbols::shoe); lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); - - stepValue = lv_label_create(lv_scr_act(), NULL); + stepValue = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); lv_label_set_text_fmt(stepValue, "%lu", settingsController.GetStepsGoal()); lv_label_set_align(stepValue, LV_LABEL_ALIGN_CENTER); lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_CENTER, 0, -10); - btnPlus = lv_btn_create(lv_scr_act(), NULL); + btnPlus = lv_btn_create(lv_scr_act(), nullptr); btnPlus->user_data = this; lv_obj_set_size(btnPlus, 80, 50); lv_obj_align(btnPlus, lv_scr_act(), LV_ALIGN_CENTER, 55, 80); lv_obj_set_style_local_value_str(btnPlus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "+"); lv_obj_set_event_cb(btnPlus, event_handler); - btnMinus = lv_btn_create(lv_scr_act(), NULL); + btnMinus = lv_btn_create(lv_scr_act(), nullptr); btnMinus->user_data = this; lv_obj_set_size(btnMinus, 80, 50); lv_obj_set_event_cb(btnMinus, event_handler); From 19c9667a3d597167241ebcb4dfefb4e0cac068df Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 25 Jun 2021 01:18:56 +0300 Subject: [PATCH 11/41] Started initial work on the UI --- src/components/ble/weather/WeatherService.cpp | 55 ++++---- src/components/ble/weather/WeatherService.h | 34 ++--- src/displayapp/screens/Weather.cpp | 133 +++--------------- src/displayapp/screens/Weather.h | 35 ++--- 4 files changed, 80 insertions(+), 177 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index ae7370b5..7d20867d 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -20,8 +20,8 @@ #include "libs/QCBOR/inc/qcbor/qcbor.h" #include "systemtask/SystemTask.h" -int WeatherCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { - return static_cast(arg)->OnCommand(conn_handle, attr_handle, ctxt); +int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg) { + return static_cast(arg)->OnCommand(connHandle, attrHandle, ctxt); } namespace Pinetime { @@ -39,7 +39,7 @@ namespace Pinetime { ASSERT(res == 0); } - int WeatherService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) { + int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt) { // TODO: Detect control messages if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); @@ -167,9 +167,9 @@ namespace Pinetime { } } - getCurrentPressure(); - tidyTimeline(); - getTimelineLength(); + GetCurrentPressure(); + TidyTimeline(); + GetTimelineLength(); QCBORDecode_ExitMap(&decodeContext); auto uErr = QCBORDecode_Finish(&decodeContext); @@ -201,29 +201,36 @@ namespace Pinetime { return 0; } - WeatherData::Location WeatherService::getCurrentLocation() const { + WeatherData::Location WeatherService::GetCurrentLocation() const { return WeatherData::Location(); } - WeatherData::Clouds WeatherService::getCurrentClouds() const { + + WeatherData::Clouds WeatherService::GetCurrentClouds() const { return WeatherData::Clouds(); } - WeatherData::Obscuration WeatherService::getCurrentObscuration() const { + + WeatherData::Obscuration WeatherService::GetCurrentObscuration() const { return WeatherData::Obscuration(); } - WeatherData::Precipitation WeatherService::getCurrentPrecipitation() const { + + WeatherData::Precipitation WeatherService::GetCurrentPrecipitation() const { return WeatherData::Precipitation(); } - WeatherData::Wind WeatherService::getCurrentWind() const { + + WeatherData::Wind WeatherService::GetCurrentWind() const { return WeatherData::Wind(); } - WeatherData::Temperature WeatherService::getCurrentTemperature() const { + + WeatherData::Temperature WeatherService::GetCurrentTemperature() const { return WeatherData::Temperature(); } - WeatherData::Humidity WeatherService::getCurrentHumidity() const { + + WeatherData::Humidity WeatherService::GetCurrentHumidity() const { return WeatherData::Humidity(); } - WeatherData::Pressure WeatherService::getCurrentPressure() const { - uint64_t currentTimestamp = getCurrentUNIXTimestamp(); + + WeatherData::Pressure WeatherService::GetCurrentPressure() const { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : timeline) { if (header->eventType == WeatherData::eventtype::Pressure && header->timestamp + header->expires <= currentTimestamp) { return WeatherData::Pressure(); @@ -232,15 +239,15 @@ namespace Pinetime { return WeatherData::Pressure(); } - WeatherData::AirQuality WeatherService::getCurrentQuality() const { + WeatherData::AirQuality WeatherService::GetCurrentQuality() const { return WeatherData::AirQuality(); } - size_t WeatherService::getTimelineLength() const { + size_t WeatherService::GetTimelineLength() const { return timeline.size(); } - bool WeatherService::addEventToTimeline(std::unique_ptr event) { + bool WeatherService::AddEventToTimeline(std::unique_ptr event) { if (timeline.size() == timeline.max_size()) { return false; } @@ -249,8 +256,8 @@ namespace Pinetime { return true; } - bool WeatherService::hasTimelineEventOfType(const WeatherData::eventtype type) const { - uint64_t currentTimestamp = getCurrentUNIXTimestamp(); + bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : timeline) { if (header->eventType == type && header->timestamp + header->expires <= currentTimestamp) { // TODO: Check if its currently valid @@ -260,7 +267,7 @@ namespace Pinetime { return false; } - void WeatherService::tidyTimeline() { + void WeatherService::TidyTimeline() { uint64_t timeCurrent = 0; timeline.erase(std::remove_if(std::begin(timeline), std::end(timeline), @@ -269,15 +276,15 @@ namespace Pinetime { }), std::end(timeline)); - std::sort(std::begin(timeline), std::end(timeline), compareTimelineEvents); + std::sort(std::begin(timeline), std::end(timeline), CompareTimelineEvents); } - bool WeatherService::compareTimelineEvents(const std::unique_ptr& first, + bool WeatherService::CompareTimelineEvents(const std::unique_ptr& first, const std::unique_ptr& second) { return first->timestamp > second->timestamp; } - uint64_t WeatherService::getCurrentUNIXTimestamp() const { + uint64_t WeatherService::GetCurrentUnixTimestamp() const { return std::chrono::duration_cast(dateTimeController.CurrentDateTime().time_since_epoch()).count(); } } diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 53dbebfb..786d4715 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -32,7 +32,7 @@ #include "WeatherData.h" #include -int WeatherCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg); +int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg); namespace Pinetime { namespace System { @@ -46,20 +46,20 @@ namespace Pinetime { void Init(); - int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt); + int OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt); /* * Helper functions for quick access to currently valid data */ - WeatherData::Location getCurrentLocation() const; - WeatherData::Clouds getCurrentClouds() const; - WeatherData::Obscuration getCurrentObscuration() const; - WeatherData::Precipitation getCurrentPrecipitation() const; - WeatherData::Wind getCurrentWind() const; - WeatherData::Temperature getCurrentTemperature() const; - WeatherData::Humidity getCurrentHumidity() const; - WeatherData::Pressure getCurrentPressure() const; - WeatherData::AirQuality getCurrentQuality() const; + WeatherData::Location GetCurrentLocation() const; + WeatherData::Clouds GetCurrentClouds() const; + WeatherData::Obscuration GetCurrentObscuration() const; + WeatherData::Precipitation GetCurrentPrecipitation() const; + WeatherData::Wind GetCurrentWind() const; + WeatherData::Temperature GetCurrentTemperature() const; + WeatherData::Humidity GetCurrentHumidity() const; + WeatherData::Pressure GetCurrentPressure() const; + WeatherData::AirQuality GetCurrentQuality() const; /* * Management functions @@ -68,16 +68,16 @@ namespace Pinetime { * Adds an event to the timeline * @return */ - bool addEventToTimeline(std::unique_ptr event); + bool AddEventToTimeline(std::unique_ptr event); /** * Gets the current timeline length */ - size_t getTimelineLength() const; + size_t GetTimelineLength() const; /** * Checks if an event of a certain type exists in the timeline * @return */ - bool hasTimelineEventOfType(WeatherData::eventtype type) const; + bool HasTimelineEventOfType(const WeatherData::eventtype type) const; private: // 00030000-78fc-48fe-8e23-433b3a1942d0 @@ -125,18 +125,18 @@ namespace Pinetime { * Cleans up the timeline of expired events * @return result code */ - void tidyTimeline(); + void TidyTimeline(); /** * Compares two timeline events */ - static bool compareTimelineEvents(const std::unique_ptr& first, + static bool CompareTimelineEvents(const std::unique_ptr& first, const std::unique_ptr& second); /** * Returns current UNIX timestamp */ - uint64_t getCurrentUNIXTimestamp() const; + uint64_t GetCurrentUnixTimestamp() const; }; } } \ No newline at end of file diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index 0ba53bea..a1278649 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -1,5 +1,6 @@ #include "Weather.h" #include +#include #include "../DisplayApp.h" #include "Label.h" #include "Version.h" @@ -12,22 +13,14 @@ using namespace Pinetime::Applications::Screens; -Weather::Weather(Pinetime::Applications::DisplayApp* app, - Pinetime::Controllers::DateTime& dateTimeController, - Pinetime::Controllers::Battery& batteryController, - Pinetime::Controllers::BrightnessController& brightnessController, - Pinetime::Controllers::Ble& bleController, - Pinetime::Drivers::WatchdogView& watchdog) +Weather::Weather(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::WeatherService& weather) : Screen(app), dateTimeController {dateTimeController}, - batteryController {batteryController}, - brightnessController {brightnessController}, - bleController {bleController}, - watchdog {watchdog}, + weatherService(weather), screens {app, 0, {[this]() -> std::unique_ptr { - return CreateScreen1(); + return CreateScreenTemperature(); }, [this]() -> std::unique_ptr { return CreateScreen2(); @@ -64,101 +57,30 @@ bool Weather::OnTouchEvent(Pinetime::Applications::TouchEvents event) { return screens.OnTouchEvent(event); } -std::unique_ptr Weather::CreateScreen1() { +std::unique_ptr Weather::CreateScreenTemperature() { lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(label, true); + Controllers::WeatherData::Temperature current = weatherService.GetCurrentTemperature(); lv_label_set_text_fmt(label, - "#FFFF00 InfiniTime#\n\n" - "#444444 Version# %ld.%ld.%ld\n\n" - "#444444 Build date#\n" - "%s\n" - "%s\n", - Version::Major(), - Version::Minor(), - Version::Patch(), - __DATE__, - __TIME__); + "#FFFF00 Temperature#\n\n" + "#444444 %hd%%#°C \n\n" + "#444444 %hd#\n" + "%llu\n" + "%lu\n", + current.temperature, + current.dewPoint, + current.timestamp, + current.expires); lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::unique_ptr(new Screens::Label(0, 5, app, label)); } std::unique_ptr Weather::CreateScreen2() { - auto batteryPercent = static_cast(batteryController.PercentRemaining()); - float batteryVoltage = batteryController.Voltage(); - - auto resetReason = [this]() { - switch (watchdog.ResetReason()) { - case Drivers::Watchdog::ResetReasons::Watchdog: - return "wtdg"; - case Drivers::Watchdog::ResetReasons::HardReset: - return "hardr"; - case Drivers::Watchdog::ResetReasons::NFC: - return "nfc"; - case Drivers::Watchdog::ResetReasons::SoftReset: - return "softr"; - case Drivers::Watchdog::ResetReasons::CpuLockup: - return "cpulock"; - case Drivers::Watchdog::ResetReasons::SystemOff: - return "off"; - case Drivers::Watchdog::ResetReasons::LpComp: - return "lpcomp"; - case Drivers::Watchdog::ResetReasons::DebugInterface: - return "dbg"; - case Drivers::Watchdog::ResetReasons::ResetPin: - return "rst"; - default: - return "?"; - } - }(); - // uptime - static constexpr uint32_t secondsInADay = 60 * 60 * 24; - static constexpr uint32_t secondsInAnHour = 60 * 60; - static constexpr uint32_t secondsInAMinute = 60; - uint32_t uptimeSeconds = dateTimeController.Uptime().count(); - uint32_t uptimeDays = (uptimeSeconds / secondsInADay); - uptimeSeconds = uptimeSeconds % secondsInADay; - uint32_t uptimeHours = uptimeSeconds / secondsInAnHour; - uptimeSeconds = uptimeSeconds % secondsInAnHour; - uint32_t uptimeMinutes = uptimeSeconds / secondsInAMinute; - uptimeSeconds = uptimeSeconds % secondsInAMinute; - // TODO handle more than 100 days of uptime - - if (batteryPercent == -1) - batteryPercent = 0; - - // hack to not use the flot functions from printf - uint8_t batteryVoltageBytes[2]; - batteryVoltageBytes[1] = static_cast(batteryVoltage); // truncate whole numbers - batteryVoltageBytes[0] = - static_cast((batteryVoltage - batteryVoltageBytes[1]) * 100); // remove whole part of flt and shift 2 places over - // - lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(label, true); - lv_label_set_text_fmt(label, - "#444444 Date# %02d/%02d/%04d\n" - "#444444 Time# %02d:%02d:%02d\n" - "#444444 Uptime#\n %02lud %02lu:%02lu:%02lu\n" - "#444444 Battery# %d%%/%1i.%02iv\n" - "#444444 Backlight# %s\n" - "#444444 Last reset# %s\n", - dateTimeController.Day(), - static_cast(dateTimeController.Month()), - dateTimeController.Year(), - dateTimeController.Hours(), - dateTimeController.Minutes(), - dateTimeController.Seconds(), - uptimeDays, - uptimeHours, - uptimeMinutes, - uptimeSeconds, - batteryPercent, - batteryVoltageBytes[1], - batteryVoltageBytes[0], - brightnessController.ToString(), - resetReason); + lv_label_set_text_fmt(label, "#444444 Date# %02d\n", dateTimeController.Day()); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::unique_ptr(new Screens::Label(1, 4, app, label)); } @@ -169,28 +91,11 @@ std::unique_ptr Weather::CreateScreen3() { lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(label, true); - auto& bleAddr = bleController.Address(); lv_label_set_text_fmt(label, - "#444444 BLE MAC#\n" - " %02x:%02x:%02x:%02x:%02x:%02x" - "\n" - "#444444 Memory#\n" - " #444444 used# %d (%d%%)\n" " #444444 frag# %d%%\n" - " #444444 free# %d" - "\n" - "#444444 Steps# %li", - bleAddr[5], - bleAddr[4], - bleAddr[3], - bleAddr[2], - bleAddr[1], - bleAddr[0], - (int) mon.total_size - mon.free_size, + " #444444 free# %d", mon.used_pct, - mon.frag_pct, - (int) mon.free_biggest_size, - 0); + mon.frag_pct); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::unique_ptr(new Screens::Label(2, 5, app, label)); } @@ -201,7 +106,7 @@ bool sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs) { std::unique_ptr Weather::CreateScreen4() { TaskStatus_t tasksStatus[7]; - lv_obj_t* infoTask = lv_table_create(lv_scr_act(), NULL); + lv_obj_t* infoTask = lv_table_create(lv_scr_act(), nullptr); lv_table_set_col_cnt(infoTask, 3); lv_table_set_row_cnt(infoTask, 8); lv_obj_set_pos(infoTask, 10, 10); diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h index 8b393ca1..469bf592 100644 --- a/src/displayapp/screens/Weather.h +++ b/src/displayapp/screens/Weather.h @@ -1,52 +1,43 @@ #pragma once #include +#include #include "Screen.h" #include "ScreenList.h" namespace Pinetime { - namespace Controllers { - class DateTime; - class Battery; - class BrightnessController; - class Ble; - } - - namespace Drivers { - class WatchdogView; - } - namespace Applications { class DisplayApp; namespace Screens { class Weather : public Screen { public: - explicit Weather(DisplayApp* app, - Pinetime::Controllers::DateTime& dateTimeController, - Pinetime::Controllers::Battery& batteryController, - Pinetime::Controllers::BrightnessController& brightnessController, - Pinetime::Controllers::Ble& bleController, - Pinetime::Drivers::WatchdogView& watchdog); + explicit Weather(DisplayApp* app, Pinetime::Controllers::WeatherService& weather); + ~Weather() override; + bool Refresh() override; + bool OnButtonPushed() override; + bool OnTouchEvent(TouchEvents event) override; private: bool running = true; Pinetime::Controllers::DateTime& dateTimeController; - Pinetime::Controllers::Battery& batteryController; - Pinetime::Controllers::BrightnessController& brightnessController; - Pinetime::Controllers::Ble& bleController; - Pinetime::Drivers::WatchdogView& watchdog; + Controllers::WeatherService& weatherService; ScreenList<5> screens; - std::unique_ptr CreateScreen1(); + + std::unique_ptr CreateScreenTemperature(); + std::unique_ptr CreateScreen2(); + std::unique_ptr CreateScreen3(); + std::unique_ptr CreateScreen4(); + std::unique_ptr CreateScreen5(); }; } From 0ed256ba15ceace2949f21ecbc1407b8553dd75d Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 25 Jun 2021 02:52:59 +0300 Subject: [PATCH 12/41] Few formatting fixes --- src/CMakeLists.txt | 12 ++++++------ src/components/ble/weather/WeatherService.cpp | 2 +- src/components/ble/weather/WeatherService.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d83c467a..f1149ce5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -796,7 +796,7 @@ link_directories( ) -set(COMMON_FLAGS -MP -MD -mthumb -mabi=aapcs -Wall -Wno-unknown-pragmas -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wreturn-type -Werror=return-type -fstack-usage -fno-exceptions -fno-non-call-exceptions) +set(COMMON_FLAGS -MP -MD -mthumb -mabi=aapcs -Wall -Wextra -Warray-bounds=2 -Wformat=2 -Wformat-overflow=2 -Wformat-truncation=2 -Wformat-nonliteral -ftree-vrp -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unknown-pragmas -Wno-expansion-to-defined -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wreturn-type -Werror=return-type -fstack-usage -fno-exceptions -fno-non-call-exceptions) add_definitions(-DCONFIG_GPIO_AS_PINRESET) add_definitions(-DNIMBLE_CFG_CONTROLLER) add_definitions(-DOS_CPUTIME_FREQ) @@ -818,10 +818,10 @@ add_library(nrf-sdk STATIC ${SDK_SOURCE_FILES}) target_include_directories(nrf-sdk SYSTEM PUBLIC . ../) target_include_directories(nrf-sdk SYSTEM PUBLIC ${INCLUDES_FROM_LIBS}) target_compile_options(nrf-sdk PRIVATE - $<$,$>: ${COMMON_FLAGS} -Og -g3> - $<$,$>: ${COMMON_FLAGS} -Os> - $<$,$>: ${COMMON_FLAGS} -Og -fno-rtti> - $<$,$>: ${COMMON_FLAGS} -Os -fno-rtti> + $<$,$>: ${COMMON_FLAGS} -Wno-expansion-to-defined -Og -g3> + $<$,$>: ${COMMON_FLAGS} -Wno-expansion-to-defined -O3> + $<$,$>: ${COMMON_FLAGS} -Wno-expansion-to-defined -Og -fno-rtti> + $<$,$>: ${COMMON_FLAGS} -Wno-expansion-to-defined -O3 -fno-rtti> $<$: -MP -MD -x assembler-with-cpp> ) @@ -1153,4 +1153,4 @@ elseif (USE_OPENOCD) COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex" ) endif () -endif () \ No newline at end of file +endif () diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 7d20867d..a9c9f114 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -288,4 +288,4 @@ namespace Pinetime { return std::chrono::duration_cast(dateTimeController.CurrentDateTime().time_since_epoch()).count(); } } -} \ No newline at end of file +} diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 786d4715..995f856e 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -139,4 +139,4 @@ namespace Pinetime { uint64_t GetCurrentUnixTimestamp() const; }; } -} \ No newline at end of file +} From ed6f0aade4db811b5013441c57944baff4528938 Mon Sep 17 00:00:00 2001 From: Avamander Date: Sat, 21 Aug 2021 21:58:03 +0300 Subject: [PATCH 13/41] Implemented a few functions. --- src/components/ble/weather/WeatherData.h | 6 +- src/components/ble/weather/WeatherService.cpp | 84 +++++++++++++++---- src/components/ble/weather/WeatherService.h | 20 ++--- src/displayapp/screens/Weather.cpp | 16 +--- 4 files changed, 85 insertions(+), 41 deletions(-) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index ee2a364d..9b424004 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -122,7 +122,7 @@ namespace Pinetime { * Events have types * then they're easier to parse after sending them over the air */ - enum class eventtype { + enum class eventtype : uint8_t { /** @see obscuration */ Obscuration = 0, /** @see precipitation */ @@ -141,6 +141,8 @@ namespace Pinetime { Location = 7, /** @see cloud */ Clouds = 8, + /** @see humidity */ + Humidity = 9, Length }; @@ -340,4 +342,4 @@ namespace Pinetime { }; }; } -} \ No newline at end of file +} diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index a9c9f114..22c80837 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -90,7 +90,7 @@ namespace Pinetime { airquality->polluter = std::make_unique(static_cast(String.ptr), String.len); int64_t tmpAmount = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - if (tmpAmount < 0 || tmpAmount > 4294967295) { + if (tmpAmount < 0) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } airquality->amount = tmpAmount; @@ -162,6 +162,14 @@ namespace Pinetime { timeline.push_back(std::move(clouds)); break; } + case WeatherData::eventtype::Humidity: { + std::unique_ptr humidity = std::make_unique(); + humidity->timestamp = tmpTimestamp; + humidity->eventType = static_cast(tmpEventType); + humidity->expires = tmpExpires; + timeline.push_back(std::move(humidity)); + break; + } default: { break; } @@ -201,46 +209,94 @@ namespace Pinetime { return 0; } - WeatherData::Location WeatherService::GetCurrentLocation() const { - return WeatherData::Location(); - } - WeatherData::Clouds WeatherService::GetCurrentClouds() const { - return WeatherData::Clouds(); + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::Clouds && header->timestamp + header->expires <= currentTimestamp) { + return reinterpret_cast(header); + } + } + return {}; } WeatherData::Obscuration WeatherService::GetCurrentObscuration() const { - return WeatherData::Obscuration(); + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::Obscuration && header->timestamp + header->expires <= currentTimestamp) { + return reinterpret_cast(header); + } + } + return {}; } WeatherData::Precipitation WeatherService::GetCurrentPrecipitation() const { - return WeatherData::Precipitation(); + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::Precipitation && header->timestamp + header->expires <= currentTimestamp) { + return reinterpret_cast(header); + } + } + return {}; } WeatherData::Wind WeatherService::GetCurrentWind() const { - return WeatherData::Wind(); + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::Wind && header->timestamp + header->expires <= currentTimestamp) { + return reinterpret_cast(header); + } + } + return {}; } WeatherData::Temperature WeatherService::GetCurrentTemperature() const { - return WeatherData::Temperature(); + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::Temperature && header->timestamp + header->expires <= currentTimestamp) { + return reinterpret_cast(header); + } + } + return {}; } WeatherData::Humidity WeatherService::GetCurrentHumidity() const { - return WeatherData::Humidity(); + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::Humidity && header->timestamp + header->expires <= currentTimestamp) { + return reinterpret_cast(header); + } + } + return {}; } WeatherData::Pressure WeatherService::GetCurrentPressure() const { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : timeline) { if (header->eventType == WeatherData::eventtype::Pressure && header->timestamp + header->expires <= currentTimestamp) { - return WeatherData::Pressure(); + return reinterpret_cast(header); } } - return WeatherData::Pressure(); + return {}; + } + + WeatherData::Location WeatherService::GetCurrentLocation() const { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::Location && header->timestamp + header->expires <= currentTimestamp) { + return reinterpret_cast(header); + } + } + return {}; } WeatherData::AirQuality WeatherService::GetCurrentQuality() const { - return WeatherData::AirQuality(); + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == WeatherData::eventtype::AirQuality && header->timestamp + header->expires <= currentTimestamp) { + return reinterpret_cast(header); + } + } + return {}; } size_t WeatherService::GetTimelineLength() const { diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 995f856e..5504ea49 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -77,42 +77,42 @@ namespace Pinetime { * Checks if an event of a certain type exists in the timeline * @return */ - bool HasTimelineEventOfType(const WeatherData::eventtype type) const; + bool HasTimelineEventOfType(WeatherData::eventtype type) const; private: // 00030000-78fc-48fe-8e23-433b3a1942d0 - static constexpr ble_uuid128_t BaseUUID() { - return CharUUID(0x00, 0x00); + static constexpr ble_uuid128_t BaseUuid() { + return CharUuid(0x00, 0x00); } // 0003yyxx-78fc-48fe-8e23-433b3a1942d0 - static constexpr ble_uuid128_t CharUUID(uint8_t x, uint8_t y) { + 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, x, y, 0x03, 0x00}}; } - ble_uuid128_t weatherUUID {BaseUUID()}; + ble_uuid128_t weatherUuid {BaseUuid()}; /** * Just write timeline data here */ - ble_uuid128_t weatherDataCharUUID {CharUUID(0x00, 0x01)}; + ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)}; /** * This doesn't take timeline data, * provides some control over it */ - ble_uuid128_t weatherControlCharUUID {CharUUID(0x00, 0x02)}; + ble_uuid128_t weatherControlCharUuid {CharUuid(0x00, 0x02)}; const struct ble_gatt_chr_def characteristicDefinition[3] = { - {.uuid = &weatherDataCharUUID.u, + {.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}, + {.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}}; + {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition}, {0}}; uint16_t eventHandle {}; diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index a1278649..ea96c9f2 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -105,7 +105,6 @@ bool sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs) { } std::unique_ptr Weather::CreateScreen4() { - TaskStatus_t tasksStatus[7]; lv_obj_t* infoTask = lv_table_create(lv_scr_act(), nullptr); lv_table_set_col_cnt(infoTask, 3); lv_table_set_row_cnt(infoTask, 8); @@ -118,19 +117,6 @@ std::unique_ptr Weather::CreateScreen4() { lv_table_set_cell_value(infoTask, 0, 2, "Free"); lv_table_set_col_width(infoTask, 2, 90); - auto nb = uxTaskGetSystemState(tasksStatus, 7, nullptr); - std::sort(tasksStatus, tasksStatus + nb, sortById); - for (uint8_t i = 0; i < nb; i++) { - - lv_table_set_cell_value(infoTask, i + 1, 0, std::to_string(tasksStatus[i].xTaskNumber).c_str()); - lv_table_set_cell_value(infoTask, i + 1, 1, tasksStatus[i].pcTaskName); - if (tasksStatus[i].usStackHighWaterMark < 20) { - std::string str1 = std::to_string(tasksStatus[i].usStackHighWaterMark) + " low"; - lv_table_set_cell_value(infoTask, i + 1, 2, str1.c_str()); - } else { - lv_table_set_cell_value(infoTask, i + 1, 2, std::to_string(tasksStatus[i].usStackHighWaterMark).c_str()); - } - } return std::unique_ptr(new Screens::Label(3, 5, app, infoTask)); } @@ -148,4 +134,4 @@ std::unique_ptr Weather::CreateScreen5() { lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::unique_ptr(new Screens::Label(4, 5, app, label)); -} \ No newline at end of file +} From ffb17357e74fd80a0381361a5cbc6bc481d28000 Mon Sep 17 00:00:00 2001 From: Avamander Date: Sun, 28 Nov 2021 15:33:06 +0200 Subject: [PATCH 14/41] Fixed a few compilation errors, fixed UUID. --- src/components/ble/weather/WeatherData.h | 4 +-- src/components/ble/weather/WeatherService.cpp | 2 +- src/components/ble/weather/WeatherService.h | 2 +- src/displayapp/screens/Weather.cpp | 27 +++++++++++++------ src/displayapp/screens/Weather.h | 4 +-- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index 9b424004..19b9709d 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -250,7 +250,7 @@ namespace Pinetime { class Location : public TimelineHeader { public: /** Location name */ - std::unique_ptr location; + std::string location; /** Altitude relative to sea level in meters */ int16_t altitude; /** Latitude, EPSG:3857 (Google Maps, Openstreetmaps datum) */ @@ -311,7 +311,7 @@ namespace Pinetime { * For chemical compounds use the molecular formula e.g. "NO2", "CO2", "O3" * For pollen use the genus, e.g. "Betula" for birch or "Alternaria" for that mold's spores */ - std::unique_ptr polluter; + std::string polluter; /** * Amount of the pollution in SI units, * otherwise it's going to be difficult to create UI, alerts diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 22c80837..bbaa21f0 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -87,7 +87,7 @@ namespace Pinetime { if (UsefulBuf_IsNULLOrEmptyC(String) != 0) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - airquality->polluter = std::make_unique(static_cast(String.ptr), String.len); + airquality->polluter = std::string(static_cast(String.ptr), String.len); int64_t tmpAmount = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); if (tmpAmount < 0) { diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 5504ea49..7bf60ee1 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -88,7 +88,7 @@ namespace Pinetime { // 0003yyxx-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, x, y, 0x03, 0x00}}; + .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, y, x, 0x03, 0x00}}; } ble_uuid128_t weatherUuid {BaseUuid()}; diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index ea96c9f2..025a3bd8 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -1,14 +1,26 @@ +/* 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 . +*/ #include "Weather.h" #include #include -#include "../DisplayApp.h" #include "Label.h" -#include "Version.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" -#include "components/brightness/BrightnessController.h" -#include "components/datetime/DateTimeController.h" -#include "drivers/Watchdog.h" #include "components/ble/weather/WeatherData.h" using namespace Pinetime::Applications::Screens; @@ -41,11 +53,10 @@ Weather::~Weather() { lv_obj_clean(lv_scr_act()); } -bool Weather::Refresh() { +void Weather::Refresh() { if (running) { - screens.Refresh(); + // screens.Refresh(); } - return running; } bool Weather::OnButtonPushed() { diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h index 469bf592..99cf15ba 100644 --- a/src/displayapp/screens/Weather.h +++ b/src/displayapp/screens/Weather.h @@ -16,7 +16,7 @@ namespace Pinetime { ~Weather() override; - bool Refresh() override; + void Refresh() override; bool OnButtonPushed() override; @@ -42,4 +42,4 @@ namespace Pinetime { }; } } -} \ No newline at end of file +} From 657dc3a9ba21039cc9abd3c40c68aa3e810bd074 Mon Sep 17 00:00:00 2001 From: Avamander Date: Sun, 28 Nov 2021 19:12:16 +0200 Subject: [PATCH 15/41] Changed UUID so it wouldn't conflict with Motion --- src/components/ble/weather/WeatherService.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 7bf60ee1..43b2ee28 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -80,15 +80,15 @@ namespace Pinetime { bool HasTimelineEventOfType(WeatherData::eventtype type) const; private: - // 00030000-78fc-48fe-8e23-433b3a1942d0 + // 00040000-78fc-48fe-8e23-433b3a1942d0 static constexpr ble_uuid128_t BaseUuid() { return CharUuid(0x00, 0x00); } - // 0003yyxx-78fc-48fe-8e23-433b3a1942d0 + // 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, 0x03, 0x00}}; + .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, y, x, 0x04, 0x00}}; } ble_uuid128_t weatherUuid {BaseUuid()}; From 900598a7eeff1a84ccf7d0d7c94ac28a780299d4 Mon Sep 17 00:00:00 2001 From: Avamander Date: Sun, 28 Nov 2021 19:13:03 +0200 Subject: [PATCH 16/41] Removed versioning because it's not necessary --- src/components/ble/weather/WeatherService.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index bbaa21f0..135f64dd 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -48,18 +48,11 @@ namespace Pinetime { } // Decode QCBORDecodeContext decodeContext; - UsefulBufC encodedCbor; - // TODO: Check, uninit fine? + UsefulBufC encodedCbor = {ctxt->om, OS_MBUF_PKTLEN(ctxt->om)}; QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL); QCBORDecode_EnterMap(&decodeContext, nullptr); // Always encodes to the smallest number of bytes based on the value - int64_t tmpVersion = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Version", &tmpVersion); - if (tmpVersion != 1) { - // TODO: Return better error? - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } int64_t tmpTimestamp = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp); int64_t tmpExpires = 0; From df04763ab41aad6be8697c377e4b570c3f2fd238 Mon Sep 17 00:00:00 2001 From: Avamander Date: Sun, 28 Nov 2021 19:13:28 +0200 Subject: [PATCH 17/41] Fixed recovery build --- src/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f1149ce5..cbccb714 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -552,6 +552,7 @@ list(APPEND RECOVERY_SOURCE_FILES components/ble/CurrentTimeService.cpp components/ble/AlertNotificationService.cpp components/ble/MusicService.cpp + components/ble/weather/WeatherService.cpp components/ble/BatteryInformationService.cpp components/ble/ImmediateAlertService.cpp components/ble/ServiceDiscovery.cpp @@ -951,7 +952,7 @@ endif() set(EXECUTABLE_RECOVERY_NAME "pinetime-recovery") set(EXECUTABLE_RECOVERY_FILE_NAME ${EXECUTABLE_RECOVERY_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}) add_executable(${EXECUTABLE_RECOVERY_NAME} ${RECOVERY_SOURCE_FILES}) -target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs) +target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs QCBOR) set_target_properties(${EXECUTABLE_RECOVERY_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_FILE_NAME}) target_compile_definitions(${EXECUTABLE_RECOVERY_NAME} PUBLIC "PINETIME_IS_RECOVERY") target_compile_options(${EXECUTABLE_RECOVERY_NAME} PUBLIC From 48beb7c3b18bcfdc369a63be5923a35a2113aa36 Mon Sep 17 00:00:00 2001 From: Avamander Date: Sun, 28 Nov 2021 20:58:28 +0200 Subject: [PATCH 18/41] Improved error handling and fixed incompatibility with co.nstant.in:cbor library --- src/CMakeLists.txt | 10 +++++----- src/components/ble/weather/WeatherService.cpp | 9 ++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cbccb714..9f3b6d4e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -859,7 +859,7 @@ target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_FLOAT_HW_USE) target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_PREFERRED_FLOAT) target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_EXP_AND_MANTISSA) target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS) -target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS) +#target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS) target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_UNCOMMON_TAGS) target_compile_definitions(QCBOR PUBLIC USEFULBUF_CONFIG_LITTLE_ENDIAN) set_target_properties(QCBOR PROPERTIES LINKER_LANGUAGE C) @@ -889,10 +889,10 @@ add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES}) set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME}) target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs QCBOR) target_compile_options(${EXECUTABLE_NAME} PUBLIC - $<$,$>: ${COMMON_FLAGS} -Og -g3> - $<$,$>: ${COMMON_FLAGS} -Os> - $<$,$>: ${COMMON_FLAGS} -Og -g3 -fno-rtti> - $<$,$>: ${COMMON_FLAGS} -Os -fno-rtti> + $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Og -g3> + $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Os> + $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Og -g3 -fno-rtti> + $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Os -fno-rtti> $<$: -MP -MD -x assembler-with-cpp> ) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 135f64dd..42302610 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -48,13 +48,17 @@ namespace Pinetime { } // Decode QCBORDecodeContext decodeContext; - UsefulBufC encodedCbor = {ctxt->om, OS_MBUF_PKTLEN(ctxt->om)}; + UsefulBufC encodedCbor = {ctxt->om->om_data, OS_MBUF_PKTLEN(ctxt->om)}; 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) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } int64_t tmpExpires = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires); if (tmpExpires < 0 || tmpExpires > 4294967295) { @@ -173,8 +177,7 @@ namespace Pinetime { GetTimelineLength(); QCBORDecode_ExitMap(&decodeContext); - auto uErr = QCBORDecode_Finish(&decodeContext); - if (uErr != 0) { + if (QCBORDecode_Finish(&decodeContext) != QCBOR_SUCCESS) { return BLE_ATT_ERR_INSUFFICIENT_RES; } } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { From c870f8ed302823e12018aa196d87937c92966d06 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 00:45:28 +0200 Subject: [PATCH 19/41] Bunch of bugs fixed, improved error handling, debug UI addition --- src/components/ble/weather/WeatherService.cpp | 127 +++++++------ src/components/ble/weather/WeatherService.h | 31 ++-- src/displayapp/Apps.h | 1 + src/displayapp/screens/Weather.cpp | 168 +++++++++++------- src/displayapp/screens/Weather.h | 8 +- 5 files changed, 194 insertions(+), 141 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 42302610..4ec57d00 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -28,6 +28,7 @@ namespace Pinetime { namespace Controllers { WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController) : system(system), dateTimeController(dateTimeController) { + nullHeader = &nullTimelineheader; } void WeatherService::Init() { @@ -42,7 +43,7 @@ namespace Pinetime { int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt) { // TODO: Detect control messages if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { - const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); + const uint8_t packetLen = OS_MBUF_PKTLEN(ctxt->om); if (packetLen <= 0) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } @@ -56,30 +57,28 @@ namespace Pinetime { // 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) { + uint8_t err = QCBORDecode_GetError(&decodeContext); + if (err != QCBOR_SUCCESS) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } int64_t tmpExpires = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires); if (tmpExpires < 0 || tmpExpires > 4294967295) { - // TODO: Return better error? return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } int64_t tmpEventType = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType); if (tmpEventType < 0 || tmpEventType > static_cast(WeatherData::eventtype::Length)) { - // TODO: Return better error? return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } switch (static_cast(tmpEventType)) { - // TODO: Populate case WeatherData::eventtype::AirQuality: { std::unique_ptr airquality = std::make_unique(); airquality->timestamp = tmpTimestamp; airquality->eventType = static_cast(tmpEventType); airquality->expires = tmpExpires; - UsefulBufC String; + UsefulBufC String; // TODO: Everything ok with lifecycle here? QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &String); if (UsefulBuf_IsNULLOrEmptyC(String) != 0) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -172,10 +171,9 @@ namespace Pinetime { } } - GetCurrentPressure(); - TidyTimeline(); - GetTimelineLength(); QCBORDecode_ExitMap(&decodeContext); + GetTimelineLength(); + TidyTimeline(); if (QCBORDecode_Finish(&decodeContext) != QCBOR_SUCCESS) { return BLE_ATT_ERR_INSUFFICIENT_RES; @@ -205,94 +203,103 @@ namespace Pinetime { return 0; } - WeatherData::Clouds WeatherService::GetCurrentClouds() const { + std::unique_ptr& WeatherService::GetCurrentClouds() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::Clouds && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Clouds && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } - WeatherData::Obscuration WeatherService::GetCurrentObscuration() const { + std::unique_ptr& WeatherService::GetCurrentObscuration() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::Obscuration && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Obscuration && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } - WeatherData::Precipitation WeatherService::GetCurrentPrecipitation() const { + std::unique_ptr& WeatherService::GetCurrentPrecipitation() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::Precipitation && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Precipitation && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } - WeatherData::Wind WeatherService::GetCurrentWind() const { + std::unique_ptr& WeatherService::GetCurrentWind() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::Wind && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Wind && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } - WeatherData::Temperature WeatherService::GetCurrentTemperature() const { + std::unique_ptr& WeatherService::GetCurrentTemperature() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::Temperature && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Temperature && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } - WeatherData::Humidity WeatherService::GetCurrentHumidity() const { + std::unique_ptr& WeatherService::GetCurrentHumidity() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::Humidity && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Humidity && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } - WeatherData::Pressure WeatherService::GetCurrentPressure() const { + std::unique_ptr& WeatherService::GetCurrentPressure() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::Pressure && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Pressure && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } - WeatherData::Location WeatherService::GetCurrentLocation() const { + std::unique_ptr& WeatherService::GetCurrentLocation() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::Location && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Location && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } - WeatherData::AirQuality WeatherService::GetCurrentQuality() const { + std::unique_ptr& WeatherService::GetCurrentQuality() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == WeatherData::eventtype::AirQuality && header->timestamp + header->expires <= currentTimestamp) { - return reinterpret_cast(header); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::AirQuality && isEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); } } - return {}; + + return reinterpret_cast&>(this->nullHeader); } size_t WeatherService::GetTimelineLength() const { @@ -311,8 +318,7 @@ namespace Pinetime { bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : timeline) { - if (header->eventType == type && header->timestamp + header->expires <= currentTimestamp) { - // TODO: Check if its currently valid + if (header->eventType == type && isEventStillValid(header, currentTimestamp)) { return true; } } @@ -320,11 +326,11 @@ namespace Pinetime { } void WeatherService::TidyTimeline() { - uint64_t timeCurrent = 0; + uint64_t timeCurrent = GetCurrentUnixTimestamp(); timeline.erase(std::remove_if(std::begin(timeline), std::end(timeline), [&](std::unique_ptr const& header) { - return header->timestamp + header->expires > timeCurrent; + return isEventStillValid(header, timeCurrent); }), std::end(timeline)); @@ -336,6 +342,11 @@ namespace Pinetime { return first->timestamp > second->timestamp; } + bool WeatherService::isEventStillValid(const std::unique_ptr& header, const uint64_t currentTimestamp) { + // Not getting timestamp in isEventStillValid for more speed + return header->timestamp + header->expires <= currentTimestamp; + } + uint64_t WeatherService::GetCurrentUnixTimestamp() const { return std::chrono::duration_cast(dateTimeController.CurrentDateTime().time_since_epoch()).count(); } diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 43b2ee28..7accc49e 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -51,15 +51,15 @@ namespace Pinetime { /* * Helper functions for quick access to currently valid data */ - WeatherData::Location GetCurrentLocation() const; - WeatherData::Clouds GetCurrentClouds() const; - WeatherData::Obscuration GetCurrentObscuration() const; - WeatherData::Precipitation GetCurrentPrecipitation() const; - WeatherData::Wind GetCurrentWind() const; - WeatherData::Temperature GetCurrentTemperature() const; - WeatherData::Humidity GetCurrentHumidity() const; - WeatherData::Pressure GetCurrentPressure() const; - WeatherData::AirQuality GetCurrentQuality() const; + std::unique_ptr& GetCurrentLocation(); + std::unique_ptr& GetCurrentClouds(); + std::unique_ptr& GetCurrentObscuration(); + std::unique_ptr& GetCurrentPrecipitation(); + std::unique_ptr& GetCurrentWind(); + std::unique_ptr& GetCurrentTemperature(); + std::unique_ptr& GetCurrentHumidity(); + std::unique_ptr& GetCurrentPressure(); + std::unique_ptr& GetCurrentQuality(); /* * Management functions @@ -123,7 +123,6 @@ namespace Pinetime { /** * Cleans up the timeline of expired events - * @return result code */ void TidyTimeline(); @@ -137,6 +136,18 @@ namespace Pinetime { * 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& uniquePtr, const uint64_t timestamp); + + std::unique_ptr nullTimelineheader = std::make_unique(); + std::unique_ptr* nullHeader; }; } } diff --git a/src/displayapp/Apps.h b/src/displayapp/Apps.h index d340efee..1cf7e2a8 100644 --- a/src/displayapp/Apps.h +++ b/src/displayapp/Apps.h @@ -25,6 +25,7 @@ namespace Pinetime { Metronome, Motion, Steps, + Weather, QuickSettings, Settings, SettingWatchFace, diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index 025a3bd8..132bee71 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -35,16 +35,16 @@ Weather::Weather(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers: return CreateScreenTemperature(); }, [this]() -> std::unique_ptr { - return CreateScreen2(); + return CreateScreenAir(); }, [this]() -> std::unique_ptr { - return CreateScreen3(); + return CreateScreenClouds(); }, [this]() -> std::unique_ptr { - return CreateScreen4(); + return CreateScreenPrecipitation(); }, [this]() -> std::unique_ptr { - return CreateScreen5(); + return CreateScreenHumidity(); }}, Screens::ScreenListModes::UpDown} { } @@ -71,78 +71,108 @@ bool Weather::OnTouchEvent(Pinetime::Applications::TouchEvents event) { std::unique_ptr Weather::CreateScreenTemperature() { lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(label, true); - Controllers::WeatherData::Temperature current = weatherService.GetCurrentTemperature(); - lv_label_set_text_fmt(label, - "#FFFF00 Temperature#\n\n" - "#444444 %hd%%#°C \n\n" - "#444444 %hd#\n" - "%llu\n" - "%lu\n", - current.temperature, - current.dewPoint, - current.timestamp, - current.expires); + std::unique_ptr& current = weatherService.GetCurrentTemperature(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Temperature#\n\n" + "#444444 %hd%%#°C \n\n" + "#444444 %hd#\n\n" + "%llu\n" + "%lu\n", + current->temperature, + current->dewPoint, + current->timestamp, + current->expires); + } lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::unique_ptr(new Screens::Label(0, 5, app, label)); } -std::unique_ptr Weather::CreateScreen2() { - // uptime +std::unique_ptr Weather::CreateScreenAir() { lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(label, true); - lv_label_set_text_fmt(label, "#444444 Date# %02d\n", dateTimeController.Day()); - lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - return std::unique_ptr(new Screens::Label(1, 4, app, label)); -} - -std::unique_ptr Weather::CreateScreen3() { - lv_mem_monitor_t mon; - lv_mem_monitor(&mon); - - lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_recolor(label, true); - lv_label_set_text_fmt(label, - " #444444 frag# %d%%\n" - " #444444 free# %d", - mon.used_pct, - mon.frag_pct); - lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - return std::unique_ptr(new Screens::Label(2, 5, app, label)); -} - -bool sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs) { - return lhs.xTaskNumber < rhs.xTaskNumber; -} - -std::unique_ptr Weather::CreateScreen4() { - lv_obj_t* infoTask = lv_table_create(lv_scr_act(), nullptr); - lv_table_set_col_cnt(infoTask, 3); - lv_table_set_row_cnt(infoTask, 8); - lv_obj_set_pos(infoTask, 10, 10); - - lv_table_set_cell_value(infoTask, 0, 0, "#"); - lv_table_set_col_width(infoTask, 0, 50); - lv_table_set_cell_value(infoTask, 0, 1, "Task"); - lv_table_set_col_width(infoTask, 1, 80); - lv_table_set_cell_value(infoTask, 0, 2, "Free"); - lv_table_set_col_width(infoTask, 2, 90); - - return std::unique_ptr(new Screens::Label(3, 5, app, infoTask)); -} - -std::unique_ptr Weather::CreateScreen5() { - lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_recolor(label, true); - lv_label_set_text_static(label, - "Software Licensed\n" - "under the terms of\n" - "the GNU General\n" - "Public License v3\n" - "#444444 Source code#\n" - "#FFFF00 https://github.com/#\n" - "#FFFF00 JF002/InfiniTime#"); + std::unique_ptr& current = weatherService.GetCurrentQuality(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Air quality#\n\n" + "#444444 %s#\n" + "#444444 %lu#\n\n" + "%llu\n" + "%lu\n", + current->polluter.c_str(), + current->amount, + current->timestamp, + current->expires); + } lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - return std::unique_ptr(new Screens::Label(4, 5, app, label)); + return std::unique_ptr(new Screens::Label(0, 5, app, label)); +} + +std::unique_ptr Weather::CreateScreenClouds() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr& current = weatherService.GetCurrentClouds(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Clouds#\n\n" + "#444444 %hhu%%#\n\n" + "%llu\n" + "%lu\n", + current->amount, + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr(new Screens::Label(0, 5, app, label)); +} + +std::unique_ptr Weather::CreateScreenPrecipitation() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr& current = weatherService.GetCurrentPrecipitation(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Precipitation#\n\n" + "#444444 %hhu%%#\n\n" + "%llu\n" + "%lu\n", + current->amount, + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr(new Screens::Label(0, 5, app, label)); +} + +std::unique_ptr Weather::CreateScreenHumidity() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr& current = weatherService.GetCurrentHumidity(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Humidity#\n\n" + "#444444 %hhu%%#\n\n" + "%llu\n" + "%lu\n", + current->humidity, + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr(new Screens::Label(0, 5, app, label)); } diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h index 99cf15ba..34f95fce 100644 --- a/src/displayapp/screens/Weather.h +++ b/src/displayapp/screens/Weather.h @@ -32,13 +32,13 @@ namespace Pinetime { std::unique_ptr CreateScreenTemperature(); - std::unique_ptr CreateScreen2(); + std::unique_ptr CreateScreenAir(); - std::unique_ptr CreateScreen3(); + std::unique_ptr CreateScreenClouds(); - std::unique_ptr CreateScreen4(); + std::unique_ptr CreateScreenPrecipitation(); - std::unique_ptr CreateScreen5(); + std::unique_ptr CreateScreenHumidity(); }; } } From 06b022fc4dd6c2b1e5145e111f5c1f32e4729eab Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 01:15:11 +0200 Subject: [PATCH 20/41] Improved UI and fixed a bug --- src/components/ble/weather/WeatherService.cpp | 21 ++++++++++--------- src/displayapp/screens/Weather.cpp | 20 ++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 4ec57d00..250b36ab 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -29,6 +29,7 @@ namespace Pinetime { WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController) : system(system), dateTimeController(dateTimeController) { nullHeader = &nullTimelineheader; + nullTimelineheader->timestamp = 0; } void WeatherService::Init() { @@ -211,7 +212,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } std::unique_ptr& WeatherService::GetCurrentObscuration() { @@ -222,7 +223,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } std::unique_ptr& WeatherService::GetCurrentPrecipitation() { @@ -233,7 +234,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } std::unique_ptr& WeatherService::GetCurrentWind() { @@ -244,7 +245,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } std::unique_ptr& WeatherService::GetCurrentTemperature() { @@ -255,7 +256,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } std::unique_ptr& WeatherService::GetCurrentHumidity() { @@ -266,7 +267,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } std::unique_ptr& WeatherService::GetCurrentPressure() { @@ -277,7 +278,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } std::unique_ptr& WeatherService::GetCurrentLocation() { @@ -288,7 +289,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } std::unique_ptr& WeatherService::GetCurrentQuality() { @@ -299,7 +300,7 @@ namespace Pinetime { } } - return reinterpret_cast&>(this->nullHeader); + return reinterpret_cast&>(*this->nullHeader); } size_t WeatherService::GetTimelineLength() const { @@ -330,7 +331,7 @@ namespace Pinetime { timeline.erase(std::remove_if(std::begin(timeline), std::end(timeline), [&](std::unique_ptr const& header) { - return isEventStillValid(header, timeCurrent); + return !isEventStillValid(header, timeCurrent); }), std::end(timeline)); diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index 132bee71..0854c74a 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -74,6 +74,16 @@ std::unique_ptr Weather::CreateScreenTemperature() { std::unique_ptr& current = weatherService.GetCurrentTemperature(); if (current->timestamp == 0) { // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Temperature#\n\n" + "#444444 %d#\n\n" + "#444444 %d#\n\n" + "%d\n" + "%d\n", + 0, + 0, + 0, + 0); } else { lv_label_set_text_fmt(label, "#FFFF00 Temperature#\n\n" @@ -97,6 +107,16 @@ std::unique_ptr Weather::CreateScreenAir() { std::unique_ptr& current = weatherService.GetCurrentQuality(); if (current->timestamp == 0) { // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Air quality#\n\n" + "#444444 %s#\n" + "#444444 %d#\n\n" + "%d\n" + "%d\n", + "", + 0, + 0, + 0); } else { lv_label_set_text_fmt(label, "#FFFF00 Air quality#\n\n" From 9108952e6ba96f94bbd1530036318c9b21ec0cf9 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 15:18:34 +0200 Subject: [PATCH 21/41] Implemented parsing of all defined weather data types --- src/components/ble/weather/WeatherService.cpp | 200 ++++++++++++++++-- 1 file changed, 181 insertions(+), 19 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 250b36ab..c60e0f09 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -42,15 +42,14 @@ namespace Pinetime { } int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt) { - // TODO: Detect control messages if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { - const uint8_t packetLen = OS_MBUF_PKTLEN(ctxt->om); + 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)}; + 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 @@ -58,18 +57,18 @@ namespace Pinetime { // Always encodes to the smallest number of bytes based on the value int64_t tmpTimestamp = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp); - uint8_t err = QCBORDecode_GetError(&decodeContext); - if (err != QCBOR_SUCCESS) { + if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } int64_t tmpExpires = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires); - if (tmpExpires < 0 || tmpExpires > 4294967295) { + if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpExpires < 0 || tmpExpires > 4294967295) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } int64_t tmpEventType = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType); - if (tmpEventType < 0 || tmpEventType > static_cast(WeatherData::eventtype::Length)) { + if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpEventType < 0 || + tmpEventType >= static_cast(WeatherData::eventtype::Length)) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } @@ -79,19 +78,24 @@ namespace Pinetime { airquality->timestamp = tmpTimestamp; airquality->eventType = static_cast(tmpEventType); airquality->expires = tmpExpires; + UsefulBufC String; // TODO: Everything ok with lifecycle here? QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &String); if (UsefulBuf_IsNULLOrEmptyC(String) != 0) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } airquality->polluter = std::string(static_cast(String.ptr), String.len); + int64_t tmpAmount = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - if (tmpAmount < 0) { + if (tmpAmount < 0 || tmpAmount > 4294967295) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + airquality->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (AddEventToTimeline(std::move(airquality))) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - airquality->amount = tmpAmount; - timeline.push_back(std::move(airquality)); break; } case WeatherData::eventtype::Obscuration: { @@ -100,7 +104,23 @@ namespace Pinetime { obscuration->eventType = static_cast(tmpEventType); obscuration->expires = tmpExpires; - timeline.push_back(std::move(obscuration)); + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); + if (tmpType < 0 || tmpType >= static_cast(WeatherData::obscurationtype::Length)) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + obscuration->type = static_cast(tmpType); + + int64_t tmpAmount = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); + if (tmpAmount < 0 || tmpAmount > 65535) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + obscuration->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (AddEventToTimeline(std::move(obscuration))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } case WeatherData::eventtype::Precipitation: { @@ -108,7 +128,24 @@ namespace Pinetime { precipitation->timestamp = tmpTimestamp; precipitation->eventType = static_cast(tmpEventType); precipitation->expires = tmpExpires; - timeline.push_back(std::move(precipitation)); + + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); + if (tmpType < 0 || tmpType >= static_cast(WeatherData::precipitationtype::Length)) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + precipitation->type = static_cast(tmpType); + + int64_t tmpAmount = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); + if (tmpAmount < 0 || tmpAmount > 255) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + precipitation->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (AddEventToTimeline(std::move(precipitation))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } case WeatherData::eventtype::Wind: { @@ -116,7 +153,38 @@ namespace Pinetime { wind->timestamp = tmpTimestamp; wind->eventType = static_cast(tmpEventType); wind->expires = tmpExpires; - timeline.push_back(std::move(wind)); + + int64_t tmpMin = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMin); + if (tmpMin < 0 || tmpMin > 255) { + 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) { + 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) { + 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) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + wind->directionMax = tmpDMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (AddEventToTimeline(std::move(wind))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } case WeatherData::eventtype::Temperature: { @@ -124,7 +192,24 @@ namespace Pinetime { temperature->timestamp = tmpTimestamp; temperature->eventType = static_cast(tmpEventType); temperature->expires = tmpExpires; - timeline.push_back(std::move(temperature)); + + int64_t tmpTemperature = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Temperature", &tmpTemperature); + if (tmpTemperature < 0 || tmpTemperature > 65535) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + temperature->temperature = tmpTemperature; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + int64_t tmpDewPoint = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint); + if (tmpDewPoint < 0 || tmpDewPoint > 65535) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + temperature->dewPoint = tmpDewPoint; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (AddEventToTimeline(std::move(temperature))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } case WeatherData::eventtype::Special: { @@ -132,7 +217,17 @@ namespace Pinetime { special->timestamp = tmpTimestamp; special->eventType = static_cast(tmpEventType); special->expires = tmpExpires; - timeline.push_back(std::move(special)); + + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpType); + if (tmpType < 0 || tmpType >= static_cast(WeatherData::specialtype::Length)) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + special->type = static_cast(tmpType); + + if (AddEventToTimeline(std::move(special))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } case WeatherData::eventtype::Pressure: { @@ -140,7 +235,17 @@ namespace Pinetime { pressure->timestamp = tmpTimestamp; pressure->eventType = static_cast(tmpEventType); pressure->expires = tmpExpires; - timeline.push_back(std::move(pressure)); + + int64_t tmpDewPoint = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint); + if (tmpDewPoint < 0 || tmpDewPoint >= 65535) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + pressure->pressure = tmpDewPoint; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (AddEventToTimeline(std::move(pressure))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } case WeatherData::eventtype::Location: { @@ -148,7 +253,38 @@ namespace Pinetime { location->timestamp = tmpTimestamp; location->eventType = static_cast(tmpEventType); location->expires = tmpExpires; - timeline.push_back(std::move(location)); + + UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here? + QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Location", &stringBuf); + if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + location->location = std::string(static_cast(stringBuf.ptr), stringBuf.len); + + int64_t tmpAltitude = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Altitude", &tmpAltitude); + if (tmpAltitude < -32768 || tmpAltitude >= 32767) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + location->altitude = static_cast(tmpAltitude); + + int64_t tmpLatitude = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Latitude", &tmpLatitude); + if (tmpLatitude < -2147483648 || tmpLatitude >= 2147483647) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + location->latitude = static_cast(tmpLatitude); + + int64_t tmpLongitude = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Longitude", &tmpLongitude); + if (tmpLongitude < -2147483648 || tmpLongitude >= 2147483647) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + location->latitude = static_cast(tmpLongitude); + + if (AddEventToTimeline(std::move(location))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } case WeatherData::eventtype::Clouds: { @@ -156,7 +292,17 @@ namespace Pinetime { clouds->timestamp = tmpTimestamp; clouds->eventType = static_cast(tmpEventType); clouds->expires = tmpExpires; - timeline.push_back(std::move(clouds)); + + int64_t tmpAmount = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); + if (tmpAmount < 0 || tmpAmount > 255) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + clouds->amount = static_cast(tmpAmount); + + if (AddEventToTimeline(std::move(clouds))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } case WeatherData::eventtype::Humidity: { @@ -164,7 +310,17 @@ namespace Pinetime { humidity->timestamp = tmpTimestamp; humidity->eventType = static_cast(tmpEventType); humidity->expires = tmpExpires; - timeline.push_back(std::move(humidity)); + + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpType); + if (tmpType < 0 || tmpType >= 255) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + humidity->humidity = static_cast(tmpType); + + if (AddEventToTimeline(std::move(humidity))) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } break; } default: { @@ -183,6 +339,12 @@ namespace Pinetime { // 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")); From cccec6e1abc8b7180d9e69c22c50fe9244b48ebc Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 15:50:36 +0200 Subject: [PATCH 22/41] Improved debug UI. --- src/CMakeLists.txt | 2 +- src/displayapp/screens/Weather.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9f3b6d4e..ac91c0c0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -892,7 +892,7 @@ target_compile_options(${EXECUTABLE_NAME} PUBLIC $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Og -g3> $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Os> $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Og -g3 -fno-rtti> - $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Os -fno-rtti> + $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -O1 -g3 -fno-rtti> $<$: -MP -MD -x assembler-with-cpp> ) diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index 0854c74a..c9852ee1 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -140,6 +140,14 @@ std::unique_ptr Weather::CreateScreenClouds() { std::unique_ptr& current = weatherService.GetCurrentClouds(); if (current->timestamp == 0) { // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Clouds#\n\n" + "#444444 %d%%#\n\n" + "%d\n" + "%d\n", + 0, + 0, + 0); } else { lv_label_set_text_fmt(label, "#FFFF00 Clouds#\n\n" @@ -161,6 +169,14 @@ std::unique_ptr Weather::CreateScreenPrecipitation() { std::unique_ptr& current = weatherService.GetCurrentPrecipitation(); if (current->timestamp == 0) { // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Precipitation#\n\n" + "#444444 %d%%#\n\n" + "%d\n" + "%d\n", + 0, + 0, + 0); } else { lv_label_set_text_fmt(label, "#FFFF00 Precipitation#\n\n" @@ -182,6 +198,14 @@ std::unique_ptr Weather::CreateScreenHumidity() { std::unique_ptr& current = weatherService.GetCurrentHumidity(); if (current->timestamp == 0) { // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Humidity#\n\n" + "#444444 %d%%#\n\n" + "%d\n" + "%d\n", + 0, + 0, + 0); } else { lv_label_set_text_fmt(label, "#FFFF00 Humidity#\n\n" From 75cf5324baf760b3f463ba84126c317471266b32 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 18:41:01 +0200 Subject: [PATCH 23/41] Fixed an incorrect decode in Humidity --- src/components/ble/weather/WeatherService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index c60e0f09..c2a1cec0 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -312,7 +312,7 @@ namespace Pinetime { humidity->expires = tmpExpires; int64_t tmpType = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpType); + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Humidity", &tmpType); if (tmpType < 0 || tmpType >= 255) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } From abbfb92fa2039754c45ae10e222ffd6d5bcbd778 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 18:41:26 +0200 Subject: [PATCH 24/41] Added new precipitation and obscuration types --- src/components/ble/weather/WeatherData.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index 19b9709d..8f5ef8ea 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -51,6 +51,8 @@ namespace Pinetime { Sand = 6, /** Water particles suspended in the air; low-ish visibility; temperature is near dewpoint */ Mist = 7, + /** This is SPECIAL in the sense that the thing raining down is doing the obscuration */ + Precipitation = 8, Length }; @@ -84,6 +86,8 @@ namespace Pinetime { SnowGrains = 8, /** Needles; columns or plates of ice. Sometimes described as "diamond dust". In very cold regions */ IceCrystals = 9, + /** It's raining down ash, e.g. from a volcano */ + Ash = 10, Length }; @@ -230,9 +234,15 @@ namespace Pinetime { */ class Temperature : public TimelineHeader { public: - /** Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250) */ + /** + * Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250) + * -32768 is reserved for "no data" + */ int16_t temperature; - /** Dewpoint °C but multiplied by 100 (e.g. -12.50°C becomes -1250) */ + /** + * Dewpoint °C but multiplied by 100 (e.g. -12.50°C becomes -1250) + * -32768 is reserved for "no data" + */ int16_t dewPoint; }; From b998d5e2a85415e86ac47fd60198bf46ae54e424 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 18:43:23 +0200 Subject: [PATCH 25/41] Removed unnecessary change in CMakeLists --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ac91c0c0..9f3b6d4e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -892,7 +892,7 @@ target_compile_options(${EXECUTABLE_NAME} PUBLIC $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Og -g3> $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Os> $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Og -g3 -fno-rtti> - $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -O1 -g3 -fno-rtti> + $<$,$>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Os -fno-rtti> $<$: -MP -MD -x assembler-with-cpp> ) From 9525fc427321ad209de657e837c47db5237912b9 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 20:54:13 +0200 Subject: [PATCH 26/41] Specified how values should be interpreted better --- src/components/ble/weather/WeatherData.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index 8f5ef8ea..195e5021 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -190,7 +190,10 @@ namespace Pinetime { public: /** Type */ obscurationtype type; - /** Visibility distance in meters */ + /** + * Visibility distance in meters + * 65535 is reserved for unspecified + */ uint16_t amount; }; @@ -199,7 +202,9 @@ namespace Pinetime { public: /** Type */ precipitationtype type; - /** How much is it going to rain? In millimeters */ + /** How much is it going to rain? In millimeters + * 255 is reserved for unspecified + **/ uint8_t amount; }; From b72c6a5bc97c786a295136742d89e6c14e1ccd72 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 21:08:57 +0200 Subject: [PATCH 27/41] Clarified a few comments --- src/components/ble/weather/WeatherData.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index 195e5021..73b15ca9 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -188,7 +188,7 @@ namespace Pinetime { /** Specifies how obscuration is stored */ class Obscuration : public TimelineHeader { public: - /** Type */ + /** Type of precipitation */ obscurationtype type; /** * Visibility distance in meters @@ -200,9 +200,10 @@ namespace Pinetime { /** Specifies how precipitation is stored */ class Precipitation : public TimelineHeader { public: - /** Type */ + /** Type of precipitation */ precipitationtype type; - /** How much is it going to rain? In millimeters + /** + * How much is it going to rain? In millimeters * 255 is reserved for unspecified **/ uint8_t amount; @@ -235,7 +236,7 @@ namespace Pinetime { * As it's annoying to figure out the dewpoint on the watch, * please send it from the companion * - * We don't do floats, microdegrees are not useful. Make sure to multiply. + * We don't do floats, picodegrees are not useful. Make sure to multiply. */ class Temperature : public TimelineHeader { public: @@ -344,7 +345,7 @@ namespace Pinetime { * ng/m³ for heavy metals * * List is not comprehensive, should be improved. - * The current ones are what watchapps assume. + * The current ones are what watchapps assume! * * Note: ppb and ppm to concentration should be calculated on the companion, using * the correct formula (taking into account temperature and air pressure) From ffd6c3f0953c753b0caf151be1eb824bdd777264 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 21:11:31 +0200 Subject: [PATCH 28/41] Removed an instance of shadowing --- src/components/ble/weather/WeatherService.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index c2a1cec0..e6d22d71 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -79,12 +79,12 @@ namespace Pinetime { airquality->eventType = static_cast(tmpEventType); airquality->expires = tmpExpires; - UsefulBufC String; // TODO: Everything ok with lifecycle here? - QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &String); - if (UsefulBuf_IsNULLOrEmptyC(String) != 0) { + UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here? + QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &stringBuf); + if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - airquality->polluter = std::string(static_cast(String.ptr), String.len); + airquality->polluter = std::string(static_cast(stringBuf.ptr), stringBuf.len); int64_t tmpAmount = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); From e0133cec36db56f71a2d9078c927e450ae361817 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 21:20:51 +0200 Subject: [PATCH 29/41] Improved documentation --- src/components/ble/weather/WeatherData.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index 73b15ca9..d56f481c 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -20,6 +20,24 @@ /** * Different weather events, weather data structures used by {@link WeatherService.h} * + * How to upload events to the timeline? + * + * All timeline write payloads are simply CBOR-encoded payloads of the structs described below. + * + * All payloads have a mandatory header part and the dynamic part that + * depends on the event type specified in the header. If you don't, + * you'll get an error returned. Data is relatively well-validated, + * so keep in the bounds of the data types given. + * + * Write all struct members into a single finite-sized map, and write it to the characteristic. + * Mind the MTU. + * + * How to debug? + * + * There's a Screen that you can compile into your firmware that shows currently valid events. + * You can adapt that to display something else. That part right now is very much work in progress + * because the exact requirements are not yet known. + * * * Implemented based on and other material: * https://en.wikipedia.org/wiki/METAR @@ -152,6 +170,8 @@ namespace Pinetime { /** * Valid event query + * + * NOTE: Not currently available, until needs are better known */ class ValidEventQuery { public: From 797b60397c630ac93dcff1bf7cef268c20747fb3 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 21:24:53 +0200 Subject: [PATCH 30/41] Improved documentation --- src/components/ble/weather/WeatherService.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 7accc49e..cc1a4b0d 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -94,12 +94,16 @@ namespace Pinetime { ble_uuid128_t weatherUuid {BaseUuid()}; /** - * Just write timeline data here + * 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 + * 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)}; From 154e3d27ad0053edf09db6437264028cbca8afd1 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 23:45:01 +0200 Subject: [PATCH 31/41] Added a few helper functions --- src/components/ble/weather/WeatherService.cpp | 46 +++++++++++++++++++ src/components/ble/weather/WeatherService.h | 17 +++++-- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index e6d22d71..c8eb3c20 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -513,5 +513,51 @@ namespace Pinetime { uint64_t WeatherService::GetCurrentUnixTimestamp() const { return std::chrono::duration_cast(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::AirQuality && isEventStillValid(header, currentTimestamp) && + header->timestamp < currentDayEnd && + reinterpret_cast&>(header)->temperature != -32768) { + int16_t temperature = reinterpret_cast&>(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::AirQuality && isEventStillValid(header, currentTimestamp) && + header->timestamp < currentDayEnd && + reinterpret_cast&>(header)->temperature != -32768) { + int16_t temperature = reinterpret_cast&>(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; + } } } diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index cc1a4b0d..52b0356a 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -61,6 +61,17 @@ namespace Pinetime { std::unique_ptr& GetCurrentPressure(); std::unique_ptr& GetCurrentQuality(); + /** + * Searches for the current day's maximum temperature + * @return -32768 if there's no data, degrees celcius times 100 otherwise + */ + int16_t getTodayMaxTemp() const; + /** + * Searches for the current day's minimum temperature + * @return -32768 if there's no data, degrees celcius times 100 otherwise + */ + int16_t getTodayMinTemp() const; + /* * Management functions */ @@ -75,7 +86,6 @@ namespace Pinetime { size_t GetTimelineLength() const; /** * Checks if an event of a certain type exists in the timeline - * @return */ bool HasTimelineEventOfType(WeatherData::eventtype type) const; @@ -124,6 +134,8 @@ namespace Pinetime { Pinetime::Controllers::DateTime& dateTimeController; std::vector> timeline; + std::unique_ptr nullTimelineheader = std::make_unique(); + std::unique_ptr* nullHeader; /** * Cleans up the timeline of expired events @@ -149,9 +161,6 @@ namespace Pinetime { * @return if the event is valid */ static bool isEventStillValid(const std::unique_ptr& uniquePtr, const uint64_t timestamp); - - std::unique_ptr nullTimelineheader = std::make_unique(); - std::unique_ptr* nullHeader; }; } } From be7931c4fb304df077f8a795d1e6e94c522556f7 Mon Sep 17 00:00:00 2001 From: Avamander Date: Wed, 1 Dec 2021 23:47:54 +0200 Subject: [PATCH 32/41] Whoops, fixed a wrong type --- src/components/ble/weather/WeatherService.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index c8eb3c20..c342602e 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -520,7 +520,7 @@ namespace Pinetime { ((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds()); int16_t result = -32768; for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::AirQuality && isEventStillValid(header, currentTimestamp) && + if (header->eventType == WeatherData::eventtype::Temperature && isEventStillValid(header, currentTimestamp) && header->timestamp < currentDayEnd && reinterpret_cast&>(header)->temperature != -32768) { int16_t temperature = reinterpret_cast&>(header)->temperature; @@ -543,7 +543,7 @@ namespace Pinetime { ((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds()); int16_t result = -32768; for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::AirQuality && isEventStillValid(header, currentTimestamp) && + if (header->eventType == WeatherData::eventtype::Temperature && isEventStillValid(header, currentTimestamp) && header->timestamp < currentDayEnd && reinterpret_cast&>(header)->temperature != -32768) { int16_t temperature = reinterpret_cast&>(header)->temperature; From 4a8f72bd1e77a387b1fe97d316c0f75d8c94c936 Mon Sep 17 00:00:00 2001 From: Avamander Date: Thu, 2 Dec 2021 21:12:33 +0200 Subject: [PATCH 33/41] Fixed a bug in DewPoint decoding --- src/components/ble/weather/WeatherService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index c342602e..e5d8053a 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -202,7 +202,7 @@ namespace Pinetime { int64_t tmpDewPoint = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint); - if (tmpDewPoint < 0 || tmpDewPoint > 65535) { + if (tmpDewPoint < -32768 || tmpDewPoint > 32767) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } temperature->dewPoint = tmpDewPoint; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) From 58d454b11fe3c143b37aea90772c122321a5b902 Mon Sep 17 00:00:00 2001 From: Avamander Date: Thu, 2 Dec 2021 23:49:51 +0200 Subject: [PATCH 34/41] Improved Temperature parsing --- src/components/ble/weather/WeatherService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index e5d8053a..a30b2270 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -195,7 +195,7 @@ namespace Pinetime { int64_t tmpTemperature = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Temperature", &tmpTemperature); - if (tmpTemperature < 0 || tmpTemperature > 65535) { + if (tmpTemperature < -32768 || tmpTemperature > 32767) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } temperature->temperature = tmpTemperature; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) From 62bb6b51634b22410a96f1ca9cbe183d1e9c504c Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 3 Dec 2021 16:28:17 +0200 Subject: [PATCH 35/41] Better cleanup, bugfixes and improvements in weather parsing. UI improvements --- src/components/ble/weather/WeatherService.cpp | 107 ++++++++++++------ src/components/ble/weather/WeatherService.h | 7 +- src/displayapp/DisplayApp.cpp | 7 +- src/displayapp/screens/Weather.cpp | 8 +- 4 files changed, 89 insertions(+), 40 deletions(-) diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index a30b2270..f3be35f2 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -58,17 +58,20 @@ namespace Pinetime { 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(WeatherData::eventtype::Length)) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } @@ -82,6 +85,7 @@ namespace Pinetime { 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(stringBuf.ptr), stringBuf.len); @@ -89,11 +93,13 @@ namespace Pinetime { 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))) { + if (!AddEventToTimeline(std::move(airquality))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -107,6 +113,7 @@ namespace Pinetime { int64_t tmpType = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); if (tmpType < 0 || tmpType >= static_cast(WeatherData::obscurationtype::Length)) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } obscuration->type = static_cast(tmpType); @@ -114,11 +121,13 @@ namespace Pinetime { 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))) { + if (!AddEventToTimeline(std::move(obscuration))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -132,6 +141,7 @@ namespace Pinetime { int64_t tmpType = 0; QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); if (tmpType < 0 || tmpType >= static_cast(WeatherData::precipitationtype::Length)) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } precipitation->type = static_cast(tmpType); @@ -139,11 +149,13 @@ namespace Pinetime { 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))) { + if (!AddEventToTimeline(std::move(precipitation))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -157,6 +169,7 @@ namespace Pinetime { 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) @@ -164,6 +177,7 @@ namespace Pinetime { 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) @@ -171,6 +185,7 @@ namespace Pinetime { 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) @@ -178,11 +193,13 @@ namespace Pinetime { 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))) { + if (!AddEventToTimeline(std::move(wind))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -196,18 +213,23 @@ namespace Pinetime { 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 = tmpTemperature; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + temperature->temperature = + static_cast(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 = tmpDewPoint; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + temperature->dewPoint = + static_cast(tmpDewPoint); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - if (AddEventToTimeline(std::move(temperature))) { + if (!AddEventToTimeline(std::move(temperature))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -219,13 +241,15 @@ namespace Pinetime { special->expires = tmpExpires; int64_t tmpType = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpType); + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); if (tmpType < 0 || tmpType >= static_cast(WeatherData::specialtype::Length)) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } special->type = static_cast(tmpType); - if (AddEventToTimeline(std::move(special))) { + if (!AddEventToTimeline(std::move(special))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -236,14 +260,16 @@ namespace Pinetime { pressure->eventType = static_cast(tmpEventType); pressure->expires = tmpExpires; - int64_t tmpDewPoint = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint); - if (tmpDewPoint < 0 || tmpDewPoint >= 65535) { + 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 = tmpDewPoint; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + pressure->pressure = tmpPressure; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - if (AddEventToTimeline(std::move(pressure))) { + if (!AddEventToTimeline(std::move(pressure))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -257,6 +283,7 @@ namespace Pinetime { 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(stringBuf.ptr), stringBuf.len); @@ -264,6 +291,7 @@ namespace Pinetime { 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(tmpAltitude); @@ -271,6 +299,7 @@ namespace Pinetime { 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(tmpLatitude); @@ -278,11 +307,13 @@ namespace Pinetime { 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(tmpLongitude); - if (AddEventToTimeline(std::move(location))) { + if (!AddEventToTimeline(std::move(location))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -296,11 +327,13 @@ namespace Pinetime { 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(tmpAmount); - if (AddEventToTimeline(std::move(clouds))) { + if (!AddEventToTimeline(std::move(clouds))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; @@ -314,17 +347,20 @@ namespace Pinetime { 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(tmpType); - if (AddEventToTimeline(std::move(humidity))) { + if (!AddEventToTimeline(std::move(humidity))) { + CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } break; } default: { - break; + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } } @@ -369,7 +405,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentClouds() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Clouds && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -380,7 +416,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentObscuration() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Obscuration && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -391,7 +427,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentPrecipitation() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Precipitation && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -402,7 +438,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentWind() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Wind && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -413,7 +449,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentTemperature() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Temperature && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -424,7 +460,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentHumidity() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Humidity && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -435,7 +471,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentPressure() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Pressure && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -446,7 +482,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentLocation() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Location && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -457,7 +493,7 @@ namespace Pinetime { std::unique_ptr& WeatherService::GetCurrentQuality() { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::AirQuality && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) { return reinterpret_cast&>(header); } } @@ -481,7 +517,7 @@ namespace Pinetime { bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const { uint64_t currentTimestamp = GetCurrentUnixTimestamp(); for (auto&& header : timeline) { - if (header->eventType == type && isEventStillValid(header, currentTimestamp)) { + if (header->eventType == type && IsEventStillValid(header, currentTimestamp)) { return true; } } @@ -493,7 +529,7 @@ namespace Pinetime { timeline.erase(std::remove_if(std::begin(timeline), std::end(timeline), [&](std::unique_ptr const& header) { - return !isEventStillValid(header, timeCurrent); + return !IsEventStillValid(header, timeCurrent); }), std::end(timeline)); @@ -505,9 +541,9 @@ namespace Pinetime { return first->timestamp > second->timestamp; } - bool WeatherService::isEventStillValid(const std::unique_ptr& header, const uint64_t currentTimestamp) { + bool WeatherService::IsEventStillValid(const std::unique_ptr& uniquePtr, const uint64_t timestamp) { // Not getting timestamp in isEventStillValid for more speed - return header->timestamp + header->expires <= currentTimestamp; + return uniquePtr->timestamp + uniquePtr->expires >= timestamp; } uint64_t WeatherService::GetCurrentUnixTimestamp() const { @@ -520,7 +556,7 @@ namespace Pinetime { ((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) && + if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) && header->timestamp < currentDayEnd && reinterpret_cast&>(header)->temperature != -32768) { int16_t temperature = reinterpret_cast&>(header)->temperature; @@ -543,7 +579,7 @@ namespace Pinetime { ((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) && + if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) && header->timestamp < currentDayEnd && reinterpret_cast&>(header)->temperature != -32768) { int16_t temperature = reinterpret_cast&>(header)->temperature; @@ -559,5 +595,10 @@ namespace Pinetime { return result; } + + void WeatherService::CleanUpQcbor(QCBORDecodeContext* decodeContext) { + QCBORDecode_ExitMap(decodeContext); + QCBORDecode_Finish(decodeContext); + } } } diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 52b0356a..a9f02b16 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -30,7 +30,8 @@ #undef min #include "WeatherData.h" -#include +#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); @@ -160,7 +161,9 @@ namespace Pinetime { * @param currentTimestamp what's the time right now * @return if the event is valid */ - static bool isEventStillValid(const std::unique_ptr& uniquePtr, const uint64_t timestamp); + static bool IsEventStillValid(const std::unique_ptr& uniquePtr, const uint64_t timestamp); + + void CleanUpQcbor(QCBORDecodeContext* decodeContext); }; } } diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 80155187..d45251b9 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -1,5 +1,6 @@ #include "displayapp/DisplayApp.h" #include +#include #include "displayapp/screens/HeartRate.h" #include "displayapp/screens/Motion.h" #include "displayapp/screens/Timer.h" @@ -439,7 +440,8 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) currentScreen = std::make_unique(this, systemTask->nimble().music()); break; case Apps::Navigation: - currentScreen = std::make_unique(this, systemTask->nimble().navigation()); + currentScreen = std::make_unique(this, systemTask->nimble().weather()); + // currentScreen = std::make_unique(this, systemTask->nimble().navigation()); break; case Apps::HeartRate: currentScreen = std::make_unique(this, heartRateController, *systemTask); @@ -451,6 +453,9 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) case Apps::Motion: currentScreen = std::make_unique(this, motionController); break; + case Apps::Weather: + currentScreen = std::make_unique(this, systemTask->nimble().weather()); + break; case Apps::Steps: currentScreen = std::make_unique(this, motionController, settingsController); break; diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index c9852ee1..d4241194 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -76,22 +76,22 @@ std::unique_ptr Weather::CreateScreenTemperature() { // Do not use the data, it's invalid lv_label_set_text_fmt(label, "#FFFF00 Temperature#\n\n" - "#444444 %d#\n\n" + "#444444 %.2f#°C \n\n" "#444444 %d#\n\n" "%d\n" "%d\n", - 0, + 0.0f, 0, 0, 0); } else { lv_label_set_text_fmt(label, "#FFFF00 Temperature#\n\n" - "#444444 %hd%%#°C \n\n" + "#444444 %.2f#°C \n\n" "#444444 %hd#\n\n" "%llu\n" "%lu\n", - current->temperature, + current->temperature / 100.0f, current->dewPoint, current->timestamp, current->expires); From 0df49bd43d6d02d8d50918543ae3eda77f31c651 Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 3 Dec 2021 17:06:30 +0200 Subject: [PATCH 36/41] Removed float usage from display --- src/displayapp/screens/Weather.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index d4241194..fcc15d61 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -76,22 +76,22 @@ std::unique_ptr Weather::CreateScreenTemperature() { // Do not use the data, it's invalid lv_label_set_text_fmt(label, "#FFFF00 Temperature#\n\n" - "#444444 %.2f#°C \n\n" + "#444444 %d#°C \n\n" "#444444 %d#\n\n" "%d\n" "%d\n", - 0.0f, + 0, 0, 0, 0); } else { lv_label_set_text_fmt(label, "#FFFF00 Temperature#\n\n" - "#444444 %.2f#°C \n\n" + "#444444 %d#°C \n\n" "#444444 %hd#\n\n" "%llu\n" "%lu\n", - current->temperature / 100.0f, + current->temperature / 100, current->dewPoint, current->timestamp, current->expires); From 7b04ce5ebaf1d73feac529bd16828415e5a046c7 Mon Sep 17 00:00:00 2001 From: Avamander Date: Fri, 3 Dec 2021 17:38:23 +0200 Subject: [PATCH 37/41] Added a note about the timestamp having a timezone offset --- src/components/ble/weather/WeatherData.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index d56f481c..42572ec0 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -182,7 +182,11 @@ namespace Pinetime { /** The header used for further parsing */ class TimelineHeader { public: - /** UNIX timestamp */ + /** + * UNIX timestamp + * TODO: This is currently WITH A TIMEZONE OFFSET! + * Please send events with the timestamp offset by the timezone. + **/ uint64_t timestamp; /** * Time in seconds until the event expires From 6879147648370dea405c169d0e1caea5c2009cbd Mon Sep 17 00:00:00 2001 From: Avamander Date: Sat, 4 Dec 2021 11:46:14 +0200 Subject: [PATCH 38/41] Revert wrong change to DisplayApp --- src/displayapp/DisplayApp.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index d45251b9..80155187 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -1,6 +1,5 @@ #include "displayapp/DisplayApp.h" #include -#include #include "displayapp/screens/HeartRate.h" #include "displayapp/screens/Motion.h" #include "displayapp/screens/Timer.h" @@ -440,8 +439,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) currentScreen = std::make_unique(this, systemTask->nimble().music()); break; case Apps::Navigation: - currentScreen = std::make_unique(this, systemTask->nimble().weather()); - // currentScreen = std::make_unique(this, systemTask->nimble().navigation()); + currentScreen = std::make_unique(this, systemTask->nimble().navigation()); break; case Apps::HeartRate: currentScreen = std::make_unique(this, heartRateController, *systemTask); @@ -453,9 +451,6 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) case Apps::Motion: currentScreen = std::make_unique(this, motionController); break; - case Apps::Weather: - currentScreen = std::make_unique(this, systemTask->nimble().weather()); - break; case Apps::Steps: currentScreen = std::make_unique(this, motionController, settingsController); break; From f1f2bc119a7c855613616ecaf5c8aa72390cde14 Mon Sep 17 00:00:00 2001 From: Avamander Date: Sat, 4 Dec 2021 13:58:40 +0200 Subject: [PATCH 39/41] Added a note about map key capitalization --- src/components/ble/weather/WeatherData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index 42572ec0..613d5acb 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -29,7 +29,7 @@ * you'll get an error returned. Data is relatively well-validated, * so keep in the bounds of the data types given. * - * Write all struct members into a single finite-sized map, and write it to the characteristic. + * Write all struct members (CamelCase keys) into a single finite-sized map, and write it to the characteristic. * Mind the MTU. * * How to debug? From 3eebe66d659c9c8e72a7c355973c74c2b8899174 Mon Sep 17 00:00:00 2001 From: Avamander Date: Sat, 4 Dec 2021 17:12:34 +0200 Subject: [PATCH 40/41] Updated docs and renamed functions for consistency --- doc/ble.md | 18 +++++++++++------- src/components/ble/weather/WeatherService.cpp | 4 ++-- src/components/ble/weather/WeatherService.h | 11 +++++++---- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/doc/ble.md b/doc/ble.md index 8573166f..2b86243e 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -2,7 +2,7 @@ ## Introduction This page describes the BLE implementation and API built in this firmware. -**Note** : I'm a beginner in BLE related technologies and the information in this document reflects my current knowledge and understanding of the BLE stack. This information might be erroneous or incomplete. Feel free to submit a PR if you think you can improve it. +**Note**: I'm a beginner in BLE related technologies and the information in this document reflects my current knowledge and understanding of the BLE stack. This information might be erroneous or incomplete. Feel free to submit a PR if you think you can improve it. --- @@ -72,12 +72,16 @@ The following custom services are implemented in InfiniTime: * [Navigation Service](NavigationService.md) : 00010000-78fc-48fe-8e23-433b3a1942d0 - - Since InfiniTime 0.13 - * Call characteristic (extension to the Alert Notification Service): 00020001-78fc-48fe-8e23-433b3a1942d0 - - - - Since InfiniTime 1.7: - * [Motion Service](MotionService.md) : 00030000-78fc-48fe-8e23-433b3a1942d0 +- Since InfiniTime 0.13 + * Call characteristic (extension to the Alert Notification Service): 00020001-78fc-48fe-8e23-433b3a1942d0 + + +- Since InfiniTime 1.7: + * [Motion Service](MotionService.md): 00030000-78fc-48fe-8e23-433b3a1942d0 + + +- Since InfiniTime 1.8: + * [Weather Service](/src/components/ble/weather/WeatherService.h): 00040000-78fc-48fe-8e23-433b3a1942d0 --- diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index f3be35f2..23f53b74 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -550,7 +550,7 @@ namespace Pinetime { return std::chrono::duration_cast(dateTimeController.CurrentDateTime().time_since_epoch()).count(); } - int16_t WeatherService::getTodayMinTemp() const { + 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()); @@ -573,7 +573,7 @@ namespace Pinetime { return result; } - int16_t WeatherService::getTodayMaxTemp() const { + 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()); diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index a9f02b16..eca70cbd 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -64,14 +64,14 @@ namespace Pinetime { /** * Searches for the current day's maximum temperature - * @return -32768 if there's no data, degrees celcius times 100 otherwise + * @return -32768 if there's no data, degrees Celsius times 100 otherwise */ - int16_t getTodayMaxTemp() const; + int16_t GetTodayMaxTemp() const; /** * Searches for the current day's minimum temperature - * @return -32768 if there's no data, degrees celcius times 100 otherwise + * @return -32768 if there's no data, degrees Celsius times 100 otherwise */ - int16_t getTodayMinTemp() const; + int16_t GetTodayMinTemp() const; /* * Management functions @@ -163,6 +163,9 @@ namespace Pinetime { */ static bool IsEventStillValid(const std::unique_ptr& uniquePtr, const uint64_t timestamp); + /** + * This is a helper function that closes a QCBOR map and decoding context cleanly + */ void CleanUpQcbor(QCBORDecodeContext* decodeContext); }; } From 5f50f0e538e20ede353b388148b706319da161ce Mon Sep 17 00:00:00 2001 From: Avamander Date: Sat, 4 Dec 2021 20:09:37 +0200 Subject: [PATCH 41/41] Fixed air quality amounts being off by a few orders of magnitude --- src/displayapp/screens/Weather.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index fcc15d61..1d0a83bd 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -125,7 +125,7 @@ std::unique_ptr Weather::CreateScreenAir() { "%llu\n" "%lu\n", current->polluter.c_str(), - current->amount, + (current->amount / 100), current->timestamp, current->expires); }