InfiniTime.git

commit 40a643d203d2d21834dd2b35d83419a56a3939b6

Author: Avamander <avamander@gmail.com>

Renamed Components/ to components/

 src/CMakeLists.txt | 62 ++++++++++++++++++++++++------------------------
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 


diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1b5cdcbe554b511629f965ee710365014dd1bfb3..f53663140a59101dc60999f7624dda652cf8eada 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -349,22 +349,22 @@         drivers/Spi.cpp
         drivers/Watchdog.cpp
         drivers/DebugPins.cpp
         drivers/InternalFlash.cpp
-        Components/Battery/BatteryController.cpp
-        Components/Ble/BleController.cpp
-        Components/Ble/NotificationManager.cpp
-        Components/DateTime/DateTimeController.cpp
-        Components/Brightness/BrightnessController.cpp
-        Components/Ble/NimbleController.cpp
-        Components/Ble/DeviceInformationService.cpp
-        Components/Ble/CurrentTimeClient.cpp
-        Components/Ble/AlertNotificationClient.cpp
-        Components/Ble/DfuService.cpp
-        Components/Ble/CurrentTimeService.cpp
-        Components/Ble/AlertNotificationService.cpp
-        Components/Ble/MusicService.cpp
-        Components/Ble/BatteryInformationService.cpp
-        Components/Ble/ImmediateAlertService.cpp
-        Components/FirmwareValidator/FirmwareValidator.cpp
+        components/Battery/BatteryController.cpp
+        components/Ble/BleController.cpp
+        components/Ble/NotificationManager.cpp
+        components/DateTime/DateTimeController.cpp
+        components/Brightness/BrightnessController.cpp
+        components/Ble/NimbleController.cpp
+        components/Ble/DeviceInformationService.cpp
+        components/Ble/CurrentTimeClient.cpp
+        components/Ble/AlertNotificationClient.cpp
+        components/Ble/DfuService.cpp
+        components/Ble/CurrentTimeService.cpp
+        components/Ble/AlertNotificationService.cpp
+        components/Ble/MusicService.cpp
+        components/Ble/BatteryInformationService.cpp
+        components/Ble/ImmediateAlertService.cpp
+        components/FirmwareValidator/FirmwareValidator.cpp
         drivers/Cst816s.cpp
         FreeRTOS/port.c
         FreeRTOS/port_cmsis_systick.c
@@ -389,9 +389,9 @@         drivers/SpiMaster.cpp
         drivers/Spi.cpp
         Logging/NrfLogger.cpp
 
-        Components/Gfx/Gfx.cpp
+        components/Gfx/Gfx.cpp
         drivers/St7789.cpp
-        Components/Brightness/BrightnessController.cpp
+        components/Brightness/BrightnessController.cpp
 
         graphics.cpp
         )
@@ -426,19 +426,19 @@         drivers/Spi.h
         drivers/Watchdog.h
         drivers/DebugPins.h
         drivers/InternalFlash.h
-        Components/Battery/BatteryController.h
-        Components/Ble/BleController.h
-        Components/Ble/NotificationManager.h
-        Components/DateTime/DateTimeController.h
-        Components/Brightness/BrightnessController.h
-        Components/Ble/NimbleController.h
-        Components/Ble/DeviceInformationService.h
-        Components/Ble/CurrentTimeClient.h
-        Components/Ble/AlertNotificationClient.h
-        Components/Ble/DfuService.h
-        Components/FirmwareValidator/FirmwareValidator.h
-        Components/Ble/BatteryInformationService.h
-        Components/Ble/ImmediateAlertService.h
+        components/Battery/BatteryController.h
+        components/Ble/BleController.h
+        components/Ble/NotificationManager.h
+        components/DateTime/DateTimeController.h
+        components/Brightness/BrightnessController.h
+        components/Ble/NimbleController.h
+        components/Ble/DeviceInformationService.h
+        components/Ble/CurrentTimeClient.h
+        components/Ble/AlertNotificationClient.h
+        components/Ble/DfuService.h
+        components/FirmwareValidator/FirmwareValidator.h
+        components/Ble/BatteryInformationService.h
+        components/Ble/ImmediateAlertService.h
         drivers/Cst816s.h
         FreeRTOS/portmacro.h
         FreeRTOS/portmacro_cmsis.h




diff --git a/src/Components/Battery/BatteryController.cpp b/src/Components/Battery/BatteryController.cpp
deleted file mode 100644
index 571efae64d7c8b352648777df15b264d8275d65f..0000000000000000000000000000000000000000
--- a/src/Components/Battery/BatteryController.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include <drivers/include/nrfx_saadc.h>
-#include <hal/nrf_gpio.h>
-#include <libraries/log/nrf_log.h>
-#include <algorithm>
-#include "BatteryController.h"
-
-using namespace Pinetime::Controllers;
-
-void Battery::Init() {
-  nrf_gpio_cfg_input(chargingPin, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup);
-  nrf_gpio_cfg_input(powerPresentPin, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup);
-
-  nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
-  nrfx_saadc_init(&adcConfig, SaadcEventHandler);
-  nrf_saadc_channel_config_t adcChannelConfig = {
-          .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
-          .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
-          .gain       = NRF_SAADC_GAIN1_5,
-          .reference  = NRF_SAADC_REFERENCE_INTERNAL,
-          .acq_time   = NRF_SAADC_ACQTIME_3US,
-          .mode       = NRF_SAADC_MODE_SINGLE_ENDED,
-          .burst      = NRF_SAADC_BURST_DISABLED,
-          .pin_p      = batteryVoltageAdcInput,
-          .pin_n      = NRF_SAADC_INPUT_DISABLED
-  };
-  nrfx_saadc_channel_init(0, &adcChannelConfig);
-}
-
-void Battery::Update() {
-  isCharging = !nrf_gpio_pin_read(chargingPin);
-  isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
-
-  nrf_saadc_value_t value = 0;
-  nrfx_saadc_sample_convert(0, &value);
-
-  // see https://forum.pine64.org/showthread.php?tid=8147
-  voltage = (value * 2.0f) / (1024/3.0f);
-  percentRemaining = ((voltage - 3.55f)*100.0f)*3.9f;
-  percentRemaining = std::max(percentRemaining, 0.0f);
-  percentRemaining = std::min(percentRemaining, 100.0f);
-
-//  NRF_LOG_INFO("BATTERY " NRF_LOG_FLOAT_MARKER " %% - " NRF_LOG_FLOAT_MARKER " v", NRF_LOG_FLOAT(percentRemaining), NRF_LOG_FLOAT(voltage));
-//  NRF_LOG_INFO("POWER Charging : %d - Power : %d", isCharging, isPowerPresent);
-}
-
-void Battery::SaadcEventHandler(nrfx_saadc_evt_t const * event) {
-
-}
\ No newline at end of file




diff --git a/src/Components/Battery/BatteryController.h b/src/Components/Battery/BatteryController.h
deleted file mode 100644
index f07648a9f0b703b0513b9106f3a8d36375ce75e1..0000000000000000000000000000000000000000
--- a/src/Components/Battery/BatteryController.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-#include <drivers/include/nrfx_saadc.h>
-
-
-namespace Pinetime {
-  namespace Controllers {
-    class Battery {
-      public:
-        void Init();
-        void Update();
-        float PercentRemaining() const { return percentRemaining; }
-        float Voltage() const { return voltage; }
-        bool IsCharging() const { return isCharging; }
-        bool IsPowerPresent() const { return isPowerPresent; }
-
-      private:
-        static constexpr uint32_t chargingPin = 12;
-        static constexpr uint32_t powerPresentPin = 19;
-        static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
-        static void SaadcEventHandler(nrfx_saadc_evt_t const * p_event);
-        float percentRemaining = 0.0f;
-        float voltage = 0.0f;
-        bool isCharging = false;
-        bool isPowerPresent = false;
-    };
-  }
-}
\ No newline at end of file




diff --git a/src/Components/Ble/AlertNotificationClient.cpp b/src/Components/Ble/AlertNotificationClient.cpp
deleted file mode 100644
index 3e4b495fb741c1f5f58e8b78b5391ad31e066228..0000000000000000000000000000000000000000
--- a/src/Components/Ble/AlertNotificationClient.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-#include <SystemTask/SystemTask.h>
-#include "NotificationManager.h"
-
-#include "AlertNotificationClient.h"
-
-
-using namespace Pinetime::Controllers;
-constexpr ble_uuid16_t AlertNotificationClient::ansServiceUuid;
-
-constexpr ble_uuid16_t AlertNotificationClient::supportedNewAlertCategoryUuid;
-constexpr ble_uuid16_t AlertNotificationClient::supportedUnreadAlertCategoryUuid ;
-constexpr ble_uuid16_t AlertNotificationClient::newAlertUuid;
-constexpr ble_uuid16_t AlertNotificationClient::unreadAlertStatusUuid;
-constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid;
-
-int Pinetime::Controllers::NewAlertSubcribeCallback(uint16_t conn_handle,
-                     const struct ble_gatt_error *error,
-                     struct ble_gatt_attr *attr,
-                     void *arg) {
-  auto client = static_cast<AlertNotificationClient*>(arg);
-  return client->OnNewAlertSubcribe(conn_handle, error, attr);
-}
-
-AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
-        Pinetime::Controllers::NotificationManager& notificationManager) :
-        systemTask{systemTask}, notificationManager{notificationManager}{
-
-}
-
-bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) {
-  if(service == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("ANS Discovery complete");
-    return true;
-  }
-
-  if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ansServiceUuid), &service->uuid.u) == 0) {
-    NRF_LOG_INFO("ANS discovered : 0x%x", service->start_handle);
-      ansStartHandle = service->start_handle;
-      ansEndHandle = service->end_handle;
-      isDiscovered = true;
-  }
-  return false;
-}
-
-int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                                                    const ble_gatt_chr *characteristic) {
-  if(error->status != 0 && error->status != BLE_HS_EDONE) {
-    NRF_LOG_INFO("ANS Characteristic discovery ERROR");
-    return 0;
-  }
-
-  if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("ANS Characteristic discovery complete");
-  } else {
-    if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
-      NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid");
-      supportedNewAlertCategoryHandle = characteristic->val_handle;
-    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
-      NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid");
-      supportedUnreadAlertCategoryHandle = characteristic->val_handle;
-    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&newAlertUuid), &characteristic->uuid.u) == 0) {
-      NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid");
-      newAlertHandle = characteristic->val_handle;
-      newAlertDefHandle = characteristic->def_handle;
-    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
-      NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid");
-      unreadAlertStatusHandle = characteristic->val_handle;
-    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&controlPointUuid), &characteristic->uuid.u) == 0) {
-      NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid");
-      controlPointHandle = characteristic->val_handle;
-    }else
-      NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle);
-    }
-  return 0;
-}
-
-int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                ble_gatt_attr *attribute) {
-  if(error->status == 0) {
-    NRF_LOG_INFO("ANS New alert subscribe OK");
-  } else {
-    NRF_LOG_INFO("ANS New alert subscribe ERROR");
-  }
-
-  return 0;
-}
-
-int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                                uint16_t characteristicValueHandle,
-                                                                const ble_gatt_dsc *descriptor) {
-  if(error->status == 0) {
-    if(characteristicValueHandle == newAlertHandle && ble_uuid_cmp(((ble_uuid_t*)&newAlertUuid), &descriptor->uuid.u)) {
-      if(newAlertDescriptorHandle == 0) {
-        NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle);
-        newAlertDescriptorHandle = descriptor->handle;
-        uint8_t value[2];
-        value[0] = 1;
-        value[1] = 0;
-        ble_gattc_write_flat(connectionHandle, newAlertDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this);
-      }
-    }
-  }
-  return 0;
-}
-
-void AlertNotificationClient::OnNotification(ble_gap_event *event) {
-  if(event->notify_rx.attr_handle == newAlertHandle) {
-    // TODO implement this with more memory safety (and constexpr)
-    static const size_t maxBufferSize{21};
-    static const size_t maxMessageSize{18};
-    size_t bufferSize = min(OS_MBUF_PKTLEN(event->notify_rx.om), maxBufferSize);
-
-    uint8_t data[bufferSize];
-    os_mbuf_copydata(event->notify_rx.om, 0, bufferSize, data);
-
-    char *s = (char *) &data[3];
-    auto messageSize = min(maxMessageSize, (bufferSize-3));
-
-    for (uint i = 0; i < messageSize-1; i++) {
-      if (s[i] == 0x00) {
-        s[i] = 0x0A;
-      }
-    }
-    s[messageSize-1] = '\0';
-
-    notificationManager.Push(Pinetime::Controllers::NotificationManager::Categories::SimpleAlert, s, messageSize);
-    systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
-  }
-}
-
-bool AlertNotificationClient::IsDiscovered() const {
-  return isDiscovered;
-}
-
-uint16_t AlertNotificationClient::StartHandle() const {
-  return ansStartHandle;
-}
-
-uint16_t AlertNotificationClient::EndHandle() const {
-  return ansEndHandle;
-}
-
-uint16_t AlertNotificationClient::NewAlerthandle() const {
-  return newAlertHandle;
-}




diff --git a/src/Components/Ble/AlertNotificationClient.h b/src/Components/Ble/AlertNotificationClient.h
deleted file mode 100644
index ca4f4e948e228c8351bfedbb119253134e493817..0000000000000000000000000000000000000000
--- a/src/Components/Ble/AlertNotificationClient.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <array>
-#include <host/ble_gap.h>
-
-
-namespace Pinetime {
-  namespace Controllers {
-    int NewAlertSubcribeCallback(uint16_t conn_handle,
-                                 const struct ble_gatt_error *error,
-                                 struct ble_gatt_attr *attr,
-                                 void *arg);
-
-    class AlertNotificationClient {
-      public:
-        explicit AlertNotificationClient(Pinetime::System::SystemTask &systemTask,
-                                         Pinetime::Controllers::NotificationManager &notificationManager);
-
-        bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service);
-        int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                            const ble_gatt_chr *characteristic);
-        int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute);
-        int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
-                                               uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
-        void OnNotification(ble_gap_event *event);
-        bool IsDiscovered() const;
-        uint16_t StartHandle() const;
-        uint16_t EndHandle() const;
-
-        static constexpr const ble_uuid16_t &Uuid() { return ansServiceUuid; }
-
-        uint16_t NewAlerthandle() const;
-      private:
-        static constexpr uint16_t ansServiceId{0x1811};
-        static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47;
-        static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48;
-        static constexpr uint16_t newAlertId = 0x2a46;
-        static constexpr uint16_t unreadAlertStatusId = 0x2a45;
-        static constexpr uint16_t controlPointId = 0x2a44;
-
-        static constexpr ble_uuid16_t ansServiceUuid{
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = ansServiceId
-        };
-        static constexpr ble_uuid16_t supportedNewAlertCategoryUuid{
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = supportedNewAlertCategoryId
-        };
-        static constexpr ble_uuid16_t supportedUnreadAlertCategoryUuid{
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = supportedUnreadAlertCategoryId
-        };
-        static constexpr ble_uuid16_t newAlertUuid{
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = newAlertId
-        };
-        static constexpr ble_uuid16_t unreadAlertStatusUuid{
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = unreadAlertStatusId
-        };
-        static constexpr ble_uuid16_t controlPointUuid{
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = controlPointId
-        };
-
-        uint16_t ansStartHandle;
-        uint16_t ansEndHandle;
-        uint16_t supportedNewAlertCategoryHandle;
-        uint16_t supportedUnreadAlertCategoryHandle;
-        uint16_t newAlertHandle;
-        uint16_t newAlertDescriptorHandle = 0;
-        uint16_t newAlertDefHandle;
-        uint16_t unreadAlertStatusHandle;
-        uint16_t controlPointHandle;
-        bool isDiscovered = false;
-        Pinetime::System::SystemTask &systemTask;
-        Pinetime::Controllers::NotificationManager &notificationManager;
-    };
-  }
-}
\ No newline at end of file




diff --git a/src/Components/Ble/AlertNotificationService.cpp b/src/Components/Ble/AlertNotificationService.cpp
deleted file mode 100644
index ce2f7dd7b9d2faf10281d6ec8e16c91972f2f53e..0000000000000000000000000000000000000000
--- a/src/Components/Ble/AlertNotificationService.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-
-#include <hal/nrf_rtc.h>
-#include "NotificationManager.h"
-#include <SystemTask/SystemTask.h>
-
-#include "AlertNotificationService.h"
-#include <cstring>
-
-using namespace Pinetime::Controllers;
-
-constexpr ble_uuid16_t AlertNotificationService::ansUuid;
-constexpr ble_uuid16_t AlertNotificationService::ansCharUuid;
-
-
-int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
-  auto anService = static_cast<AlertNotificationService*>(arg);
-  return anService->OnAlert(conn_handle, attr_handle, ctxt);
-}
-
-void AlertNotificationService::Init() {
-  int res;
-  res = ble_gatts_count_cfg(serviceDefinition);
-  ASSERT(res == 0);
-
-  res = ble_gatts_add_svcs(serviceDefinition);
-  ASSERT(res == 0);
-}
-
-AlertNotificationService::AlertNotificationService ( System::SystemTask& systemTask, NotificationManager& notificationManager )
-  : characteristicDefinition{
-                {
-                        .uuid = (ble_uuid_t *) &ansCharUuid,
-                        .access_cb = AlertNotificationCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_WRITE
-                },
-                {
-                  0
-                }
-        },
-        serviceDefinition{
-                {
-                        /* Device Information Service */
-                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
-                        .uuid = (ble_uuid_t *) &ansUuid,
-                        .characteristics = characteristicDefinition
-                },
-                {
-                        0
-                },
-        }, m_systemTask{systemTask}, m_notificationManager{notificationManager} {
-}
-
-int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle,
-                                                    struct ble_gatt_access_ctxt *ctxt) {
-
-  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
-    // TODO implement this with more memory safety (and constexpr)
-    static const size_t maxBufferSize{21};
-    static const size_t maxMessageSize{18};
-    size_t bufferSize = min(OS_MBUF_PKTLEN(ctxt->om), maxBufferSize);
-
-    uint8_t data[bufferSize];
-    os_mbuf_copydata(ctxt->om, 0, bufferSize, data);
-
-    char *s = (char *) &data[3];
-    auto messageSize = min(maxMessageSize, (bufferSize-3));
-
-    for (uint i = 0; i < messageSize-1; i++) {
-      if (s[i] == 0x00) {
-        s[i] = 0x0A;
-      }
-    }
-    s[messageSize-1] = '\0';
-
-    m_notificationManager.Push(Pinetime::Controllers::NotificationManager::Categories::SimpleAlert, s, messageSize);
-    m_systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
-  }
-  return 0;
-}




diff --git a/src/Components/Ble/AlertNotificationService.h b/src/Components/Ble/AlertNotificationService.h
deleted file mode 100644
index 53cb44cc59fb7bf1444d9737831112b90a2a3102..0000000000000000000000000000000000000000
--- a/src/Components/Ble/AlertNotificationService.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-#include <cstdint>
-#include <array>
-#include <host/ble_gap.h>
-
-namespace Pinetime {
-  namespace Controllers {
-    class AlertNotificationService {
-      public:
-        AlertNotificationService(Pinetime::System::SystemTask &systemTask,
-                                         Pinetime::Controllers::NotificationManager &notificationManager);
-        void Init();
-
-        int OnAlert(uint16_t conn_handle, uint16_t attr_handle,
-                                    struct ble_gatt_access_ctxt *ctxt);
-
-
-      private:
-        static constexpr uint16_t ansId {0x1811};
-        static constexpr uint16_t ansCharId {0x2a46};
-
-        static constexpr ble_uuid16_t ansUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = ansId
-        };
-
-        static constexpr ble_uuid16_t ansCharUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = ansCharId
-        };
-
-        struct ble_gatt_chr_def characteristicDefinition[2];
-        struct ble_gatt_svc_def serviceDefinition[2];
-
-        Pinetime::System::SystemTask &m_systemTask;
-        NotificationManager &m_notificationManager;
-    };
-  }
-}




diff --git a/src/Components/Ble/BatteryInformationService.cpp b/src/Components/Ble/BatteryInformationService.cpp
deleted file mode 100644
index c86830b88c33b647d8be22000f5a24acaf961530..0000000000000000000000000000000000000000
--- a/src/Components/Ble/BatteryInformationService.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#include "BatteryInformationService.h"
-#include "../Battery/BatteryController.h"
-
-using namespace Pinetime::Controllers;
-
-constexpr ble_uuid16_t BatteryInformationService::batteryInformationServiceUuid;
-constexpr ble_uuid16_t BatteryInformationService::batteryLevelUuid;
-
-
-
-int BatteryInformationServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
-  auto* batteryInformationService = static_cast<BatteryInformationService*>(arg);
-  return batteryInformationService->OnBatteryServiceRequested(conn_handle, attr_handle, ctxt);
-}
-
-BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController) :
-        batteryController{batteryController},
-        characteristicDefinition{
-                {
-                        .uuid = (ble_uuid_t *) &batteryLevelUuid,
-                        .access_cb = BatteryInformationServiceCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_READ,
-                        .val_handle = &batteryLevelHandle
-                },
-                {
-                        0
-                }
-        },
-        serviceDefinition{
-                {
-                        /* Device Information Service */
-                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
-                        .uuid = (ble_uuid_t *) &batteryInformationServiceUuid,
-                        .characteristics = characteristicDefinition
-                },
-                {
-                        0
-                },
-        }{
-
-}
-
-void BatteryInformationService::Init() {
-  int res = 0;
-  res = ble_gatts_count_cfg(serviceDefinition);
-  ASSERT(res == 0);
-
-  res = ble_gatts_add_svcs(serviceDefinition);
-  ASSERT(res == 0);
-}
-
-int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle,
-                                                         ble_gatt_access_ctxt *context) {
-  if(attributeHandle == batteryLevelHandle) {
-    NRF_LOG_INFO("BATTERY : handle = %d", batteryLevelHandle);
-    static uint8_t batteryValue = batteryController.PercentRemaining();
-    int res = os_mbuf_append(context->om, &batteryValue, 1);
-    return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
-  }
-  return 0;
-}
\ No newline at end of file




diff --git a/src/Components/Ble/BatteryInformationService.h b/src/Components/Ble/BatteryInformationService.h
deleted file mode 100644
index 74b2222c4ee59f67e0029b74275444158c35e3b0..0000000000000000000000000000000000000000
--- a/src/Components/Ble/BatteryInformationService.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-#include <host/ble_gap.h>
-
-namespace Pinetime {
-  namespace System {
-    class SystemTask;
-  }
-  namespace Controllers {
-    class Battery;
-    class BatteryInformationService {
-      public:
-        BatteryInformationService(Controllers::Battery& batteryController);
-        void Init();
-
-        int
-        OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
-
-      private:
-        Controllers::Battery& batteryController;
-        static constexpr uint16_t batteryInformationServiceId {0x180F};
-        static constexpr uint16_t batteryLevelId {0x2A19};
-
-        static constexpr ble_uuid16_t batteryInformationServiceUuid {
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = batteryInformationServiceId
-        };
-
-        static constexpr ble_uuid16_t batteryLevelUuid {
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = batteryLevelId
-        };
-
-        struct ble_gatt_chr_def characteristicDefinition[3];
-        struct ble_gatt_svc_def serviceDefinition[2];
-
-        uint16_t batteryLevelHandle;
-
-    };
-  }
-}




diff --git a/src/Components/Ble/BleController.cpp b/src/Components/Ble/BleController.cpp
deleted file mode 100644
index 2b396e12f069072360f1d97fd36c1fc0be91b933..0000000000000000000000000000000000000000
--- a/src/Components/Ble/BleController.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <cstring>
-#include <cstdlib>
-#include "BleController.h"
-
-using namespace Pinetime::Controllers;
-
-void Ble::Connect() {
-  isConnected = true;
-}
-
-void Ble::Disconnect() {
-  isConnected = false;
-}
-
-void Ble::StartFirmwareUpdate() {
-  isFirmwareUpdating = true;
-}
-
-void Ble::StopFirmwareUpdate() {
-  isFirmwareUpdating = false;
-}
-
-void Ble::FirmwareUpdateTotalBytes(uint32_t totalBytes) {
-  firmwareUpdateTotalBytes = totalBytes;
-}
-
-void Ble::FirmwareUpdateCurrentBytes(uint32_t currentBytes) {
-  firmwareUpdateCurrentBytes = currentBytes;
-}
-
-




diff --git a/src/Components/Ble/BleController.h b/src/Components/Ble/BleController.h
deleted file mode 100644
index 3f52ea258ae766453d150a91de3d0912899a4d56..0000000000000000000000000000000000000000
--- a/src/Components/Ble/BleController.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#pragma once
-
-#include <FreeRTOS.h>
-#include <queue.h>
-#include <array>
-
-namespace Pinetime {
-  namespace Controllers {
-    class Ble {
-      public:
-        using BleAddress = std::array<uint8_t, 6>;
-        enum class FirmwareUpdateStates {Idle, Running, Validated, Error};
-        enum class AddressTypes { Public, Random };
-
-        Ble() = default;
-        bool IsConnected() const {return isConnected;}
-        void Connect();
-        void Disconnect();
-
-        void StartFirmwareUpdate();
-        void StopFirmwareUpdate();
-        void FirmwareUpdateTotalBytes(uint32_t totalBytes);
-        void FirmwareUpdateCurrentBytes(uint32_t currentBytes);
-        void State(FirmwareUpdateStates state) { firmwareUpdateState = state; }
-
-        bool IsFirmwareUpdating() const { return isFirmwareUpdating; }
-        uint32_t FirmwareUpdateTotalBytes() const { return firmwareUpdateTotalBytes; }
-        uint32_t FirmwareUpdateCurrentBytes() const { return firmwareUpdateCurrentBytes; }
-        FirmwareUpdateStates State() const { return firmwareUpdateState; }
-
-        void Address(BleAddress&& addr) { address = addr; }
-        const BleAddress& Address() const { return address; }
-        void AddressType(AddressTypes t) { addressType = t;}
-      private:
-        bool isConnected = false;
-        bool isFirmwareUpdating = false;
-        uint32_t firmwareUpdateTotalBytes = 0;
-        uint32_t firmwareUpdateCurrentBytes = 0;
-        FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle;
-        BleAddress address;
-        AddressTypes addressType;
-
-    };
-  }
-}
\ No newline at end of file




diff --git a/src/Components/Ble/CurrentTimeClient.cpp b/src/Components/Ble/CurrentTimeClient.cpp
deleted file mode 100644
index 7a225f4bad3e6825edddde1af187c983edb08185..0000000000000000000000000000000000000000
--- a/src/Components/Ble/CurrentTimeClient.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#include <hal/nrf_rtc.h>
-#include "CurrentTimeClient.h"
-
-using namespace Pinetime::Controllers;
-
-constexpr ble_uuid16_t CurrentTimeClient::ctsServiceUuid;
-constexpr ble_uuid16_t CurrentTimeClient::currentTimeCharacteristicUuid;
-
-CurrentTimeClient::CurrentTimeClient(DateTime& dateTimeController) : dateTimeController{dateTimeController} {
-
-}
-
-void CurrentTimeClient::Init() {
-
-}
-
-bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) {
-    if(service == nullptr && error->status == BLE_HS_EDONE) {
-        NRF_LOG_INFO("CTS Discovery complete");
-        return true;
-    }
-
-    if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ctsServiceUuid), &service->uuid.u) == 0) {
-        NRF_LOG_INFO("CTS discovered : 0x%x",  service->start_handle);
-        isDiscovered = true;
-        ctsStartHandle = service->start_handle;
-        ctsEndHandle = service->end_handle;
-        return false;
-    }
-    return false;
-}
-
-int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
-                                                      const ble_gatt_chr *characteristic) {
-    if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
-        NRF_LOG_INFO("CTS Characteristic discovery complete");
-        return 0;
-    }
-
-    if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&currentTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
-        NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle);
-        currentTimeHandle = characteristic->val_handle;
-    }
-    return 0;
-}
-
-int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute) {
-    if(error->status == 0) {
-        // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
-        CtsData result;
-        os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
-        NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
-                     result.month, result.dayofmonth,
-                     result.hour, result.minute, result.second);
-        dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
-                                   0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
-    } else {
-        NRF_LOG_INFO("Error retrieving current time: %d", error->status);
-    }
-    return 0;
-}
-
-bool CurrentTimeClient::IsDiscovered() const {
-    return isDiscovered;
-}
-
-uint16_t CurrentTimeClient::StartHandle() const {
-    return ctsStartHandle;
-}
-
-uint16_t CurrentTimeClient::EndHandle() const {
-    return ctsEndHandle;
-}
-
-uint16_t CurrentTimeClient::CurrentTimeHandle() const {
-    return currentTimeHandle;
-}
\ No newline at end of file




diff --git a/src/Components/Ble/CurrentTimeClient.h b/src/Components/Ble/CurrentTimeClient.h
deleted file mode 100644
index fabcdacae8366c32657c56707e3cc4c06377577c..0000000000000000000000000000000000000000
--- a/src/Components/Ble/CurrentTimeClient.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma once
-#include <cstdint>
-#include <array>
-#include <Components/DateTime/DateTimeController.h>
-#include <host/ble_gap.h>
-
-namespace Pinetime {
-    namespace Controllers {
-
-        class CurrentTimeClient {
-        public:
-            explicit CurrentTimeClient(DateTime& dateTimeController);
-            void Init();
-            bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service);
-            int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
-                                               const ble_gatt_chr *characteristic);
-            int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute);
-            bool IsDiscovered() const;
-            uint16_t StartHandle() const;
-            uint16_t EndHandle() const;
-            uint16_t CurrentTimeHandle() const;
-            static constexpr const ble_uuid16_t* Uuid() { return &CurrentTimeClient::ctsServiceUuid; }
-            static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() { return &CurrentTimeClient::currentTimeCharacteristicUuid; }
-        private:
-            typedef struct __attribute__((packed)) {
-                uint16_t year;
-                uint8_t month;
-                uint8_t dayofmonth;
-                uint8_t hour;
-                uint8_t minute;
-                uint8_t second;
-                uint8_t millis;
-                uint8_t reason;
-            } CtsData;
-
-            static constexpr uint16_t ctsServiceId {0x1805};
-            static constexpr uint16_t currentTimeCharacteristicId {0x2a2b};
-
-            static constexpr ble_uuid16_t ctsServiceUuid {
-                    .u { .type = BLE_UUID_TYPE_16 },
-                    .value = ctsServiceId
-            };
-            static constexpr ble_uuid16_t currentTimeCharacteristicUuid {
-                    .u { .type = BLE_UUID_TYPE_16 },
-                    .value = currentTimeCharacteristicId
-            };
-
-            uint16_t currentTimeHandle;
-            DateTime& dateTimeController;
-            bool isDiscovered = false;
-            uint16_t ctsStartHandle;
-            uint16_t ctsEndHandle;
-        };
-    }
-}
\ No newline at end of file




diff --git a/src/Components/Ble/CurrentTimeService.cpp b/src/Components/Ble/CurrentTimeService.cpp
deleted file mode 100644
index 3a6264e2bebd353b2d5a40173b823593ced61103..0000000000000000000000000000000000000000
--- a/src/Components/Ble/CurrentTimeService.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include "CurrentTimeService.h"
-#include <hal/nrf_rtc.h>
-
-using namespace Pinetime::Controllers;
-
-constexpr ble_uuid16_t CurrentTimeService::ctsUuid;
-constexpr ble_uuid16_t CurrentTimeService::ctChrUuid;
-
-
-int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
-  auto cts = static_cast<CurrentTimeService*>(arg);
-  return cts->OnTimeAccessed(conn_handle, attr_handle, ctxt);
-}
-
-void CurrentTimeService::Init() {
-  int res;
-  res = ble_gatts_count_cfg(serviceDefinition);
-  ASSERT(res == 0);
-
-  res = ble_gatts_add_svcs(serviceDefinition);
-  ASSERT(res == 0);
-}
-
-
-int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle,
-                                                    struct ble_gatt_access_ctxt *ctxt) {
-
-    NRF_LOG_INFO("Setting time...");
-
-  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
-    CtsData result;
-    os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &result);
-
-    NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
-            result.month, result.dayofmonth,
-            result.hour, result.minute, result.second);
-
-    m_dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
-                        0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
-
-  } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
-    CtsData currentDateTime;
-    currentDateTime.year = m_dateTimeController.Year();
-    currentDateTime.month = static_cast<u_int8_t>(m_dateTimeController.Month());
-    currentDateTime.dayofmonth = m_dateTimeController.Day();
-    currentDateTime.hour = m_dateTimeController.Hours();
-    currentDateTime.minute = m_dateTimeController.Minutes();
-    currentDateTime.second = m_dateTimeController.Seconds();
-    currentDateTime.millis = 0;
-
-
-    int res = os_mbuf_append(ctxt->om, &currentDateTime, sizeof(CtsData));
-    return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
-
-  }
-
-  return 0;
-}
-
-CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) :
-        characteristicDefinition{
-                {
-                        .uuid = (ble_uuid_t *) &ctChrUuid,
-                        .access_cb = CTSCallback,
-
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
-                },
-                {
-                  0
-                }
-        },
-        serviceDefinition{
-                {
-                        /* Device Information Service */
-                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
-                        .uuid = (ble_uuid_t *) &ctsUuid,
-                        .characteristics = characteristicDefinition
-                },
-                {
-                        0
-                },
-        }, m_dateTimeController{dateTimeController} {
-
-}
-




diff --git a/src/Components/Ble/CurrentTimeService.h b/src/Components/Ble/CurrentTimeService.h
deleted file mode 100644
index 58bc5ba6397b188d0282ae3cfc3a296f1ad1f73b..0000000000000000000000000000000000000000
--- a/src/Components/Ble/CurrentTimeService.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-#include <cstdint>
-#include <array>
-#include <Components/DateTime/DateTimeController.h>
-#include <host/ble_gap.h>
-
-namespace Pinetime {
-  namespace Controllers {
-    class CurrentTimeService {
-      public:
-        CurrentTimeService(DateTime &dateTimeController);
-        void Init();
-
-        int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle,
-                                    struct ble_gatt_access_ctxt *ctxt);
-
-      private:
-        static constexpr uint16_t ctsId {0x1805};
-        static constexpr uint16_t ctsCharId {0x2a2b};
-
-        static constexpr ble_uuid16_t ctsUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = ctsId
-        };
-
-        static constexpr ble_uuid16_t ctChrUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = ctsCharId
-        };
-
-        struct ble_gatt_chr_def characteristicDefinition[2];
-        struct ble_gatt_svc_def serviceDefinition[2];
-
-        typedef struct __attribute__((packed)) {
-          uint16_t year;
-          uint8_t month;
-          uint8_t dayofmonth;
-          uint8_t hour;
-          uint8_t minute;
-          uint8_t second;
-          uint8_t millis;
-          uint8_t reason;
-        } CtsData;
-
-        DateTime &m_dateTimeController;
-    };
-  }
-}




diff --git a/src/Components/Ble/DeviceInformationService.cpp b/src/Components/Ble/DeviceInformationService.cpp
deleted file mode 100644
index 406db1cff7a319e03758c013176893513307cd86..0000000000000000000000000000000000000000
--- a/src/Components/Ble/DeviceInformationService.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#include "DeviceInformationService.h"
-
-using namespace Pinetime::Controllers;
-
-constexpr ble_uuid16_t DeviceInformationService::manufacturerNameUuid;
-constexpr ble_uuid16_t DeviceInformationService::modelNumberUuid;
-constexpr ble_uuid16_t DeviceInformationService::serialNumberUuid;
-constexpr ble_uuid16_t DeviceInformationService::fwRevisionUuid;
-constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid;
-constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid;
-constexpr ble_uuid16_t DeviceInformationService::swRevisionUuid;
-
-
-int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
-  auto deviceInformationService = static_cast<DeviceInformationService*>(arg);
-  return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt);
-}
-
-void DeviceInformationService::Init() {
-  int res = 0;
-  res = ble_gatts_count_cfg(serviceDefinition);
-  ASSERT(res == 0);
-
-  res = ble_gatts_add_svcs(serviceDefinition);
-  ASSERT(res == 0);
-}
-
-
-int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
-                                                    struct ble_gatt_access_ctxt *ctxt) {
-  const char *str;
-
-  switch (ble_uuid_u16(ctxt->chr->uuid)) {
-    case manufacturerNameId:
-      str = manufacturerName;
-      break;
-    case modelNumberId:
-      str = modelNumber;
-      break;
-    case serialNumberId:
-      str = serialNumber;
-      break;
-    case fwRevisionId:
-      str = fwRevision;
-      break;
-    case hwRevisionId:
-      str = hwRevision;
-      break;
-    case swRevisionId:
-      str = swRevision;
-      break;
-    default:
-      return BLE_ATT_ERR_UNLIKELY;
-  }
-
-  int res = os_mbuf_append(ctxt->om, str, strlen(str));
-  return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
-}
-
-DeviceInformationService::DeviceInformationService() :
-        characteristicDefinition{
-                {
-                        .uuid = (ble_uuid_t *) &manufacturerNameUuid,
-                        .access_cb = DeviceInformationCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_READ,
-                },
-                {
-                        .uuid = (ble_uuid_t *) &modelNumberUuid,
-                        .access_cb = DeviceInformationCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_READ,
-                },
-                {
-                        .uuid = (ble_uuid_t *) &serialNumberUuid,
-                        .access_cb = DeviceInformationCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_READ,
-                },
-                {
-                        .uuid = (ble_uuid_t *) &fwRevisionUuid,
-                        .access_cb = DeviceInformationCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_READ,
-                },
-                {
-                        .uuid = (ble_uuid_t *) &hwRevisionUuid,
-                        .access_cb = DeviceInformationCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_READ,
-                },
-                {
-                        .uuid = (ble_uuid_t *) &swRevisionUuid,
-                        .access_cb = DeviceInformationCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_READ,
-                },
-                {
-                  0
-                }
-        },
-        serviceDefinition{
-                {
-                        /* Device Information Service */
-                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
-                        .uuid = (ble_uuid_t *) &deviceInfoUuid,
-                        .characteristics = characteristicDefinition
-                },
-                {
-                        0
-                },
-        }
-         {
-
-}
-




diff --git a/src/Components/Ble/DeviceInformationService.h b/src/Components/Ble/DeviceInformationService.h
deleted file mode 100644
index 25ab8402ea54573372ea6e5012bd74795e243f82..0000000000000000000000000000000000000000
--- a/src/Components/Ble/DeviceInformationService.h
+++ /dev/null
@@ -1,76 +0,0 @@
-#pragma once
-#include <cstdint>
-#include <array>
-
-#include <host/ble_gap.h>
-#include <Version.h>
-
-namespace Pinetime {
-  namespace Controllers {
-    class DeviceInformationService {
-      public:
-        DeviceInformationService();
-        void Init();
-
-        int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
-                                    struct ble_gatt_access_ctxt *ctxt);
-
-      private:
-        static constexpr uint16_t deviceInfoId {0x180a};
-        static constexpr uint16_t manufacturerNameId {0x2a29};
-        static constexpr uint16_t modelNumberId {0x2a24};
-        static constexpr uint16_t serialNumberId {0x2a25};
-        static constexpr uint16_t fwRevisionId {0x2a26};
-        static constexpr uint16_t hwRevisionId {0x2a27};
-        static constexpr uint16_t swRevisionId {0x2a28};
-
-        static constexpr const char* manufacturerName = "PINE64";
-        static constexpr const char* modelNumber = "PineTime";
-        static constexpr const char* hwRevision = "1.0.0";
-        static constexpr const char* serialNumber = "0";
-        static constexpr const char* fwRevision =  Version::VersionString();
-        static constexpr const char* swRevision = "InfiniTime";
-
-
-        static constexpr ble_uuid16_t deviceInfoUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = deviceInfoId
-        };
-
-        static constexpr ble_uuid16_t manufacturerNameUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = manufacturerNameId
-        };
-
-        static constexpr ble_uuid16_t modelNumberUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = modelNumberId
-        };
-
-        static constexpr ble_uuid16_t serialNumberUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = serialNumberId
-        };
-
-        static constexpr ble_uuid16_t fwRevisionUuid {
-                .u { .type = BLE_UUID_TYPE_16 },
-                .value = fwRevisionId
-        };
-
-        static constexpr ble_uuid16_t hwRevisionUuid {
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = hwRevisionId
-        };
-
-        static constexpr ble_uuid16_t swRevisionUuid {
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = swRevisionId
-        };
-
-        struct ble_gatt_chr_def characteristicDefinition[7];
-        struct ble_gatt_svc_def serviceDefinition[2];
-
-
-    };
-  }
-}
\ No newline at end of file




diff --git a/src/Components/Ble/DfuService.cpp b/src/Components/Ble/DfuService.cpp
deleted file mode 100644
index fcbefdd037591e22c34bd2dad04670b854aa47b7..0000000000000000000000000000000000000000
--- a/src/Components/Ble/DfuService.cpp
+++ /dev/null
@@ -1,440 +0,0 @@
-#include <Components/Ble/BleController.h>
-#include <SystemTask/SystemTask.h>
-#include <cstring>
-#include "DfuService.h"
-
-using namespace Pinetime::Controllers;
-
-constexpr ble_uuid128_t DfuService::serviceUuid;
-constexpr ble_uuid128_t DfuService::controlPointCharacteristicUuid;
-constexpr ble_uuid128_t DfuService::revisionCharacteristicUuid;
-constexpr ble_uuid128_t DfuService::packetCharacteristicUuid;
-
-int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle,
-                       struct ble_gatt_access_ctxt *ctxt, void *arg) {
-  auto dfuService = static_cast<DfuService *>(arg);
-  return dfuService->OnServiceData(conn_handle, attr_handle, ctxt);
-}
-
-void NotificationTimerCallback(TimerHandle_t xTimer) {
-  auto notificationManager = static_cast<DfuService::NotificationManager *>(pvTimerGetTimerID(xTimer));
-  notificationManager->OnNotificationTimer();
-}
-
-void TimeoutTimerCallback(TimerHandle_t xTimer) {
-  auto dfuService = static_cast<DfuService *>(pvTimerGetTimerID(xTimer));
-  dfuService->OnTimeout();
-}
-
-DfuService::DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController,
-                       Pinetime::Drivers::SpiNorFlash &spiNorFlash) :
-        systemTask{systemTask},
-        bleController{bleController},
-        dfuImage{spiNorFlash},
-        characteristicDefinition{
-                {
-                        .uuid = (ble_uuid_t *) &packetCharacteristicUuid,
-                        .access_cb = DfuServiceCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
-                        .val_handle = nullptr,
-                },
-                {
-                        .uuid = (ble_uuid_t *) &controlPointCharacteristicUuid,
-                        .access_cb = DfuServiceCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
-                        .val_handle = nullptr,
-                },
-                {
-                        .uuid = (ble_uuid_t *) &revisionCharacteristicUuid,
-                        .access_cb = DfuServiceCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_READ,
-                        .val_handle = &revision,
-
-                },
-                {
-                        0
-                }
-
-        },
-        serviceDefinition{
-                {
-                        /* Device Information Service */
-                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
-                        .uuid = (ble_uuid_t *) &serviceUuid,
-                        .characteristics = characteristicDefinition
-                },
-                {
-                        0
-                },
-        } {
-  timeoutTimer = xTimerCreate ("notificationTimer", 10000, pdFALSE, this, TimeoutTimerCallback);
-}
-
-void DfuService::Init() {
-  int res;
-  res = ble_gatts_count_cfg(serviceDefinition);
-  ASSERT(res == 0);
-
-  res = ble_gatts_add_svcs(serviceDefinition);
-  ASSERT(res == 0);
-}
-
-int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) {
-  if(bleController.IsFirmwareUpdating()){
-    xTimerStart(timeoutTimer, 0);
-  }
-
-
-  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &packetCharacteristicUuid, nullptr,
-                     &packetCharacteristicHandle);
-  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &controlPointCharacteristicUuid, nullptr,
-                     &controlPointCharacteristicHandle);
-  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &revisionCharacteristicUuid, nullptr,
-                     &revisionCharacteristicHandle);
-
-  if (attributeHandle == packetCharacteristicHandle) {
-    if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
-      return WritePacketHandler(connectionHandle, context->om);
-    else return 0;
-  } else if (attributeHandle == controlPointCharacteristicHandle) {
-    if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
-      return ControlPointHandler(connectionHandle, context->om);
-    else return 0;
-  } else if (attributeHandle == revisionCharacteristicHandle) {
-    if (context->op == BLE_GATT_ACCESS_OP_READ_CHR)
-      return SendDfuRevision(context->om);
-    else return 0;
-  } else {
-    NRF_LOG_INFO("[DFU] Unknown Characteristic : %d", attributeHandle);
-    return 0;
-  }
-}
-
-int DfuService::SendDfuRevision(os_mbuf *om) const {
-  int res = os_mbuf_append(om, &revision, sizeof(revision));
-  return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
-}
-
-int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
-  switch (state) {
-    case States::Start: {
-      softdeviceSize = om->om_data[0] + (om->om_data[1] << 8) + (om->om_data[2] << 16) + (om->om_data[3] << 24);
-      bootloaderSize = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
-      applicationSize = om->om_data[8] + (om->om_data[9] << 8) + (om->om_data[10] << 16) + (om->om_data[11] << 24);
-      bleController.FirmwareUpdateTotalBytes(applicationSize);
-      NRF_LOG_INFO("[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize,
-                   bootloaderSize, applicationSize);
-
-      dfuImage.Erase();
-
-      uint8_t data[]{16, 1, 1};
-      notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
-      state = States::Init;
-    }
-      return 0;
-    case States::Init: {
-      uint16_t deviceType = om->om_data[0] + (om->om_data[1] << 8);
-      uint16_t deviceRevision = om->om_data[2] + (om->om_data[3] << 8);
-      uint32_t applicationVersion =
-              om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
-      uint16_t softdeviceArrayLength = om->om_data[8] + (om->om_data[9] << 8);
-      uint16_t sd[softdeviceArrayLength];
-      for (int i = 0; i < softdeviceArrayLength; i++) {
-        sd[i] = om->om_data[10 + (i * 2)] + (om->om_data[10 + (i * 2) + 1] << 8);
-      }
-      expectedCrc =
-              om->om_data[10 + (softdeviceArrayLength * 2)] + (om->om_data[10 + (softdeviceArrayLength * 2) + 1] << 8);
-
-      NRF_LOG_INFO(
-              "[DFU] -> Init data received : deviceType = %d, deviceRevision = %d, applicationVersion = %d, nb SD = %d, First SD = %d, CRC = %u",
-              deviceType, deviceRevision, applicationVersion, softdeviceArrayLength, sd[0], expectedCrc);
-
-      return 0;
-    }
-
-    case States::Data: {
-      nbPacketReceived++;
-      dfuImage.Append(om->om_data, om->om_len);
-      bytesReceived += om->om_len;
-      bleController.FirmwareUpdateCurrentBytes(bytesReceived);
-
-      if ((nbPacketReceived % nbPacketsToNotify) == 0 && bytesReceived != applicationSize) {
-        uint8_t data[5]{static_cast<uint8_t>(Opcodes::PacketReceiptNotification),
-                        (uint8_t) (bytesReceived & 0x000000FFu), (uint8_t) (bytesReceived >> 8u),
-                        (uint8_t) (bytesReceived >> 16u), (uint8_t) (bytesReceived >> 24u)};
-        NRF_LOG_INFO("[DFU] -> Send packet notification: %d bytes received", bytesReceived);
-        notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 5);
-      }
-      if (dfuImage.IsComplete()) {
-        uint8_t data[3]{static_cast<uint8_t>(Opcodes::Response),
-                        static_cast<uint8_t>(Opcodes::ReceiveFirmwareImage),
-                        static_cast<uint8_t>(ErrorCodes::NoError)};
-        NRF_LOG_INFO("[DFU] -> Send packet notification : all bytes received!");
-        notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
-        state = States::Validate;
-      }
-    }
-      return 0;
-    default:
-      // Invalid state
-      return 0;
-  }
-  return 0;
-}
-
-int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
-  auto opcode = static_cast<Opcodes>(om->om_data[0]);
-  NRF_LOG_INFO("[DFU] -> ControlPointHandler");
-
-  switch (opcode) {
-    case Opcodes::StartDFU: {
-      if (state != States::Idle && state != States::Start) {
-        NRF_LOG_INFO("[DFU] -> Start DFU requested, but we are not in Idle state");
-        return 0;
-      }
-      if (state == States::Start) {
-        NRF_LOG_INFO("[DFU] -> Start DFU requested, but we are already in Start state");
-        return 0;
-      }
-      auto imageType = static_cast<ImageTypes>(om->om_data[1]);
-      if (imageType == ImageTypes::Application) {
-        NRF_LOG_INFO("[DFU] -> Start DFU, mode = Application");
-        state = States::Start;
-        bleController.StartFirmwareUpdate();
-        bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Running);
-        bleController.FirmwareUpdateTotalBytes(0xffffffffu);
-        bleController.FirmwareUpdateCurrentBytes(0);
-        systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateStarted);
-        return 0;
-      } else {
-        NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType);
-        return 0;
-      }
-    }
-      break;
-    case Opcodes::InitDFUParameters: {
-      if (state != States::Init) {
-        NRF_LOG_INFO("[DFU] -> Init DFU requested, but we are not in Init state");
-        return 0;
-      }
-      bool isInitComplete = (om->om_data[1] != 0);
-      NRF_LOG_INFO("[DFU] -> Init DFU parameters %s", isInitComplete ? " complete" : " not complete");
-
-      if (isInitComplete) {
-        uint8_t data[3] {
-                static_cast<uint8_t>(Opcodes::Response),
-                static_cast<uint8_t>(Opcodes::InitDFUParameters),
-                (isInitComplete ? uint8_t{1} : uint8_t{0})
-        };
-        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
-        return 0;
-      }
-    }
-      return 0;
-    case Opcodes::PacketReceiptNotificationRequest:
-      nbPacketsToNotify = om->om_data[1];
-      NRF_LOG_INFO("[DFU] -> Receive Packet Notification Request, nb packet = %d", nbPacketsToNotify);
-      return 0;
-    case Opcodes::ReceiveFirmwareImage:
-      if (state != States::Init) {
-        NRF_LOG_INFO("[DFU] -> Receive firmware image requested, but we are not in Start Init");
-        return 0;
-      }
-      // TODO the chunk size is dependant of the implementation of the host application...
-      dfuImage.Init(20, applicationSize, expectedCrc);
-      NRF_LOG_INFO("[DFU] -> Starting receive firmware");
-      state = States::Data;
-      return 0;
-    case Opcodes::ValidateFirmware: {
-      if (state != States::Validate) {
-        NRF_LOG_INFO("[DFU] -> Validate firmware image requested, but we are not in Data state %d", state);
-        return 0;
-      }
-
-      NRF_LOG_INFO("[DFU] -> Validate firmware image requested -- %d", connectionHandle);
-
-      if(dfuImage.Validate()){
-        state = States::Validated;
-        bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
-        NRF_LOG_INFO("Image OK");
-
-        uint8_t data[3] {
-                static_cast<uint8_t>(Opcodes::Response),
-                static_cast<uint8_t>(Opcodes::ValidateFirmware),
-                static_cast<uint8_t>(ErrorCodes::NoError)
-        };
-        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
-      } else {
-        bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
-        NRF_LOG_INFO("Image Error : bad CRC");
-
-        uint8_t data[3] {
-                static_cast<uint8_t>(Opcodes::Response),
-                static_cast<uint8_t>(Opcodes::ValidateFirmware),
-                static_cast<uint8_t>(ErrorCodes::CrcError)
-        };
-        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
-      }
-
-      return 0;
-    }
-    case Opcodes::ActivateImageAndReset:
-      if (state != States::Validated) {
-        NRF_LOG_INFO("[DFU] -> Activate image and reset requested, but we are not in Validated state");
-        return 0;
-      }
-      NRF_LOG_INFO("[DFU] -> Activate image and reset!");
-      bleController.StopFirmwareUpdate();
-      systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
-      Reset();
-      bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
-      return 0;
-    default:
-      return 0;
-  }
-}
-
-void DfuService::OnTimeout() {
-  Reset();
-}
-
-void DfuService::Reset() {
-  state = States::Idle;
-  nbPacketsToNotify = 0;
-  nbPacketReceived = 0;
-  bytesReceived = 0;
-  softdeviceSize = 0;
-  bootloaderSize = 0;
-  applicationSize = 0;
-  expectedCrc = 0;
-  notificationManager.Reset();
-  bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
-  bleController.StopFirmwareUpdate();
-  systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
-}
-
-DfuService::NotificationManager::NotificationManager() {
-  timer = xTimerCreate ("notificationTimer", 1000, pdFALSE, this, NotificationTimerCallback);
-}
-
-bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t *data, size_t s) {
-  if(size != 0 || s > 10)
-    return false;
-
-  connectionHandle = connection;
-  characteristicHandle = charactHandle;
-  size = s;
-  std::memcpy(buffer, data, size);
-  xTimerStart(timer, 0);
-  return true;
-}
-
-void DfuService::NotificationManager::OnNotificationTimer() {
-  if(size > 0) {
-    Send(connectionHandle, characteristicHandle, buffer, size);
-    size = 0;
-  }
-}
-
-void DfuService::NotificationManager::Send(uint16_t connection, uint16_t charactHandle, const uint8_t *data, const size_t s) {
-  auto *om = ble_hs_mbuf_from_flat(data, s);
-  auto ret = ble_gattc_notify_custom(connection, charactHandle, om);
-  ASSERT(ret == 0);
-}
-
-void DfuService::NotificationManager::Reset() {
-  connectionHandle = 0;
-  characteristicHandle = 0;
-  size = 0;
-  xTimerStop(timer, 0);
-}
-
-void DfuService::DfuImage::Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc) {
-  if(chunkSize != 20) return;
-  this->chunkSize = chunkSize;
-  this->totalSize = totalSize;
-  this->expectedCrc = expectedCrc;
-  this->ready = true;
-}
-
-void DfuService::DfuImage::Append(uint8_t *data, size_t size) {
-  if(!ready) return;
-  ASSERT(size <= 20);
-
-  std::memcpy(tempBuffer + bufferWriteIndex, data, size);
-  bufferWriteIndex += size;
-
-  if(bufferWriteIndex == bufferSize) {
-    spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
-    totalWriteIndex += bufferWriteIndex;
-    bufferWriteIndex = 0;
-  }
-
-  if(bufferWriteIndex > 0 && totalWriteIndex + bufferWriteIndex == totalSize) {
-    spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
-    totalWriteIndex += bufferWriteIndex;
-    if (totalSize < maxSize)
-      WriteMagicNumber();
-  }
-}
-
-void DfuService::DfuImage::WriteMagicNumber() {
-  uint32_t magic[4] = { // TODO When this variable is a static constexpr, the values written to the memory are not correct. Why?
-          0xf395c277,
-          0x7fefd260,
-          0x0f505235,
-          0x8079b62c,
-  };
-
-  uint32_t offset = writeOffset + (maxSize - (4 * sizeof(uint32_t)));
-  spiNorFlash.Write(offset, reinterpret_cast<const uint8_t *>(magic), 4 * sizeof(uint32_t));
-}
-
-void DfuService::DfuImage::Erase() {
-  for (size_t erased = 0; erased < maxSize; erased += 0x1000) {
-    spiNorFlash.SectorErase(writeOffset + erased);
-  }
-}
-
-bool DfuService::DfuImage::Validate() {
-  uint32_t chunkSize = 200;
-  size_t currentOffset = 0;
-  uint16_t crc = 0;
-
-  bool first = true;
-  while (currentOffset < totalSize) {
-    uint32_t readSize = (totalSize - currentOffset) > chunkSize ? chunkSize : (totalSize - currentOffset);
-
-    spiNorFlash.Read(writeOffset + currentOffset, tempBuffer, readSize);
-    if (first) {
-      crc = ComputeCrc(tempBuffer, readSize, NULL);
-      first = false;
-    } else
-      crc = ComputeCrc(tempBuffer, readSize, &crc);
-    currentOffset += readSize;
-  }
-
-  return (crc == expectedCrc);
-}
-
-uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const *p_data, uint32_t size, uint16_t const *p_crc) {
-  uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
-
-  for (uint32_t i = 0; i < size; i++) {
-    crc = (uint8_t) (crc >> 8) | (crc << 8);
-    crc ^= p_data[i];
-    crc ^= (uint8_t) (crc & 0xFF) >> 4;
-    crc ^= (crc << 8) << 4;
-    crc ^= ((crc & 0xFF) << 4) << 1;
-  }
-
-  return crc;
-}
-
-bool DfuService::DfuImage::IsComplete() {
-  if(!ready) return false;
-  return totalWriteIndex == totalSize;
-}




diff --git a/src/Components/Ble/DfuService.h b/src/Components/Ble/DfuService.h
deleted file mode 100644
index d7ba460c70b767a5ee03520f88a0550e7121b69d..0000000000000000000000000000000000000000
--- a/src/Components/Ble/DfuService.h
+++ /dev/null
@@ -1,161 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <array>
-
-#include <host/ble_gap.h>
-
-namespace Pinetime {
-  namespace System {
-    class SystemTask;
-  }
-  namespace Drivers {
-    class SpiNorFlash;
-  }
-  namespace Controllers {
-    class Ble;
-
-    class DfuService {
-      public:
-        DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController,
-                   Pinetime::Drivers::SpiNorFlash &spiNorFlash);
-        void Init();
-        int OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
-        void OnTimeout();
-        void Reset();
-
-        class NotificationManager {
-          public:
-            NotificationManager();
-            bool AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t *data, size_t size);
-            void Send(uint16_t connection, uint16_t characteristicHandle, const uint8_t *data, const size_t s);
-          private:
-            TimerHandle_t timer;
-            uint16_t connectionHandle = 0;
-            uint16_t characteristicHandle = 0;
-            size_t size = 0;
-            uint8_t buffer[10];
-          public:
-            void OnNotificationTimer();
-            void Reset();
-        };
-        class DfuImage {
-          public:
-            DfuImage(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash{spiNorFlash} {}
-            void Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc);
-            void Erase();
-            void Append(uint8_t* data, size_t size);
-            bool Validate();
-            bool IsComplete();
-
-          private:
-            Pinetime::Drivers::SpiNorFlash& spiNorFlash;
-            static constexpr size_t bufferSize = 200;
-            bool ready = false;
-            size_t chunkSize = 0;
-            size_t totalSize = 0;
-            size_t maxSize = 475136;
-            size_t bufferWriteIndex = 0;
-            size_t totalWriteIndex = 0;
-            static constexpr size_t writeOffset = 0x40000;
-            uint8_t tempBuffer[bufferSize];
-            uint16_t expectedCrc = 0;
-
-            void WriteMagicNumber();
-            uint16_t ComputeCrc(uint8_t const *p_data, uint32_t size, uint16_t const *p_crc);
-
-        };
-
-      private:
-        Pinetime::System::SystemTask &systemTask;
-        Pinetime::Controllers::Ble &bleController;
-        DfuImage dfuImage;
-        NotificationManager notificationManager;
-
-        static constexpr uint16_t dfuServiceId{0x1530};
-        static constexpr uint16_t packetCharacteristicId{0x1532};
-        static constexpr uint16_t controlPointCharacteristicId{0x1531};
-        static constexpr uint16_t revisionCharacteristicId{0x1534};
-
-        uint16_t revision{0x0008};
-
-        static constexpr ble_uuid128_t serviceUuid{
-                .u {.type = BLE_UUID_TYPE_128},
-                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
-                          0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}
-        };
-
-        static constexpr ble_uuid128_t packetCharacteristicUuid{
-                .u {.type = BLE_UUID_TYPE_128},
-                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
-                          0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00}
-        };
-
-        static constexpr ble_uuid128_t controlPointCharacteristicUuid{
-                .u {.type = BLE_UUID_TYPE_128},
-                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
-                          0xDE, 0xEF, 0x12, 0x12, 0x31, 0x15, 0x00, 0x00}
-        };
-
-        static constexpr ble_uuid128_t revisionCharacteristicUuid{
-                .u {.type = BLE_UUID_TYPE_128},
-                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
-                          0xDE, 0xEF, 0x12, 0x12, 0x34, 0x15, 0x00, 0x00}
-        };
-
-        struct ble_gatt_chr_def characteristicDefinition[4];
-        struct ble_gatt_svc_def serviceDefinition[2];
-        uint16_t packetCharacteristicHandle;
-        uint16_t controlPointCharacteristicHandle;
-        uint16_t revisionCharacteristicHandle;
-
-        enum class States : uint8_t {
-            Idle, Init, Start, Data, Validate, Validated
-        };
-        States state = States::Idle;
-
-        enum class ImageTypes : uint8_t {
-            NoImage = 0x00,
-            SoftDevice = 0x01,
-            Bootloader = 0x02,
-            SoftDeviceAndBootloader = 0x03,
-            Application = 0x04
-        };
-
-        enum class Opcodes : uint8_t {
-            StartDFU = 0x01,
-            InitDFUParameters = 0x02,
-            ReceiveFirmwareImage = 0x03,
-            ValidateFirmware = 0x04,
-            ActivateImageAndReset = 0x05,
-            PacketReceiptNotificationRequest = 0x08,
-            Response = 0x10,
-            PacketReceiptNotification = 0x11
-        };
-
-        enum class ErrorCodes {
-            NoError = 0x01,
-            InvalidState = 0x02,
-            NotSupported = 0x03,
-            DataSizeExceedsLimits = 0x04,
-            CrcError = 0x05,
-            OperationFailed = 0x06
-        };
-
-        uint8_t nbPacketsToNotify = 0;
-        uint32_t nbPacketReceived = 0;
-        uint32_t bytesReceived = 0;
-
-        uint32_t softdeviceSize = 0;
-        uint32_t bootloaderSize = 0;
-        uint32_t applicationSize = 0;
-        uint16_t expectedCrc = 0;
-
-        int SendDfuRevision(os_mbuf *om) const;
-        int WritePacketHandler(uint16_t connectionHandle, os_mbuf *om);
-        int ControlPointHandler(uint16_t connectionHandle, os_mbuf *om);
-
-        TimerHandle_t timeoutTimer;
-    };
-  }
-}
\ No newline at end of file




diff --git a/src/Components/Ble/ImmediateAlertService.cpp b/src/Components/Ble/ImmediateAlertService.cpp
deleted file mode 100644
index d2c4cffb0d01bd76fb36c0fe1067add572b45b99..0000000000000000000000000000000000000000
--- a/src/Components/Ble/ImmediateAlertService.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "ImmediateAlertService.h"
-#include <SystemTask/SystemTask.h>
-#include "AlertNotificationService.h"
-
-using namespace Pinetime::Controllers;
-
-constexpr ble_uuid16_t ImmediateAlertService::immediateAlertServiceUuid;
-constexpr ble_uuid16_t ImmediateAlertService::alertLevelUuid;
-
-namespace {
-  int AlertLevelCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
-    auto *immediateAlertService = static_cast<ImmediateAlertService *>(arg);
-    return immediateAlertService->OnAlertLevelChanged(conn_handle, attr_handle, ctxt);
-  }
-
-  const char* ToString(ImmediateAlertService::Levels level) {
-    switch (level) {
-      case ImmediateAlertService::Levels::NoAlert: return "Alert : None";
-      case ImmediateAlertService::Levels::HighAlert: return "Alert : High";
-      case ImmediateAlertService::Levels::MildAlert: return "Alert : Mild";
-      default: return "";
-    }
-  }
-}
-
-ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask &systemTask,
-                                             Pinetime::Controllers::NotificationManager &notificationManager) :
-        systemTask{systemTask},
-        notificationManager{notificationManager},
-        characteristicDefinition{
-                {
-                        .uuid = (ble_uuid_t *) &alertLevelUuid,
-                        .access_cb = AlertLevelCallback,
-                        .arg = this,
-                        .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
-                        .val_handle = &alertLevelHandle
-                },
-                {
-                        0
-                }
-        },
-        serviceDefinition{
-                {
-                        /* Device Information Service */
-                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
-                        .uuid = (ble_uuid_t *) &immediateAlertServiceUuid,
-                        .characteristics = characteristicDefinition
-                },
-                {
-                        0
-                },
-        }{
-
-}
-
-void ImmediateAlertService::Init() {
-  int res = 0;
-  res = ble_gatts_count_cfg(serviceDefinition);
-  ASSERT(res == 0);
-
-  res = ble_gatts_add_svcs(serviceDefinition);
-  ASSERT(res == 0);
-}
-
-int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) {
-  if(attributeHandle == alertLevelHandle) {
-    if(context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
-      auto alertLevel = static_cast<Levels>(context->om->om_data[0]);
-      auto* alertString = ToString(alertLevel);
-      notificationManager.Push(Pinetime::Controllers::NotificationManager::Categories::SimpleAlert, alertString, strlen(alertString));
-      systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
-    }
-  }
-
-  return 0;
-}
\ No newline at end of file




diff --git a/src/Components/Ble/ImmediateAlertService.h b/src/Components/Ble/ImmediateAlertService.h
deleted file mode 100644
index c42846c485cd9fe25713bcc511674ee3ba9c87f9..0000000000000000000000000000000000000000
--- a/src/Components/Ble/ImmediateAlertService.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#pragma once
-#include <host/ble_gap.h>
-
-namespace Pinetime {
-  namespace System {
-    class SystemTask;
-  }
-  namespace Controllers {
-    class NotificationManager;
-    class ImmediateAlertService {
-      public:
-        enum class Levels : uint8_t {
-            NoAlert = 0,
-            MildAlert = 1,
-            HighAlert = 2
-        };
-
-        ImmediateAlertService(Pinetime::System::SystemTask &systemTask,
-                              Pinetime::Controllers::NotificationManager &notificationManager);
-        void Init();
-        int OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
-
-      private:
-        Pinetime::System::SystemTask& systemTask;
-        NotificationManager& notificationManager;
-
-        static constexpr uint16_t immediateAlertServiceId {0x1802};
-        static constexpr uint16_t alertLevelId {0x2A06};
-
-        static constexpr ble_uuid16_t immediateAlertServiceUuid {
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = immediateAlertServiceId
-        };
-
-        static constexpr ble_uuid16_t alertLevelUuid {
-                .u {.type = BLE_UUID_TYPE_16},
-                .value = alertLevelId
-        };
-
-        struct ble_gatt_chr_def characteristicDefinition[3];
-        struct ble_gatt_svc_def serviceDefinition[2];
-
-        uint16_t alertLevelHandle;
-    };
-  }
-}




diff --git a/src/Components/Ble/MusicService.cpp b/src/Components/Ble/MusicService.cpp
deleted file mode 100644
index b5fa53562bd2fd054f370ab73adee332ad18e859..0000000000000000000000000000000000000000
--- a/src/Components/Ble/MusicService.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-#include <SystemTask/SystemTask.h>
-#include "MusicService.h"
-
-int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
-  auto musicService = static_cast<Pinetime::Controllers::MusicService*>(arg);
-  return musicService->OnCommand(conn_handle, attr_handle, ctxt);
-}
-
-Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask &system) : m_system(system)
-{
-    msUuid.value[11] = msId[0];
-    msUuid.value[12] = msId[1];
-    msEventCharUuid.value[11] = msEventCharId[0];
-    msEventCharUuid.value[12] = msEventCharId[1];
-    msStatusCharUuid.value[11] = msStatusCharId[0];
-    msStatusCharUuid.value[12] = msStatusCharId[1];
-    msTrackCharUuid.value[11] = msTrackCharId[0];
-    msTrackCharUuid.value[12] = msTrackCharId[1];
-    msArtistCharUuid.value[11] = msArtistCharId[0];
-    msArtistCharUuid.value[12] = msArtistCharId[1];
-    msAlbumCharUuid.value[11] = msAlbumCharId[0];
-    msAlbumCharUuid.value[12] = msAlbumCharId[1];
-
-    characteristicDefinition[0] = { .uuid = (ble_uuid_t*)(&msEventCharUuid),
-                                    .access_cb = MSCallback,
-                                    .arg = this,
-                                    .flags =  BLE_GATT_CHR_F_NOTIFY,
-                                    .val_handle = &m_eventHandle
-    };
-    characteristicDefinition[1] = { .uuid = (ble_uuid_t*)(&msStatusCharUuid),
-                                    .access_cb = MSCallback,
-                                    .arg = this,
-                                    .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
-    };
-    characteristicDefinition[2] = { .uuid = (ble_uuid_t*)(&msTrackCharUuid),
-                                    .access_cb = MSCallback,
-                                    .arg = this,
-                                    .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
-    };
-    characteristicDefinition[3] = { .uuid = (ble_uuid_t*)(&msArtistCharUuid),
-                                    .access_cb = MSCallback,
-                                    .arg = this,
-                                    .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
-    };
-    characteristicDefinition[4] = { .uuid = (ble_uuid_t*)(&msAlbumCharUuid),
-                                    .access_cb = MSCallback,
-                                    .arg = this,
-                                    .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
-    };
-    characteristicDefinition[5] = {0};
-
-    serviceDefinition[0] = {
-                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
-                        .uuid = (ble_uuid_t *) &msUuid,
-                        .characteristics = characteristicDefinition
-    };
-    serviceDefinition[1] = {0};
-
-    m_artist = "Waiting for";
-    m_album = "";
-    m_track = "track information...";
-}
-
-void Pinetime::Controllers::MusicService::Init()
-{
-  int res = 0;
-  res = ble_gatts_count_cfg(serviceDefinition);
-  ASSERT(res == 0);
-
-  res = ble_gatts_add_svcs(serviceDefinition);
-  ASSERT(res == 0);
-}
-
-int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle,
-                                                    struct ble_gatt_access_ctxt *ctxt) {
-
-  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
-        size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
-        uint8_t data[notifSize + 1];
-        data[notifSize] = '\0';
-        os_mbuf_copydata(ctxt->om, 0, notifSize, data);
-        char *s = (char *) &data[0];
-        NRF_LOG_INFO("DATA : %s", s);
-        if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msArtistCharUuid) == 0) {
-            m_artist = s;
-        } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msTrackCharUuid) == 0) {
-            m_track = s;
-        } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msAlbumCharUuid) == 0) {
-            m_album = s;
-        } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msStatusCharUuid) == 0) {
-            m_status = s[0];
-        }
-  }
-  return 0;
-}
-
-std::string Pinetime::Controllers::MusicService::album()
-{
-    return m_album;
-}
-
-std::string Pinetime::Controllers::MusicService::artist()
-{
-    return m_artist;
-}
-
-std::string Pinetime::Controllers::MusicService::track()
-{
-    return m_track;
-}
-
-unsigned char Pinetime::Controllers::MusicService::status()
-{
-    return m_status;
-}
-
-void Pinetime::Controllers::MusicService::event(char event)
-{
-    auto *om = ble_hs_mbuf_from_flat(&event, 1);
-
-    uint16_t connectionHandle = m_system.nimble().connHandle();
-
-    if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
-        return;
-    }
-
-    ble_gattc_notify_custom(connectionHandle, m_eventHandle, om);
-}
-




diff --git a/src/Components/Ble/MusicService.h b/src/Components/Ble/MusicService.h
deleted file mode 100644
index ab6db572b183980efec46bd4942f729b4762d672..0000000000000000000000000000000000000000
--- a/src/Components/Ble/MusicService.h
+++ /dev/null
@@ -1,92 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <array>
-#include <host/ble_gap.h>
-#include <host/ble_uuid.h>
-#include <string>
-
-//c7e50000-78fc-48fe-8e23-43b37a1942d0
-#define MUSIC_SERVICE_UUID_BASE {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0xe5, 0xc7}
-
-namespace Pinetime {
-  namespace System {
-    class SystemTask;
-  }
-  namespace Controllers {
-
-    class MusicService {
-      public:
-        MusicService(Pinetime::System::SystemTask &system);
-        void Init();
-        int OnCommand(uint16_t conn_handle, uint16_t attr_handle,
-                                    struct ble_gatt_access_ctxt *ctxt);
-
-        std::string artist();
-        std::string track();
-        std::string album();
-        unsigned char status();
-
-        void event(char event);
-
-        static const char EVENT_MUSIC_OPEN = 0xe0;
-        static const char EVENT_MUSIC_PLAY = 0x00;
-        static const char EVENT_MUSIC_PAUSE = 0x01;
-        static const char EVENT_MUSIC_NEXT = 0x03;
-        static const char EVENT_MUSIC_PREV = 0x04;
-        static const char EVENT_MUSIC_VOLUP = 0x05;
-        static const char EVENT_MUSIC_VOLDOWN = 0x06;
-        static const char STATUS_MUSIC_PAUSED = 0x00;
-        static const char STATUS_MUSIC_PLAYING = 0x01;
-
-      private:
-        static constexpr uint8_t msId[2] = {0x00, 0x01};
-        static constexpr uint8_t msEventCharId[2] = {0x00, 0x02};
-        static constexpr uint8_t msStatusCharId[2] = {0x00, 0x03};
-        static constexpr uint8_t msArtistCharId[2] = {0x00, 0x04};
-        static constexpr uint8_t msTrackCharId[2] = {0x00, 0x05};
-        static constexpr uint8_t msAlbumCharId[2] = {0x00, 0x06};
-
-        ble_uuid128_t msUuid {
-                .u = { .type = BLE_UUID_TYPE_128 },
-                .value = MUSIC_SERVICE_UUID_BASE
-        };
-
-        ble_uuid128_t msEventCharUuid {
-                .u = { .type = BLE_UUID_TYPE_128 },
-                .value = MUSIC_SERVICE_UUID_BASE
-        };
-        ble_uuid128_t msStatusCharUuid {
-                .u = { .type = BLE_UUID_TYPE_128 },
-                .value = MUSIC_SERVICE_UUID_BASE
-        };
-        ble_uuid128_t msArtistCharUuid {
-                .u = { .type = BLE_UUID_TYPE_128 },
-                .value = MUSIC_SERVICE_UUID_BASE
-        };
-        ble_uuid128_t msTrackCharUuid {
-                .u = { .type = BLE_UUID_TYPE_128 },
-                .value = MUSIC_SERVICE_UUID_BASE
-        };
-        ble_uuid128_t msAlbumCharUuid {
-                .u = { .type = BLE_UUID_TYPE_128 },
-                .value = MUSIC_SERVICE_UUID_BASE
-        };
-
-        struct ble_gatt_chr_def characteristicDefinition[6];
-        struct ble_gatt_svc_def serviceDefinition[2];
-
-        uint16_t m_eventHandle;
-
-        std::string m_artist;
-        std::string m_album;
-        std::string m_track;
-
-        unsigned char m_status;
-
-        Pinetime::System::SystemTask& m_system;
-
-    };
-  }
-}
-




diff --git a/src/Components/Ble/NimbleController.cpp b/src/Components/Ble/NimbleController.cpp
deleted file mode 100644
index b13f9ce3bf7888df78e000a0653bd29afaeac5ad..0000000000000000000000000000000000000000
--- a/src/Components/Ble/NimbleController.cpp
+++ /dev/null
@@ -1,337 +0,0 @@
-
-#include <Components/DateTime/DateTimeController.h>
-
-#include <SystemTask/SystemTask.h>
-#include <Components/Ble/NotificationManager.h>
-#include <hal/nrf_rtc.h>
-
-#include "NimbleController.h"
-#include "MusicService.h"
-#include <services/gatt/ble_svc_gatt.h>
-#include <services/gap/ble_svc_gap.h>
-#include <host/util/util.h>
-#include <host/ble_hs_id.h>
-#include <host/ble_hs.h>
-#include <host/ble_gap.h>
-
-
-
-using namespace Pinetime::Controllers;
-
-// TODO I'm not satisfied by how this code looks like (AlertNotificationClient and CurrentTimeClient must
-// expose too much data, too many callbacks -> NimbleController -> CTS/ANS client.
-// Let's try to improve this code (and keep it working!)
-
-NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
-                                   Pinetime::Controllers::Ble& bleController,
-        DateTime& dateTimeController,
-        Pinetime::Controllers::NotificationManager& notificationManager,
-        Controllers::Battery& batteryController,
-        Pinetime::Drivers::SpiNorFlash& spiNorFlash) :
-        systemTask{systemTask},
-        bleController{bleController},
-        dateTimeController{dateTimeController},
-        notificationManager{notificationManager},
-        spiNorFlash{spiNorFlash},
-        dfuService{systemTask, bleController, spiNorFlash},
-        currentTimeClient{dateTimeController},
-        anService{systemTask, notificationManager},
-        alertNotificationClient{systemTask, notificationManager},
-        currentTimeService{dateTimeController},
-        musicService{systemTask},
-        batteryInformationService{batteryController},
-        immediateAlertService{systemTask, notificationManager} {
-
-}
-
-int GAPEventCallback(struct ble_gap_event *event, void *arg) {
-  auto nimbleController = static_cast<NimbleController*>(arg);
-  return nimbleController->OnGAPEvent(event);
-}
-
-int CurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
-                                                const struct ble_gatt_chr *chr, void *arg) {
-  auto client = static_cast<NimbleController*>(arg);
-  return client->OnCTSCharacteristicDiscoveryEvent(conn_handle, error, chr);
-}
-
-int AlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
-                                                const struct ble_gatt_chr *chr, void *arg) {
-  auto client = static_cast<NimbleController*>(arg);
-  return client->OnANSCharacteristicDiscoveryEvent(conn_handle, error, chr);
-}
-
-int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
-                                   struct ble_gatt_attr *attr, void *arg) {
-  auto client = static_cast<NimbleController*>(arg);
-  return client->OnCurrentTimeReadResult(conn_handle, error, attr);
-}
-
-int AlertNotificationDescriptorDiscoveryEventCallback(uint16_t conn_handle,
-                                                                             const struct ble_gatt_error *error,
-                                                                             uint16_t chr_val_handle,
-                                                                             const struct ble_gatt_dsc *dsc,
-                                                                             void *arg) {
-  auto client = static_cast<NimbleController*>(arg);
-  return client->OnANSDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc);
-}
-
-void NimbleController::Init() {
-  while (!ble_hs_synced()) {}
-
-  ble_svc_gap_init();
-  ble_svc_gatt_init();
-
-  deviceInformationService.Init();
-  currentTimeClient.Init();
-  currentTimeService.Init();
-  musicService.Init();
-  anService.Init();
-  dfuService.Init();
-  batteryInformationService.Init();
-  immediateAlertService.Init();
-  int res;
-  res = ble_hs_util_ensure_addr(0);
-  ASSERT(res == 0);
-  res = ble_hs_id_infer_auto(0, &addrType);
-  ASSERT(res == 0);
-  res = ble_svc_gap_device_name_set(deviceName);
-  ASSERT(res == 0);
-  Pinetime::Controllers::Ble::BleAddress address;
-  res = ble_hs_id_copy_addr(addrType, address.data(), nullptr);
-  ASSERT(res == 0);
-  bleController.AddressType((addrType == 0) ? Ble::AddressTypes::Public : Ble::AddressTypes::Random);
-  bleController.Address(std::move(address));
-
-  res = ble_gatts_start();
-  ASSERT(res == 0);
-}
-
-void NimbleController::StartAdvertising() {
-  if(ble_gap_adv_active()) return;
-
-  ble_svc_gap_device_name_set(deviceName);
-
-  /* set adv parameters */
-  struct ble_gap_adv_params adv_params;
-  struct ble_hs_adv_fields fields;
-  /* advertising payload is split into advertising data and advertising
-     response, because all data cannot fit into single packet; name of device
-     is sent as response to scan request */
-  struct ble_hs_adv_fields rsp_fields;
-
-  /* fill all fields and parameters with zeros */
-  memset(&adv_params, 0, sizeof(adv_params));
-  memset(&fields, 0, sizeof(fields));
-  memset(&rsp_fields, 0, sizeof(rsp_fields));
-
-  adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
-  adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
-
-  fields.flags = BLE_HS_ADV_F_DISC_GEN |
-                 BLE_HS_ADV_F_BREDR_UNSUP;
-//  fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE(
-//          0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-//          0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
-  fields.uuids128 = &dfuServiceUuid;
-  fields.num_uuids128 = 1;
-  fields.uuids128_is_complete = 1;
-  fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
-
-  rsp_fields.name = (uint8_t *)deviceName;
-  rsp_fields.name_len = strlen(deviceName);
-  rsp_fields.name_is_complete = 1;
-
-  ble_gap_adv_set_fields(&fields);
-//  ASSERT(res == 0); // TODO this one sometimes fails with error 22 (notsync)
-
-  ble_gap_adv_rsp_set_fields(&rsp_fields);
-//  ASSERT(res == 0);
-
-  ble_gap_adv_start(addrType, NULL, 180000,
-                          &adv_params, GAPEventCallback, this);
-//  ASSERT(res == 0);// TODO I've disabled these ASSERT as they sometime asserts and reset the mcu.
-  // For now, the advertising is restarted as soon as it ends. There may be a race condition
-  // that prevent the advertising from restarting reliably.
-  // I remove the assert to prevent this uncesseray crash, but in the long term, the management of
-  // the advertising should be improve (better error handling, and advertise for 3 minutes after
-  // the application has been woken up, for example.
-}
-
-int OnAllSvrDisco(uint16_t conn_handle,
-                                 const struct ble_gatt_error *error,
-                                 const struct ble_gatt_svc *service,
-                                 void *arg) {
-  auto nimbleController = static_cast<NimbleController*>(arg);
-  return nimbleController->OnDiscoveryEvent(conn_handle, error, service);
-  return 0;
-}
-
-int NimbleController::OnGAPEvent(ble_gap_event *event) {
-  switch (event->type) {
-    case BLE_GAP_EVENT_ADV_COMPLETE:
-      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE");
-      NRF_LOG_INFO("advertise complete; reason=%dn status=%d", event->adv_complete.reason, event->connect.status);
-      break;
-    case BLE_GAP_EVENT_CONNECT: {
-      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONNECT");
-
-      /* A new connection was established or a connection attempt failed. */
-      NRF_LOG_INFO("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed",
-                   event->connect.status);
-
-      if (event->connect.status != 0) {
-        /* Connection failed; resume advertising. */
-        StartAdvertising();
-        bleController.Disconnect();
-      } else {
-        bleController.Connect();
-        systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleConnected);
-        connectionHandle = event->connect.conn_handle;
-        // Service discovery is deffered via systemtask
-      }
-    }
-      break;
-    case BLE_GAP_EVENT_DISCONNECT:
-      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_DISCONNECT");
-      NRF_LOG_INFO("disconnect; reason=%d", event->disconnect.reason);
-
-      /* Connection terminated; resume advertising. */
-      connectionHandle = BLE_HS_CONN_HANDLE_NONE;
-      bleController.Disconnect();
-      StartAdvertising();
-      break;
-    case BLE_GAP_EVENT_CONN_UPDATE:
-      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONN_UPDATE");
-      /* The central has updated the connection parameters. */
-      NRF_LOG_INFO("connection updated; status=%d ", event->conn_update.status);
-      break;
-    case BLE_GAP_EVENT_ENC_CHANGE:
-      /* Encryption has been enabled or disabled for this connection. */
-      NRF_LOG_INFO("encryption change event; status=%d ", event->enc_change.status);
-      return 0;
-    case BLE_GAP_EVENT_SUBSCRIBE:
-      NRF_LOG_INFO("subscribe event; conn_handle=%d attr_handle=%d "
-                        "reason=%d prevn=%d curn=%d previ=%d curi=???\n",
-                  event->subscribe.conn_handle,
-                  event->subscribe.attr_handle,
-                  event->subscribe.reason,
-                  event->subscribe.prev_notify,
-                  event->subscribe.cur_notify,
-                  event->subscribe.prev_indicate);
-      return 0;
-    case BLE_GAP_EVENT_MTU:
-      NRF_LOG_INFO("mtu update event; conn_handle=%d cid=%d mtu=%d\n",
-                  event->mtu.conn_handle,
-                  event->mtu.channel_id,
-                  event->mtu.value);
-      return 0;
-
-    case BLE_GAP_EVENT_REPEAT_PAIRING: {
-      /* We already have a bond with the peer, but it is attempting to
-       * establish a new secure link.  This app sacrifices security for
-       * convenience: just throw away the old bond and accept the new link.
-       */
-
-      /* Delete the old bond. */
-      struct ble_gap_conn_desc desc;
-      ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
-      ble_store_util_delete_peer(&desc.peer_id_addr);
-
-      /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
-       * continue with the pairing operation.
-       */
-    }
-      return BLE_GAP_REPEAT_PAIRING_RETRY;
-
-    case BLE_GAP_EVENT_NOTIFY_RX: {
-      /* Peer sent us a notification or indication. */
-      size_t notifSize = OS_MBUF_PKTLEN(event->notify_rx.om);
-
-      NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d "
-                   "attr_len=%d",
-                   event->notify_rx.indication ?
-                   "indication" :
-                   "notification",
-                   event->notify_rx.conn_handle,
-                   event->notify_rx.attr_handle,
-                   notifSize);
-
-      alertNotificationClient.OnNotification(event);
-      return 0;
-    }
-      /* Attribute data is contained in event->notify_rx.attr_data. */
-
-    default:
-//      NRF_LOG_INFO("Advertising event : %d", event->type);
-      break;
-  }
-  return 0;
-}
-
-int NimbleController::OnDiscoveryEvent(uint16_t i, const ble_gatt_error *error, const ble_gatt_svc *service) {
-  if(service == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("Service Discovery complete");
-    if(currentTimeClient.IsDiscovered()) {
-      ble_gattc_disc_all_chrs(connectionHandle, currentTimeClient.StartHandle(), currentTimeClient.EndHandle(),
-                              CurrentTimeCharacteristicDiscoveredCallback, this);
-
-    } else if(alertNotificationClient.IsDiscovered()) {
-      ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(), alertNotificationClient.EndHandle(),
-                              AlertNotificationCharacteristicDiscoveredCallback, this);
-    }
-  }
-
-  alertNotificationClient.OnDiscoveryEvent(i, error, service);
-  currentTimeClient.OnDiscoveryEvent(i, error, service);
-  return 0;
-}
-
-int NimbleController::OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                        const ble_gatt_chr *characteristic) {
-  if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("CTS characteristic Discovery complete");
-    ble_gattc_read(connectionHandle, currentTimeClient.CurrentTimeHandle(), CurrentTimeReadCallback, this);
-    return 0;
-  }
-  return currentTimeClient.OnCharacteristicDiscoveryEvent(connectionHandle, error, characteristic);
-}
-
-int NimbleController::OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                        const ble_gatt_chr *characteristic) {
-  if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("ANS characteristic Discovery complete");
-    ble_gattc_disc_all_dscs(connectionHandle,
-            alertNotificationClient.NewAlerthandle(), alertNotificationClient.EndHandle(),
-            AlertNotificationDescriptorDiscoveryEventCallback, this);
-    return 0;
-  }
-  return alertNotificationClient.OnCharacteristicsDiscoveryEvent(connectionHandle, error, characteristic);
-}
-
-int NimbleController::OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute) {
-  currentTimeClient.OnCurrentTimeReadResult(connectionHandle, error, attribute);
-
-  if (alertNotificationClient.IsDiscovered()) {
-    ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(),
-                            alertNotificationClient.EndHandle(),
-                            AlertNotificationCharacteristicDiscoveredCallback, this);
-  }
-  return 0;
-}
-
-int NimbleController::OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                            uint16_t characteristicValueHandle,
-                                                            const ble_gatt_dsc *descriptor) {
-  return alertNotificationClient.OnDescriptorDiscoveryEventCallback(connectionHandle, error, characteristicValueHandle, descriptor);
-}
-
-void NimbleController::StartDiscovery() {
-  ble_gattc_disc_all_svcs(connectionHandle, OnAllSvrDisco, this);
-}
-
-
-uint16_t NimbleController::connHandle() {
-    return connectionHandle;
-}
-




diff --git a/src/Components/Ble/NimbleController.h b/src/Components/Ble/NimbleController.h
deleted file mode 100644
index 89fa4250e692fa387dd7e1e615e369c09883213a..0000000000000000000000000000000000000000
--- a/src/Components/Ble/NimbleController.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include "AlertNotificationService.h"
-#include "AlertNotificationClient.h"
-#include "DeviceInformationService.h"
-#include "CurrentTimeClient.h"
-#include "DfuService.h"
-#include "CurrentTimeService.h"
-#include "MusicService.h"
-#include "BatteryInformationService.h"
-#include "ImmediateAlertService.h"
-#include <host/ble_gap.h>
-
-namespace Pinetime {
-  namespace Drivers {
-    class SpiNorFlash;
-  }
-  namespace Controllers {
-    class DateTime;
-
-    class NimbleController {
-
-      public:
-        NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController,
-                DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager,
-                Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash);
-        void Init();
-        void StartAdvertising();
-        int OnGAPEvent(ble_gap_event *event);
-
-        int OnDiscoveryEvent(uint16_t i, const ble_gatt_error *pError, const ble_gatt_svc *pSvc);
-        int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                              const ble_gatt_chr *characteristic);
-        int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                              const ble_gatt_chr *characteristic);
-        int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute);
-        int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                  uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
-
-        void StartDiscovery();
-
-        Pinetime::Controllers::MusicService& music() {return musicService;};
-
-        uint16_t connHandle();
-
-      private:
-        static constexpr const char* deviceName = "InfiniTime";
-        Pinetime::System::SystemTask& systemTask;
-        Pinetime::Controllers::Ble& bleController;
-        DateTime& dateTimeController;
-        Pinetime::Controllers::NotificationManager& notificationManager;
-        Pinetime::Drivers::SpiNorFlash& spiNorFlash;
-        Pinetime::Controllers::DfuService dfuService;
-
-        DeviceInformationService deviceInformationService;
-        CurrentTimeClient currentTimeClient;
-        AlertNotificationService anService;
-        AlertNotificationClient alertNotificationClient;
-        CurrentTimeService currentTimeService;
-        MusicService musicService;
-        BatteryInformationService batteryInformationService;
-        ImmediateAlertService immediateAlertService;
-
-        uint8_t addrType; // 1 = Random, 0 = PUBLIC
-        uint16_t connectionHandle = 0;
-
-        ble_uuid128_t dfuServiceUuid {
-                .u { .type = BLE_UUID_TYPE_128},
-                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
-                          0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}
-        };
-    };
-  }
-}




diff --git a/src/Components/Ble/NotificationManager.cpp b/src/Components/Ble/NotificationManager.cpp
deleted file mode 100644
index 0aea0697358482a966e6c158e525751c9c4bc156..0000000000000000000000000000000000000000
--- a/src/Components/Ble/NotificationManager.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <cstring>
-#include "NotificationManager.h"
-
-using namespace Pinetime::Controllers;
-
-void NotificationManager::Push(Pinetime::Controllers::NotificationManager::Categories category,
-                                                      const char *message, uint8_t currentMessageSize) {
-  // TODO handle edge cases on read/write index
-  auto checkedSize = std::min(currentMessageSize, uint8_t{18});
-  auto& notif = notifications[writeIndex];
-  std::memcpy(notif.message.data(), message, checkedSize);
-  notif.message[checkedSize] = '\0';
-  notif.category = category;
-
-  writeIndex = (writeIndex + 1 < TotalNbNotifications) ? writeIndex + 1 : 0;
-  if(!empty && writeIndex == readIndex)
-    readIndex = writeIndex + 1;
-}
-
-NotificationManager::Notification Pinetime::Controllers::NotificationManager::Pop() {
-// TODO handle edge cases on read/write index
-  NotificationManager::Notification notification = notifications[readIndex];
-
-  if(readIndex != writeIndex) {
-    readIndex = (readIndex + 1 < TotalNbNotifications) ? readIndex + 1 : 0;
-  }
-
-  // TODO Check move optimization on return
-  return notification;
-}




diff --git a/src/Components/Ble/NotificationManager.h b/src/Components/Ble/NotificationManager.h
deleted file mode 100644
index daa1571b3d6feeafcc80287854d265b9ab9b5c4b..0000000000000000000000000000000000000000
--- a/src/Components/Ble/NotificationManager.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-
-#include <array>
-
-namespace Pinetime {
-  namespace Controllers {
-    class NotificationManager {
-      public:
-        enum class Categories {Unknown, SimpleAlert, Email, News, IncomingCall, MissedCall, Sms, VoiceMail, Schedule, HighProriotyAlert, InstantMessage };
-        static constexpr uint8_t MessageSize{18};
-
-        struct Notification {
-          std::array<char, MessageSize+1> message;
-          Categories category = Categories::Unknown;
-        };
-
-      void Push(Categories category, const char* message, uint8_t messageSize);
-      Notification Pop();
-
-
-      private:
-        static constexpr uint8_t TotalNbNotifications = 5;
-        std::array<Notification, TotalNbNotifications> notifications;
-        uint8_t readIndex = 0;
-        uint8_t writeIndex = 0;
-        bool empty = true;
-    };
-  }
-}
\ No newline at end of file




diff --git a/src/Components/Brightness/BrightnessController.cpp b/src/Components/Brightness/BrightnessController.cpp
deleted file mode 100644
index c8825d6807a722d7bd401ac86af2457910911da5..0000000000000000000000000000000000000000
--- a/src/Components/Brightness/BrightnessController.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include <hal/nrf_gpio.h>
-#include "BrightnessController.h"
-
-using namespace Pinetime::Controllers;
-
-
-void BrightnessController::Init() {
-  nrf_gpio_cfg_output(pinLcdBacklight1);
-  nrf_gpio_cfg_output(pinLcdBacklight2);
-  nrf_gpio_cfg_output(pinLcdBacklight3);
-  Set(level);
-}
-
-void BrightnessController::Set(BrightnessController::Levels level) {
-  this->level = level;
-  switch(level) {
-    default:
-    case Levels::High:
-      nrf_gpio_pin_clear(pinLcdBacklight1);
-      nrf_gpio_pin_clear(pinLcdBacklight2);
-      nrf_gpio_pin_clear(pinLcdBacklight3);
-      break;
-    case Levels::Medium:
-      nrf_gpio_pin_clear(pinLcdBacklight1);
-      nrf_gpio_pin_clear(pinLcdBacklight2);
-      nrf_gpio_pin_set(pinLcdBacklight3);
-      break;
-    case Levels::Low:
-      nrf_gpio_pin_clear(pinLcdBacklight1);
-      nrf_gpio_pin_set(pinLcdBacklight2);
-      nrf_gpio_pin_set(pinLcdBacklight3);
-      break;
-    case Levels::Off:
-      nrf_gpio_pin_set(pinLcdBacklight1);
-      nrf_gpio_pin_set(pinLcdBacklight2);
-      nrf_gpio_pin_set(pinLcdBacklight3);
-      break;
-  }
-}
-
-void BrightnessController::Lower() {
-  switch(level) {
-    case Levels::High: Set(Levels::Medium); break;
-    case Levels::Medium: Set(Levels::Low); break;
-    case Levels::Low: Set(Levels::Off); break;
-    default: break;
-  }
-}
-
-void BrightnessController::Higher() {
-  switch(level) {
-    case Levels::Off: Set(Levels::Low); break;
-    case Levels::Low: Set(Levels::Medium); break;
-    case Levels::Medium: Set(Levels::High); break;
-    default: break;
-  }
-}
-
-BrightnessController::Levels BrightnessController::Level() const {
-  return level;
-}
-
-void BrightnessController::Backup() {
-  backupLevel = level;
-}
-
-void BrightnessController::Restore() {
-  Set(backupLevel);
-}
-




diff --git a/src/Components/Brightness/BrightnessController.h b/src/Components/Brightness/BrightnessController.h
deleted file mode 100644
index b8354ec0538b58e70befe0a16758cdf456d530d8..0000000000000000000000000000000000000000
--- a/src/Components/Brightness/BrightnessController.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <cstdint>
-
-namespace Pinetime {
-  namespace Controllers {
-    class BrightnessController {
-    public:
-      enum class Levels {Off, Low, Medium, High};
-      void Init();
-
-      void Set(Levels level);
-      Levels Level() const;
-      void Lower();
-      void Higher();
-
-      void Backup();
-      void Restore();
-
-    private:
-      static constexpr uint8_t pinLcdBacklight1 = 14;
-      static constexpr uint8_t pinLcdBacklight2 = 22;
-      static constexpr uint8_t pinLcdBacklight3 = 23;
-      Levels level = Levels::High;
-      Levels backupLevel = Levels::High;
-    };
-  }
-}




diff --git a/src/Components/DateTime/DateTimeController.cpp b/src/Components/DateTime/DateTimeController.cpp
deleted file mode 100644
index 30d9c13f2587a4d2a9a5a7f4196189b2932d5256..0000000000000000000000000000000000000000
--- a/src/Components/DateTime/DateTimeController.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "DateTimeController.h"
-#include <date/date.h>
-#include <libraries/log/nrf_log.h>
-
-using namespace Pinetime::Controllers;
-
-
-void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute,
-                       uint8_t second, uint32_t systickCounter) {
-  std::tm tm = { /* .tm_sec  = */ second,
-          /* .tm_min  = */ minute,
-          /* .tm_hour = */ hour,
-          /* .tm_mday = */ day,
-          /* .tm_mon  = */ month - 1,
-          /* .tm_year = */ year - 1900,
-  };
-  tm.tm_isdst = -1; // Use DST value from local time zone
-  currentDateTime =  std::chrono::system_clock::from_time_t(std::mktime(&tm));
-
-  NRF_LOG_INFO("%d %d %d ", day, month, year);
-  NRF_LOG_INFO("%d %d %d ", hour, minute, second);
-  previousSystickCounter = systickCounter;
-
-  UpdateTime(systickCounter);
-  NRF_LOG_INFO("* %d %d %d ", this->hour, this->minute, this->second);
-  NRF_LOG_INFO("* %d %d %d ", this->day, this->month, this->year);
-}
-
-void DateTime::UpdateTime(uint32_t systickCounter) {
-  // Handle systick counter overflow
-  uint32_t systickDelta = 0;
-  if(systickCounter < previousSystickCounter) {
-    systickDelta = 0xffffff - previousSystickCounter;
-    systickDelta += systickCounter + 1;
-  } else {
-    systickDelta = systickCounter - previousSystickCounter;
-  }
-
-  /*
- * 1000 ms = 1024 ticks
- */
-  auto correctedDelta = systickDelta / 1024;
-  auto rest = (systickDelta - (correctedDelta*1024));
-  if(systickCounter >= rest) {
-    previousSystickCounter = systickCounter - rest;
-  } else {
-    previousSystickCounter = 0xffffff - (rest - systickCounter);
-  }
-
-  currentDateTime += std::chrono::seconds(correctedDelta);
-  uptime += std::chrono::seconds(correctedDelta);
-
-  auto dp = date::floor<date::days>(currentDateTime);
-  auto time = date::make_time(currentDateTime-dp);
-  auto yearMonthDay = date::year_month_day(dp);
-
-  year = (int)yearMonthDay.year();
-  month = static_cast<Months>((unsigned)yearMonthDay.month());
-  day = (unsigned)yearMonthDay.day();
-  dayOfWeek = static_cast<Days>(date::weekday(yearMonthDay).iso_encoding());
-
-  hour = time.hours().count();
-  minute = time.minutes().count();
-  second = time.seconds().count();
-}
-




diff --git a/src/Components/DateTime/DateTimeController.h b/src/Components/DateTime/DateTimeController.h
deleted file mode 100644
index d6020745609e1ad6b5281b8f34c9397046fe1294..0000000000000000000000000000000000000000
--- a/src/Components/DateTime/DateTimeController.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <chrono>
-
-namespace Pinetime {
-  namespace Controllers {
-    class DateTime {
-      public:
-        enum class Days : uint8_t {Unknown, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
-        enum class Months : uint8_t {Unknown, January, February, March, April, May, June, July, August, September, October, November, December};
-
-        void SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter);
-        void UpdateTime(uint32_t systickCounter);
-        uint16_t Year() const { return year; }
-        Months Month() const { return month; }
-        uint8_t Day() const { return day; }
-        Days DayOfWeek() const { return dayOfWeek; }
-        uint8_t Hours() const { return hour; }
-        uint8_t Minutes() const { return minute; }
-        uint8_t Seconds() const { return second; }
-
-        std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const { return currentDateTime; }
-        std::chrono::seconds Uptime() const { return uptime; }
-      private:
-        uint16_t year = 0;
-        Months month = Months::Unknown;
-        uint8_t day = 0;
-        Days dayOfWeek = Days::Unknown;
-        uint8_t hour = 0;
-        uint8_t minute = 0;
-        uint8_t second = 0;
-
-        uint32_t previousSystickCounter = 0;
-        std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
-        std::chrono::seconds uptime {0};
-    };
-  }
-}
\ No newline at end of file




diff --git a/src/Components/FirmwareValidator/FirmwareValidator.cpp b/src/Components/FirmwareValidator/FirmwareValidator.cpp
deleted file mode 100644
index 244d5c06bf704afdc9727e02a5248a82c4e381f9..0000000000000000000000000000000000000000
--- a/src/Components/FirmwareValidator/FirmwareValidator.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include <drivers/InternalFlash.h>
-#include <hal/nrf_rtc.h>
-
-#include "FirmwareValidator.h"
-
-using namespace Pinetime::Controllers;
-
-bool FirmwareValidator::IsValidated() const {
-  auto* imageOkPtr = reinterpret_cast<uint32_t *>(validBitAdress);
-  return (*imageOkPtr) == validBitValue;
-}
-
-void FirmwareValidator::Validate() {
-  if(!IsValidated())
-    Pinetime::Drivers::InternalFlash::WriteWord(validBitAdress, validBitValue);
-}
-
-void FirmwareValidator::Reset() {
-  NVIC_SystemReset();
-}




diff --git a/src/Components/FirmwareValidator/FirmwareValidator.h b/src/Components/FirmwareValidator/FirmwareValidator.h
deleted file mode 100644
index aa576d8872157563227a112beab9401450a9444f..0000000000000000000000000000000000000000
--- a/src/Components/FirmwareValidator/FirmwareValidator.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <cstdint>
-
-namespace Pinetime {
-  namespace Controllers {
-    class FirmwareValidator {
-      public:
-        void Validate();
-        bool IsValidated() const;
-
-        void Reset();
-      private:
-        static constexpr uint32_t validBitAdress {0x7BFE8};
-        static constexpr uint32_t validBitValue {1};
-    };
-  }
-}




diff --git a/src/Components/Gfx/Gfx.cpp b/src/Components/Gfx/Gfx.cpp
deleted file mode 100644
index 3c5dbfb7857279eac1ab21fe6c3c25719dd1b50b..0000000000000000000000000000000000000000
--- a/src/Components/Gfx/Gfx.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-#include <libraries/svc/nrf_svci.h>
-#include <FreeRTOS.h>
-#include <task.h>
-#include "Gfx.h"
-#include "../../drivers/St7789.h"
-using namespace Pinetime::Components;
-
-Gfx::Gfx(Pinetime::Drivers::St7789 &lcd) : lcd{lcd} {
-}
-
-void Gfx::Init() {
-
-}
-
-void Gfx::ClearScreen() {
-  SetBackgroundColor(0x0000);
-
-  state.remainingIterations = 240 + 1;
-  state.currentIteration = 0;
-  state.busy = true;
-  state.action = Action::FillRectangle;
-  state.taskToNotify = xTaskGetCurrentTaskHandle();
-
-  lcd.BeginDrawBuffer(0, 0, width, height);
-  lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
-  WaitTransfertFinished();
-
-}
-
-void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
-  SetBackgroundColor(color);
-
-  state.remainingIterations = h;
-  state.currentIteration = 0;
-  state.busy = true;
-  state.action = Action::FillRectangle;
-  state.color = color;
-  state.taskToNotify = xTaskGetCurrentTaskHandle();
-
-  lcd.BeginDrawBuffer(x, y, w, h);
-  lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
-
-  WaitTransfertFinished();
-}
-
-void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b) {
-  state.remainingIterations = h;
-  state.currentIteration = 0;
-  state.busy = true;
-  state.action = Action::FillRectangle;
-  state.color = 0x00;
-  state.taskToNotify = xTaskGetCurrentTaskHandle();
-
-  lcd.BeginDrawBuffer(x, y, w, h);
-  lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(b), width * 2);
-
-  WaitTransfertFinished();
-}
-
-void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, const FONT_INFO *p_font, bool wrap) {
-  if (y > (height - p_font->height)) {
-    // Not enough space to write even single char.
-    return;
-  }
-
-  uint8_t current_x = x;
-  uint8_t current_y = y;
-
-  for (size_t i = 0; text[i] != '\0'; i++) {
-    if (text[i] == '\n') {
-      current_x = x;
-      current_y += p_font->height + p_font->height / 10;
-    } else {
-      DrawChar(p_font, (uint8_t) text[i], &current_x, current_y, color);
-    }
-
-    uint8_t char_idx = text[i] - p_font->startChar;
-    uint16_t char_width = text[i] == ' ' ? (p_font->height / 2) : p_font->charInfo[char_idx].widthBits;
-
-    if (current_x > (width - char_width)) {
-      if (wrap) {
-        current_x = x;
-        current_y += p_font->height + p_font->height / 10;
-      } else {
-        break;
-      }
-
-      if (y > (height - p_font->height)) {
-        break;
-      }
-    }
-  }
-}
-
-void Gfx::DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color) {
-  uint8_t char_idx = c - font->startChar;
-  uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8);
-  uint16_t bg = 0x0000;
-
-  if (c == ' ') {
-    *x += font->height / 2;
-    return;
-  }
-
-  // Build first line
-  for (uint16_t j = 0; j < bytes_in_line; j++) {
-    for (uint8_t k = 0; k < 8; k++) {
-      if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + j]) {
-        buffer[(j*8)+k] = color;
-      }
-      else {
-        buffer[(j*8)+k] = bg;
-      }
-    }
-  }
-
-  state.remainingIterations = font->height + 0;
-  state.currentIteration = 0;
-  state.busy = true;
-  state.action = Action::DrawChar;
-  state.font = const_cast<FONT_INFO *>(font);
-  state.character = c;
-  state.color = color;
-  state.taskToNotify = xTaskGetCurrentTaskHandle();
-
-  lcd.BeginDrawBuffer(*x, y, bytes_in_line*8, font->height);
-  lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(&buffer), bytes_in_line*8*2);
-  WaitTransfertFinished();
-
-  *x += font->charInfo[char_idx].widthBits + font->spacePixels;
-}
-
-void Gfx::pixel_draw(uint8_t x, uint8_t y, uint16_t color) {
-  lcd.DrawPixel(x, y, color);
-}
-
-void Gfx::Sleep() {
-  lcd.Sleep();
-}
-
-void Gfx::Wakeup() {
-  lcd.Wakeup();
-}
-
-void Gfx::SetBackgroundColor(uint16_t color) {
-  for(int i = 0; i < width; i++) {
-    buffer[i] = color;
-  }
-}
-
-bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) {
-  if(!state.busy) return false;
-  state.remainingIterations--;
-  if (state.remainingIterations == 0) {
-    state.busy = false;
-    NotifyEndOfTransfert(state.taskToNotify);
-    return false;
-  }
-
-  if(state.action == Action::FillRectangle) {
-    *data = reinterpret_cast<uint8_t *>(buffer);
-    size = width * 2;
-  } else if(state.action == Action::DrawChar) {
-    uint16_t bg = 0x0000;
-    uint8_t char_idx = state.character - state.font->startChar;
-    uint16_t bytes_in_line = CEIL_DIV(state.font->charInfo[char_idx].widthBits, 8);
-
-    for (uint16_t j = 0; j < bytes_in_line; j++) {
-      for (uint8_t k = 0; k < 8; k++) {
-        if ((1 << (7 - k)) & state.font->data[state.font->charInfo[char_idx].offset + ((state.currentIteration+1) * bytes_in_line) + j]) {
-          buffer[(j*8)+k] = state.color;
-        }
-        else {
-          buffer[(j*8)+k] = bg;
-        }
-      }
-    }
-
-    *data = reinterpret_cast<uint8_t *>(buffer);
-    size = bytes_in_line*8*2;
-  }
-
-  state.currentIteration++;
-
-  return true;
-}
-
-void Gfx::NotifyEndOfTransfert(TaskHandle_t task) {
-  if(task != nullptr) {
-    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-    vTaskNotifyGiveFromISR(task, &xHigherPriorityTaskWoken);
-    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
-  }
-}
-
-void Gfx::WaitTransfertFinished() const {
-  ulTaskNotifyTake(pdTRUE, 500);
-}
-
-void Gfx::SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines) {
-  lcd.VerticalScrollDefinition(topFixedLines, scrollLines, bottomFixedLines);
-}
-
-void Gfx::SetScrollStartLine(uint16_t line) {
-  lcd.VerticalScrollStartAddress(line);
-}
-




diff --git a/src/Components/Gfx/Gfx.h b/src/Components/Gfx/Gfx.h
deleted file mode 100644
index 091f06f5604c5febddf3637a8c7307b904b43147..0000000000000000000000000000000000000000
--- a/src/Components/Gfx/Gfx.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#pragma once
-#include <cstdint>
-#include <nrf_font.h>
-#include <drivers/BufferProvider.h>
-#include <FreeRTOS.h>
-#include <task.h>
-
-
-namespace Pinetime {
-  namespace Drivers {
-    class St7789;
-  }
-  namespace Components {
-    class Gfx : public Pinetime::Drivers::BufferProvider {
-      public:
-        explicit Gfx(Drivers::St7789& lcd);
-        void Init();
-        void ClearScreen();
-        void DrawString(uint8_t x, uint8_t y, uint16_t color, const char* text, const FONT_INFO *p_font, bool wrap);
-        void DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color);
-        void FillRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t color);
-        void FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b);
-        void SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
-        void SetScrollStartLine(uint16_t line);
-
-
-        void Sleep();
-        void Wakeup();
-        bool GetNextBuffer(uint8_t **buffer, size_t &size) override;
-        void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
-
-
-      private:
-        static constexpr uint8_t width = 240;
-        static constexpr uint8_t height = 240;
-
-        enum class Action { None, FillRectangle, DrawChar};
-        struct State {
-          State() : busy{false}, action{Action::None}, remainingIterations{0}, currentIteration{0} {}
-          volatile bool busy;
-          volatile Action action;
-          volatile uint16_t remainingIterations;
-          volatile uint16_t currentIteration;
-          volatile FONT_INFO *font;
-          volatile uint16_t color;
-          volatile uint8_t character;
-          volatile TaskHandle_t taskToNotify = nullptr;
-        };
-
-        volatile State state;
-
-        uint16_t buffer[width]; // 1 line buffer
-        Drivers::St7789& lcd;
-
-        void SetBackgroundColor(uint16_t color);
-        void WaitTransfertFinished() const;
-        void NotifyEndOfTransfert(TaskHandle_t task);
-    };
-  }
-}




diff --git a/src/components/Battery/BatteryController.cpp b/src/components/Battery/BatteryController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..571efae64d7c8b352648777df15b264d8275d65f
--- /dev/null
+++ b/src/components/Battery/BatteryController.cpp
@@ -0,0 +1,48 @@
+#include <drivers/include/nrfx_saadc.h>
+#include <hal/nrf_gpio.h>
+#include <libraries/log/nrf_log.h>
+#include <algorithm>
+#include "BatteryController.h"
+
+using namespace Pinetime::Controllers;
+
+void Battery::Init() {
+  nrf_gpio_cfg_input(chargingPin, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup);
+  nrf_gpio_cfg_input(powerPresentPin, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup);
+
+  nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
+  nrfx_saadc_init(&adcConfig, SaadcEventHandler);
+  nrf_saadc_channel_config_t adcChannelConfig = {
+          .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
+          .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
+          .gain       = NRF_SAADC_GAIN1_5,
+          .reference  = NRF_SAADC_REFERENCE_INTERNAL,
+          .acq_time   = NRF_SAADC_ACQTIME_3US,
+          .mode       = NRF_SAADC_MODE_SINGLE_ENDED,
+          .burst      = NRF_SAADC_BURST_DISABLED,
+          .pin_p      = batteryVoltageAdcInput,
+          .pin_n      = NRF_SAADC_INPUT_DISABLED
+  };
+  nrfx_saadc_channel_init(0, &adcChannelConfig);
+}
+
+void Battery::Update() {
+  isCharging = !nrf_gpio_pin_read(chargingPin);
+  isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
+
+  nrf_saadc_value_t value = 0;
+  nrfx_saadc_sample_convert(0, &value);
+
+  // see https://forum.pine64.org/showthread.php?tid=8147
+  voltage = (value * 2.0f) / (1024/3.0f);
+  percentRemaining = ((voltage - 3.55f)*100.0f)*3.9f;
+  percentRemaining = std::max(percentRemaining, 0.0f);
+  percentRemaining = std::min(percentRemaining, 100.0f);
+
+//  NRF_LOG_INFO("BATTERY " NRF_LOG_FLOAT_MARKER " %% - " NRF_LOG_FLOAT_MARKER " v", NRF_LOG_FLOAT(percentRemaining), NRF_LOG_FLOAT(voltage));
+//  NRF_LOG_INFO("POWER Charging : %d - Power : %d", isCharging, isPowerPresent);
+}
+
+void Battery::SaadcEventHandler(nrfx_saadc_evt_t const * event) {
+
+}
\ No newline at end of file




diff --git a/src/components/Battery/BatteryController.h b/src/components/Battery/BatteryController.h
new file mode 100644
index 0000000000000000000000000000000000000000..f07648a9f0b703b0513b9106f3a8d36375ce75e1
--- /dev/null
+++ b/src/components/Battery/BatteryController.h
@@ -0,0 +1,27 @@
+#pragma once
+#include <drivers/include/nrfx_saadc.h>
+
+
+namespace Pinetime {
+  namespace Controllers {
+    class Battery {
+      public:
+        void Init();
+        void Update();
+        float PercentRemaining() const { return percentRemaining; }
+        float Voltage() const { return voltage; }
+        bool IsCharging() const { return isCharging; }
+        bool IsPowerPresent() const { return isPowerPresent; }
+
+      private:
+        static constexpr uint32_t chargingPin = 12;
+        static constexpr uint32_t powerPresentPin = 19;
+        static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
+        static void SaadcEventHandler(nrfx_saadc_evt_t const * p_event);
+        float percentRemaining = 0.0f;
+        float voltage = 0.0f;
+        bool isCharging = false;
+        bool isPowerPresent = false;
+    };
+  }
+}
\ No newline at end of file




diff --git a/src/components/Ble/AlertNotificationClient.cpp b/src/components/Ble/AlertNotificationClient.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3e4b495fb741c1f5f58e8b78b5391ad31e066228
--- /dev/null
+++ b/src/components/Ble/AlertNotificationClient.cpp
@@ -0,0 +1,145 @@
+#include <SystemTask/SystemTask.h>
+#include "NotificationManager.h"
+
+#include "AlertNotificationClient.h"
+
+
+using namespace Pinetime::Controllers;
+constexpr ble_uuid16_t AlertNotificationClient::ansServiceUuid;
+
+constexpr ble_uuid16_t AlertNotificationClient::supportedNewAlertCategoryUuid;
+constexpr ble_uuid16_t AlertNotificationClient::supportedUnreadAlertCategoryUuid ;
+constexpr ble_uuid16_t AlertNotificationClient::newAlertUuid;
+constexpr ble_uuid16_t AlertNotificationClient::unreadAlertStatusUuid;
+constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid;
+
+int Pinetime::Controllers::NewAlertSubcribeCallback(uint16_t conn_handle,
+                     const struct ble_gatt_error *error,
+                     struct ble_gatt_attr *attr,
+                     void *arg) {
+  auto client = static_cast<AlertNotificationClient*>(arg);
+  return client->OnNewAlertSubcribe(conn_handle, error, attr);
+}
+
+AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
+        Pinetime::Controllers::NotificationManager& notificationManager) :
+        systemTask{systemTask}, notificationManager{notificationManager}{
+
+}
+
+bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) {
+  if(service == nullptr && error->status == BLE_HS_EDONE) {
+    NRF_LOG_INFO("ANS Discovery complete");
+    return true;
+  }
+
+  if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ansServiceUuid), &service->uuid.u) == 0) {
+    NRF_LOG_INFO("ANS discovered : 0x%x", service->start_handle);
+      ansStartHandle = service->start_handle;
+      ansEndHandle = service->end_handle;
+      isDiscovered = true;
+  }
+  return false;
+}
+
+int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
+                                                                                    const ble_gatt_chr *characteristic) {
+  if(error->status != 0 && error->status != BLE_HS_EDONE) {
+    NRF_LOG_INFO("ANS Characteristic discovery ERROR");
+    return 0;
+  }
+
+  if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
+    NRF_LOG_INFO("ANS Characteristic discovery complete");
+  } else {
+    if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
+      NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid");
+      supportedNewAlertCategoryHandle = characteristic->val_handle;
+    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
+      NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid");
+      supportedUnreadAlertCategoryHandle = characteristic->val_handle;
+    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&newAlertUuid), &characteristic->uuid.u) == 0) {
+      NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid");
+      newAlertHandle = characteristic->val_handle;
+      newAlertDefHandle = characteristic->def_handle;
+    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
+      NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid");
+      unreadAlertStatusHandle = characteristic->val_handle;
+    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&controlPointUuid), &characteristic->uuid.u) == 0) {
+      NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid");
+      controlPointHandle = characteristic->val_handle;
+    }else
+      NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle);
+    }
+  return 0;
+}
+
+int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error,
+                                                ble_gatt_attr *attribute) {
+  if(error->status == 0) {
+    NRF_LOG_INFO("ANS New alert subscribe OK");
+  } else {
+    NRF_LOG_INFO("ANS New alert subscribe ERROR");
+  }
+
+  return 0;
+}
+
+int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
+                                                                uint16_t characteristicValueHandle,
+                                                                const ble_gatt_dsc *descriptor) {
+  if(error->status == 0) {
+    if(characteristicValueHandle == newAlertHandle && ble_uuid_cmp(((ble_uuid_t*)&newAlertUuid), &descriptor->uuid.u)) {
+      if(newAlertDescriptorHandle == 0) {
+        NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle);
+        newAlertDescriptorHandle = descriptor->handle;
+        uint8_t value[2];
+        value[0] = 1;
+        value[1] = 0;
+        ble_gattc_write_flat(connectionHandle, newAlertDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this);
+      }
+    }
+  }
+  return 0;
+}
+
+void AlertNotificationClient::OnNotification(ble_gap_event *event) {
+  if(event->notify_rx.attr_handle == newAlertHandle) {
+    // TODO implement this with more memory safety (and constexpr)
+    static const size_t maxBufferSize{21};
+    static const size_t maxMessageSize{18};
+    size_t bufferSize = min(OS_MBUF_PKTLEN(event->notify_rx.om), maxBufferSize);
+
+    uint8_t data[bufferSize];
+    os_mbuf_copydata(event->notify_rx.om, 0, bufferSize, data);
+
+    char *s = (char *) &data[3];
+    auto messageSize = min(maxMessageSize, (bufferSize-3));
+
+    for (uint i = 0; i < messageSize-1; i++) {
+      if (s[i] == 0x00) {
+        s[i] = 0x0A;
+      }
+    }
+    s[messageSize-1] = '\0';
+
+    notificationManager.Push(Pinetime::Controllers::NotificationManager::Categories::SimpleAlert, s, messageSize);
+    systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
+  }
+}
+
+bool AlertNotificationClient::IsDiscovered() const {
+  return isDiscovered;
+}
+
+uint16_t AlertNotificationClient::StartHandle() const {
+  return ansStartHandle;
+}
+
+uint16_t AlertNotificationClient::EndHandle() const {
+  return ansEndHandle;
+}
+
+uint16_t AlertNotificationClient::NewAlerthandle() const {
+  return newAlertHandle;
+}




diff --git a/src/components/Ble/AlertNotificationClient.h b/src/components/Ble/AlertNotificationClient.h
new file mode 100644
index 0000000000000000000000000000000000000000..ca4f4e948e228c8351bfedbb119253134e493817
--- /dev/null
+++ b/src/components/Ble/AlertNotificationClient.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <cstdint>
+#include <array>
+#include <host/ble_gap.h>
+
+
+namespace Pinetime {
+  namespace Controllers {
+    int NewAlertSubcribeCallback(uint16_t conn_handle,
+                                 const struct ble_gatt_error *error,
+                                 struct ble_gatt_attr *attr,
+                                 void *arg);
+
+    class AlertNotificationClient {
+      public:
+        explicit AlertNotificationClient(Pinetime::System::SystemTask &systemTask,
+                                         Pinetime::Controllers::NotificationManager &notificationManager);
+
+        bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service);
+        int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
+                                            const ble_gatt_chr *characteristic);
+        int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute);
+        int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
+                                               uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
+        void OnNotification(ble_gap_event *event);
+        bool IsDiscovered() const;
+        uint16_t StartHandle() const;
+        uint16_t EndHandle() const;
+
+        static constexpr const ble_uuid16_t &Uuid() { return ansServiceUuid; }
+
+        uint16_t NewAlerthandle() const;
+      private:
+        static constexpr uint16_t ansServiceId{0x1811};
+        static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47;
+        static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48;
+        static constexpr uint16_t newAlertId = 0x2a46;
+        static constexpr uint16_t unreadAlertStatusId = 0x2a45;
+        static constexpr uint16_t controlPointId = 0x2a44;
+
+        static constexpr ble_uuid16_t ansServiceUuid{
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = ansServiceId
+        };
+        static constexpr ble_uuid16_t supportedNewAlertCategoryUuid{
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = supportedNewAlertCategoryId
+        };
+        static constexpr ble_uuid16_t supportedUnreadAlertCategoryUuid{
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = supportedUnreadAlertCategoryId
+        };
+        static constexpr ble_uuid16_t newAlertUuid{
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = newAlertId
+        };
+        static constexpr ble_uuid16_t unreadAlertStatusUuid{
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = unreadAlertStatusId
+        };
+        static constexpr ble_uuid16_t controlPointUuid{
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = controlPointId
+        };
+
+        uint16_t ansStartHandle;
+        uint16_t ansEndHandle;
+        uint16_t supportedNewAlertCategoryHandle;
+        uint16_t supportedUnreadAlertCategoryHandle;
+        uint16_t newAlertHandle;
+        uint16_t newAlertDescriptorHandle = 0;
+        uint16_t newAlertDefHandle;
+        uint16_t unreadAlertStatusHandle;
+        uint16_t controlPointHandle;
+        bool isDiscovered = false;
+        Pinetime::System::SystemTask &systemTask;
+        Pinetime::Controllers::NotificationManager &notificationManager;
+    };
+  }
+}
\ No newline at end of file




diff --git a/src/components/Ble/AlertNotificationService.cpp b/src/components/Ble/AlertNotificationService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ce2f7dd7b9d2faf10281d6ec8e16c91972f2f53e
--- /dev/null
+++ b/src/components/Ble/AlertNotificationService.cpp
@@ -0,0 +1,80 @@
+
+#include <hal/nrf_rtc.h>
+#include "NotificationManager.h"
+#include <SystemTask/SystemTask.h>
+
+#include "AlertNotificationService.h"
+#include <cstring>
+
+using namespace Pinetime::Controllers;
+
+constexpr ble_uuid16_t AlertNotificationService::ansUuid;
+constexpr ble_uuid16_t AlertNotificationService::ansCharUuid;
+
+
+int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
+  auto anService = static_cast<AlertNotificationService*>(arg);
+  return anService->OnAlert(conn_handle, attr_handle, ctxt);
+}
+
+void AlertNotificationService::Init() {
+  int res;
+  res = ble_gatts_count_cfg(serviceDefinition);
+  ASSERT(res == 0);
+
+  res = ble_gatts_add_svcs(serviceDefinition);
+  ASSERT(res == 0);
+}
+
+AlertNotificationService::AlertNotificationService ( System::SystemTask& systemTask, NotificationManager& notificationManager )
+  : characteristicDefinition{
+                {
+                        .uuid = (ble_uuid_t *) &ansCharUuid,
+                        .access_cb = AlertNotificationCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_WRITE
+                },
+                {
+                  0
+                }
+        },
+        serviceDefinition{
+                {
+                        /* Device Information Service */
+                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+                        .uuid = (ble_uuid_t *) &ansUuid,
+                        .characteristics = characteristicDefinition
+                },
+                {
+                        0
+                },
+        }, m_systemTask{systemTask}, m_notificationManager{notificationManager} {
+}
+
+int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle,
+                                                    struct ble_gatt_access_ctxt *ctxt) {
+
+  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+    // TODO implement this with more memory safety (and constexpr)
+    static const size_t maxBufferSize{21};
+    static const size_t maxMessageSize{18};
+    size_t bufferSize = min(OS_MBUF_PKTLEN(ctxt->om), maxBufferSize);
+
+    uint8_t data[bufferSize];
+    os_mbuf_copydata(ctxt->om, 0, bufferSize, data);
+
+    char *s = (char *) &data[3];
+    auto messageSize = min(maxMessageSize, (bufferSize-3));
+
+    for (uint i = 0; i < messageSize-1; i++) {
+      if (s[i] == 0x00) {
+        s[i] = 0x0A;
+      }
+    }
+    s[messageSize-1] = '\0';
+
+    m_notificationManager.Push(Pinetime::Controllers::NotificationManager::Categories::SimpleAlert, s, messageSize);
+    m_systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
+  }
+  return 0;
+}




diff --git a/src/components/Ble/AlertNotificationService.h b/src/components/Ble/AlertNotificationService.h
new file mode 100644
index 0000000000000000000000000000000000000000..53cb44cc59fb7bf1444d9737831112b90a2a3102
--- /dev/null
+++ b/src/components/Ble/AlertNotificationService.h
@@ -0,0 +1,39 @@
+#pragma once
+#include <cstdint>
+#include <array>
+#include <host/ble_gap.h>
+
+namespace Pinetime {
+  namespace Controllers {
+    class AlertNotificationService {
+      public:
+        AlertNotificationService(Pinetime::System::SystemTask &systemTask,
+                                         Pinetime::Controllers::NotificationManager &notificationManager);
+        void Init();
+
+        int OnAlert(uint16_t conn_handle, uint16_t attr_handle,
+                                    struct ble_gatt_access_ctxt *ctxt);
+
+
+      private:
+        static constexpr uint16_t ansId {0x1811};
+        static constexpr uint16_t ansCharId {0x2a46};
+
+        static constexpr ble_uuid16_t ansUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = ansId
+        };
+
+        static constexpr ble_uuid16_t ansCharUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = ansCharId
+        };
+
+        struct ble_gatt_chr_def characteristicDefinition[2];
+        struct ble_gatt_svc_def serviceDefinition[2];
+
+        Pinetime::System::SystemTask &m_systemTask;
+        NotificationManager &m_notificationManager;
+    };
+  }
+}




diff --git a/src/components/Ble/BatteryInformationService.cpp b/src/components/Ble/BatteryInformationService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c86830b88c33b647d8be22000f5a24acaf961530
--- /dev/null
+++ b/src/components/Ble/BatteryInformationService.cpp
@@ -0,0 +1,62 @@
+#include "BatteryInformationService.h"
+#include "../Battery/BatteryController.h"
+
+using namespace Pinetime::Controllers;
+
+constexpr ble_uuid16_t BatteryInformationService::batteryInformationServiceUuid;
+constexpr ble_uuid16_t BatteryInformationService::batteryLevelUuid;
+
+
+
+int BatteryInformationServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
+  auto* batteryInformationService = static_cast<BatteryInformationService*>(arg);
+  return batteryInformationService->OnBatteryServiceRequested(conn_handle, attr_handle, ctxt);
+}
+
+BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController) :
+        batteryController{batteryController},
+        characteristicDefinition{
+                {
+                        .uuid = (ble_uuid_t *) &batteryLevelUuid,
+                        .access_cb = BatteryInformationServiceCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_READ,
+                        .val_handle = &batteryLevelHandle
+                },
+                {
+                        0
+                }
+        },
+        serviceDefinition{
+                {
+                        /* Device Information Service */
+                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+                        .uuid = (ble_uuid_t *) &batteryInformationServiceUuid,
+                        .characteristics = characteristicDefinition
+                },
+                {
+                        0
+                },
+        }{
+
+}
+
+void BatteryInformationService::Init() {
+  int res = 0;
+  res = ble_gatts_count_cfg(serviceDefinition);
+  ASSERT(res == 0);
+
+  res = ble_gatts_add_svcs(serviceDefinition);
+  ASSERT(res == 0);
+}
+
+int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle,
+                                                         ble_gatt_access_ctxt *context) {
+  if(attributeHandle == batteryLevelHandle) {
+    NRF_LOG_INFO("BATTERY : handle = %d", batteryLevelHandle);
+    static uint8_t batteryValue = batteryController.PercentRemaining();
+    int res = os_mbuf_append(context->om, &batteryValue, 1);
+    return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+  }
+  return 0;
+}
\ No newline at end of file




diff --git a/src/components/Ble/BatteryInformationService.h b/src/components/Ble/BatteryInformationService.h
new file mode 100644
index 0000000000000000000000000000000000000000..74b2222c4ee59f67e0029b74275444158c35e3b0
--- /dev/null
+++ b/src/components/Ble/BatteryInformationService.h
@@ -0,0 +1,40 @@
+#pragma once
+#include <host/ble_gap.h>
+
+namespace Pinetime {
+  namespace System {
+    class SystemTask;
+  }
+  namespace Controllers {
+    class Battery;
+    class BatteryInformationService {
+      public:
+        BatteryInformationService(Controllers::Battery& batteryController);
+        void Init();
+
+        int
+        OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
+
+      private:
+        Controllers::Battery& batteryController;
+        static constexpr uint16_t batteryInformationServiceId {0x180F};
+        static constexpr uint16_t batteryLevelId {0x2A19};
+
+        static constexpr ble_uuid16_t batteryInformationServiceUuid {
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = batteryInformationServiceId
+        };
+
+        static constexpr ble_uuid16_t batteryLevelUuid {
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = batteryLevelId
+        };
+
+        struct ble_gatt_chr_def characteristicDefinition[3];
+        struct ble_gatt_svc_def serviceDefinition[2];
+
+        uint16_t batteryLevelHandle;
+
+    };
+  }
+}




diff --git a/src/components/Ble/BleController.cpp b/src/components/Ble/BleController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b396e12f069072360f1d97fd36c1fc0be91b933
--- /dev/null
+++ b/src/components/Ble/BleController.cpp
@@ -0,0 +1,31 @@
+#include <cstring>
+#include <cstdlib>
+#include "BleController.h"
+
+using namespace Pinetime::Controllers;
+
+void Ble::Connect() {
+  isConnected = true;
+}
+
+void Ble::Disconnect() {
+  isConnected = false;
+}
+
+void Ble::StartFirmwareUpdate() {
+  isFirmwareUpdating = true;
+}
+
+void Ble::StopFirmwareUpdate() {
+  isFirmwareUpdating = false;
+}
+
+void Ble::FirmwareUpdateTotalBytes(uint32_t totalBytes) {
+  firmwareUpdateTotalBytes = totalBytes;
+}
+
+void Ble::FirmwareUpdateCurrentBytes(uint32_t currentBytes) {
+  firmwareUpdateCurrentBytes = currentBytes;
+}
+
+




diff --git a/src/components/Ble/BleController.h b/src/components/Ble/BleController.h
new file mode 100644
index 0000000000000000000000000000000000000000..3f52ea258ae766453d150a91de3d0912899a4d56
--- /dev/null
+++ b/src/components/Ble/BleController.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <FreeRTOS.h>
+#include <queue.h>
+#include <array>
+
+namespace Pinetime {
+  namespace Controllers {
+    class Ble {
+      public:
+        using BleAddress = std::array<uint8_t, 6>;
+        enum class FirmwareUpdateStates {Idle, Running, Validated, Error};
+        enum class AddressTypes { Public, Random };
+
+        Ble() = default;
+        bool IsConnected() const {return isConnected;}
+        void Connect();
+        void Disconnect();
+
+        void StartFirmwareUpdate();
+        void StopFirmwareUpdate();
+        void FirmwareUpdateTotalBytes(uint32_t totalBytes);
+        void FirmwareUpdateCurrentBytes(uint32_t currentBytes);
+        void State(FirmwareUpdateStates state) { firmwareUpdateState = state; }
+
+        bool IsFirmwareUpdating() const { return isFirmwareUpdating; }
+        uint32_t FirmwareUpdateTotalBytes() const { return firmwareUpdateTotalBytes; }
+        uint32_t FirmwareUpdateCurrentBytes() const { return firmwareUpdateCurrentBytes; }
+        FirmwareUpdateStates State() const { return firmwareUpdateState; }
+
+        void Address(BleAddress&& addr) { address = addr; }
+        const BleAddress& Address() const { return address; }
+        void AddressType(AddressTypes t) { addressType = t;}
+      private:
+        bool isConnected = false;
+        bool isFirmwareUpdating = false;
+        uint32_t firmwareUpdateTotalBytes = 0;
+        uint32_t firmwareUpdateCurrentBytes = 0;
+        FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle;
+        BleAddress address;
+        AddressTypes addressType;
+
+    };
+  }
+}
\ No newline at end of file




diff --git a/src/components/Ble/CurrentTimeClient.cpp b/src/components/Ble/CurrentTimeClient.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7a225f4bad3e6825edddde1af187c983edb08185
--- /dev/null
+++ b/src/components/Ble/CurrentTimeClient.cpp
@@ -0,0 +1,77 @@
+#include <hal/nrf_rtc.h>
+#include "CurrentTimeClient.h"
+
+using namespace Pinetime::Controllers;
+
+constexpr ble_uuid16_t CurrentTimeClient::ctsServiceUuid;
+constexpr ble_uuid16_t CurrentTimeClient::currentTimeCharacteristicUuid;
+
+CurrentTimeClient::CurrentTimeClient(DateTime& dateTimeController) : dateTimeController{dateTimeController} {
+
+}
+
+void CurrentTimeClient::Init() {
+
+}
+
+bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) {
+    if(service == nullptr && error->status == BLE_HS_EDONE) {
+        NRF_LOG_INFO("CTS Discovery complete");
+        return true;
+    }
+
+    if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ctsServiceUuid), &service->uuid.u) == 0) {
+        NRF_LOG_INFO("CTS discovered : 0x%x",  service->start_handle);
+        isDiscovered = true;
+        ctsStartHandle = service->start_handle;
+        ctsEndHandle = service->end_handle;
+        return false;
+    }
+    return false;
+}
+
+int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
+                                                      const ble_gatt_chr *characteristic) {
+    if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
+        NRF_LOG_INFO("CTS Characteristic discovery complete");
+        return 0;
+    }
+
+    if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&currentTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
+        NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle);
+        currentTimeHandle = characteristic->val_handle;
+    }
+    return 0;
+}
+
+int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute) {
+    if(error->status == 0) {
+        // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
+        CtsData result;
+        os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
+        NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
+                     result.month, result.dayofmonth,
+                     result.hour, result.minute, result.second);
+        dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
+                                   0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
+    } else {
+        NRF_LOG_INFO("Error retrieving current time: %d", error->status);
+    }
+    return 0;
+}
+
+bool CurrentTimeClient::IsDiscovered() const {
+    return isDiscovered;
+}
+
+uint16_t CurrentTimeClient::StartHandle() const {
+    return ctsStartHandle;
+}
+
+uint16_t CurrentTimeClient::EndHandle() const {
+    return ctsEndHandle;
+}
+
+uint16_t CurrentTimeClient::CurrentTimeHandle() const {
+    return currentTimeHandle;
+}
\ No newline at end of file




diff --git a/src/components/Ble/CurrentTimeClient.h b/src/components/Ble/CurrentTimeClient.h
new file mode 100644
index 0000000000000000000000000000000000000000..fabcdacae8366c32657c56707e3cc4c06377577c
--- /dev/null
+++ b/src/components/Ble/CurrentTimeClient.h
@@ -0,0 +1,55 @@
+#pragma once
+#include <cstdint>
+#include <array>
+#include <Components/DateTime/DateTimeController.h>
+#include <host/ble_gap.h>
+
+namespace Pinetime {
+    namespace Controllers {
+
+        class CurrentTimeClient {
+        public:
+            explicit CurrentTimeClient(DateTime& dateTimeController);
+            void Init();
+            bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service);
+            int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
+                                               const ble_gatt_chr *characteristic);
+            int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute);
+            bool IsDiscovered() const;
+            uint16_t StartHandle() const;
+            uint16_t EndHandle() const;
+            uint16_t CurrentTimeHandle() const;
+            static constexpr const ble_uuid16_t* Uuid() { return &CurrentTimeClient::ctsServiceUuid; }
+            static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() { return &CurrentTimeClient::currentTimeCharacteristicUuid; }
+        private:
+            typedef struct __attribute__((packed)) {
+                uint16_t year;
+                uint8_t month;
+                uint8_t dayofmonth;
+                uint8_t hour;
+                uint8_t minute;
+                uint8_t second;
+                uint8_t millis;
+                uint8_t reason;
+            } CtsData;
+
+            static constexpr uint16_t ctsServiceId {0x1805};
+            static constexpr uint16_t currentTimeCharacteristicId {0x2a2b};
+
+            static constexpr ble_uuid16_t ctsServiceUuid {
+                    .u { .type = BLE_UUID_TYPE_16 },
+                    .value = ctsServiceId
+            };
+            static constexpr ble_uuid16_t currentTimeCharacteristicUuid {
+                    .u { .type = BLE_UUID_TYPE_16 },
+                    .value = currentTimeCharacteristicId
+            };
+
+            uint16_t currentTimeHandle;
+            DateTime& dateTimeController;
+            bool isDiscovered = false;
+            uint16_t ctsStartHandle;
+            uint16_t ctsEndHandle;
+        };
+    }
+}
\ No newline at end of file




diff --git a/src/components/Ble/CurrentTimeService.cpp b/src/components/Ble/CurrentTimeService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3a6264e2bebd353b2d5a40173b823593ced61103
--- /dev/null
+++ b/src/components/Ble/CurrentTimeService.cpp
@@ -0,0 +1,86 @@
+#include "CurrentTimeService.h"
+#include <hal/nrf_rtc.h>
+
+using namespace Pinetime::Controllers;
+
+constexpr ble_uuid16_t CurrentTimeService::ctsUuid;
+constexpr ble_uuid16_t CurrentTimeService::ctChrUuid;
+
+
+int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
+  auto cts = static_cast<CurrentTimeService*>(arg);
+  return cts->OnTimeAccessed(conn_handle, attr_handle, ctxt);
+}
+
+void CurrentTimeService::Init() {
+  int res;
+  res = ble_gatts_count_cfg(serviceDefinition);
+  ASSERT(res == 0);
+
+  res = ble_gatts_add_svcs(serviceDefinition);
+  ASSERT(res == 0);
+}
+
+
+int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle,
+                                                    struct ble_gatt_access_ctxt *ctxt) {
+
+    NRF_LOG_INFO("Setting time...");
+
+  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+    CtsData result;
+    os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &result);
+
+    NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
+            result.month, result.dayofmonth,
+            result.hour, result.minute, result.second);
+
+    m_dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
+                        0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
+
+  } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+    CtsData currentDateTime;
+    currentDateTime.year = m_dateTimeController.Year();
+    currentDateTime.month = static_cast<u_int8_t>(m_dateTimeController.Month());
+    currentDateTime.dayofmonth = m_dateTimeController.Day();
+    currentDateTime.hour = m_dateTimeController.Hours();
+    currentDateTime.minute = m_dateTimeController.Minutes();
+    currentDateTime.second = m_dateTimeController.Seconds();
+    currentDateTime.millis = 0;
+
+
+    int res = os_mbuf_append(ctxt->om, &currentDateTime, sizeof(CtsData));
+    return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+
+  }
+
+  return 0;
+}
+
+CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) :
+        characteristicDefinition{
+                {
+                        .uuid = (ble_uuid_t *) &ctChrUuid,
+                        .access_cb = CTSCallback,
+
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
+                },
+                {
+                  0
+                }
+        },
+        serviceDefinition{
+                {
+                        /* Device Information Service */
+                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+                        .uuid = (ble_uuid_t *) &ctsUuid,
+                        .characteristics = characteristicDefinition
+                },
+                {
+                        0
+                },
+        }, m_dateTimeController{dateTimeController} {
+
+}
+




diff --git a/src/components/Ble/CurrentTimeService.h b/src/components/Ble/CurrentTimeService.h
new file mode 100644
index 0000000000000000000000000000000000000000..58bc5ba6397b188d0282ae3cfc3a296f1ad1f73b
--- /dev/null
+++ b/src/components/Ble/CurrentTimeService.h
@@ -0,0 +1,48 @@
+#pragma once
+#include <cstdint>
+#include <array>
+#include <Components/DateTime/DateTimeController.h>
+#include <host/ble_gap.h>
+
+namespace Pinetime {
+  namespace Controllers {
+    class CurrentTimeService {
+      public:
+        CurrentTimeService(DateTime &dateTimeController);
+        void Init();
+
+        int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle,
+                                    struct ble_gatt_access_ctxt *ctxt);
+
+      private:
+        static constexpr uint16_t ctsId {0x1805};
+        static constexpr uint16_t ctsCharId {0x2a2b};
+
+        static constexpr ble_uuid16_t ctsUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = ctsId
+        };
+
+        static constexpr ble_uuid16_t ctChrUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = ctsCharId
+        };
+
+        struct ble_gatt_chr_def characteristicDefinition[2];
+        struct ble_gatt_svc_def serviceDefinition[2];
+
+        typedef struct __attribute__((packed)) {
+          uint16_t year;
+          uint8_t month;
+          uint8_t dayofmonth;
+          uint8_t hour;
+          uint8_t minute;
+          uint8_t second;
+          uint8_t millis;
+          uint8_t reason;
+        } CtsData;
+
+        DateTime &m_dateTimeController;
+    };
+  }
+}




diff --git a/src/components/Ble/DeviceInformationService.cpp b/src/components/Ble/DeviceInformationService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..406db1cff7a319e03758c013176893513307cd86
--- /dev/null
+++ b/src/components/Ble/DeviceInformationService.cpp
@@ -0,0 +1,116 @@
+#include "DeviceInformationService.h"
+
+using namespace Pinetime::Controllers;
+
+constexpr ble_uuid16_t DeviceInformationService::manufacturerNameUuid;
+constexpr ble_uuid16_t DeviceInformationService::modelNumberUuid;
+constexpr ble_uuid16_t DeviceInformationService::serialNumberUuid;
+constexpr ble_uuid16_t DeviceInformationService::fwRevisionUuid;
+constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid;
+constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid;
+constexpr ble_uuid16_t DeviceInformationService::swRevisionUuid;
+
+
+int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
+  auto deviceInformationService = static_cast<DeviceInformationService*>(arg);
+  return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt);
+}
+
+void DeviceInformationService::Init() {
+  int res = 0;
+  res = ble_gatts_count_cfg(serviceDefinition);
+  ASSERT(res == 0);
+
+  res = ble_gatts_add_svcs(serviceDefinition);
+  ASSERT(res == 0);
+}
+
+
+int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
+                                                    struct ble_gatt_access_ctxt *ctxt) {
+  const char *str;
+
+  switch (ble_uuid_u16(ctxt->chr->uuid)) {
+    case manufacturerNameId:
+      str = manufacturerName;
+      break;
+    case modelNumberId:
+      str = modelNumber;
+      break;
+    case serialNumberId:
+      str = serialNumber;
+      break;
+    case fwRevisionId:
+      str = fwRevision;
+      break;
+    case hwRevisionId:
+      str = hwRevision;
+      break;
+    case swRevisionId:
+      str = swRevision;
+      break;
+    default:
+      return BLE_ATT_ERR_UNLIKELY;
+  }
+
+  int res = os_mbuf_append(ctxt->om, str, strlen(str));
+  return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+}
+
+DeviceInformationService::DeviceInformationService() :
+        characteristicDefinition{
+                {
+                        .uuid = (ble_uuid_t *) &manufacturerNameUuid,
+                        .access_cb = DeviceInformationCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_READ,
+                },
+                {
+                        .uuid = (ble_uuid_t *) &modelNumberUuid,
+                        .access_cb = DeviceInformationCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_READ,
+                },
+                {
+                        .uuid = (ble_uuid_t *) &serialNumberUuid,
+                        .access_cb = DeviceInformationCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_READ,
+                },
+                {
+                        .uuid = (ble_uuid_t *) &fwRevisionUuid,
+                        .access_cb = DeviceInformationCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_READ,
+                },
+                {
+                        .uuid = (ble_uuid_t *) &hwRevisionUuid,
+                        .access_cb = DeviceInformationCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_READ,
+                },
+                {
+                        .uuid = (ble_uuid_t *) &swRevisionUuid,
+                        .access_cb = DeviceInformationCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_READ,
+                },
+                {
+                  0
+                }
+        },
+        serviceDefinition{
+                {
+                        /* Device Information Service */
+                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+                        .uuid = (ble_uuid_t *) &deviceInfoUuid,
+                        .characteristics = characteristicDefinition
+                },
+                {
+                        0
+                },
+        }
+         {
+
+}
+




diff --git a/src/components/Ble/DeviceInformationService.h b/src/components/Ble/DeviceInformationService.h
new file mode 100644
index 0000000000000000000000000000000000000000..25ab8402ea54573372ea6e5012bd74795e243f82
--- /dev/null
+++ b/src/components/Ble/DeviceInformationService.h
@@ -0,0 +1,76 @@
+#pragma once
+#include <cstdint>
+#include <array>
+
+#include <host/ble_gap.h>
+#include <Version.h>
+
+namespace Pinetime {
+  namespace Controllers {
+    class DeviceInformationService {
+      public:
+        DeviceInformationService();
+        void Init();
+
+        int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
+                                    struct ble_gatt_access_ctxt *ctxt);
+
+      private:
+        static constexpr uint16_t deviceInfoId {0x180a};
+        static constexpr uint16_t manufacturerNameId {0x2a29};
+        static constexpr uint16_t modelNumberId {0x2a24};
+        static constexpr uint16_t serialNumberId {0x2a25};
+        static constexpr uint16_t fwRevisionId {0x2a26};
+        static constexpr uint16_t hwRevisionId {0x2a27};
+        static constexpr uint16_t swRevisionId {0x2a28};
+
+        static constexpr const char* manufacturerName = "PINE64";
+        static constexpr const char* modelNumber = "PineTime";
+        static constexpr const char* hwRevision = "1.0.0";
+        static constexpr const char* serialNumber = "0";
+        static constexpr const char* fwRevision =  Version::VersionString();
+        static constexpr const char* swRevision = "InfiniTime";
+
+
+        static constexpr ble_uuid16_t deviceInfoUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = deviceInfoId
+        };
+
+        static constexpr ble_uuid16_t manufacturerNameUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = manufacturerNameId
+        };
+
+        static constexpr ble_uuid16_t modelNumberUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = modelNumberId
+        };
+
+        static constexpr ble_uuid16_t serialNumberUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = serialNumberId
+        };
+
+        static constexpr ble_uuid16_t fwRevisionUuid {
+                .u { .type = BLE_UUID_TYPE_16 },
+                .value = fwRevisionId
+        };
+
+        static constexpr ble_uuid16_t hwRevisionUuid {
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = hwRevisionId
+        };
+
+        static constexpr ble_uuid16_t swRevisionUuid {
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = swRevisionId
+        };
+
+        struct ble_gatt_chr_def characteristicDefinition[7];
+        struct ble_gatt_svc_def serviceDefinition[2];
+
+
+    };
+  }
+}
\ No newline at end of file




diff --git a/src/components/Ble/DfuService.cpp b/src/components/Ble/DfuService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fcbefdd037591e22c34bd2dad04670b854aa47b7
--- /dev/null
+++ b/src/components/Ble/DfuService.cpp
@@ -0,0 +1,440 @@
+#include <Components/Ble/BleController.h>
+#include <SystemTask/SystemTask.h>
+#include <cstring>
+#include "DfuService.h"
+
+using namespace Pinetime::Controllers;
+
+constexpr ble_uuid128_t DfuService::serviceUuid;
+constexpr ble_uuid128_t DfuService::controlPointCharacteristicUuid;
+constexpr ble_uuid128_t DfuService::revisionCharacteristicUuid;
+constexpr ble_uuid128_t DfuService::packetCharacteristicUuid;
+
+int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle,
+                       struct ble_gatt_access_ctxt *ctxt, void *arg) {
+  auto dfuService = static_cast<DfuService *>(arg);
+  return dfuService->OnServiceData(conn_handle, attr_handle, ctxt);
+}
+
+void NotificationTimerCallback(TimerHandle_t xTimer) {
+  auto notificationManager = static_cast<DfuService::NotificationManager *>(pvTimerGetTimerID(xTimer));
+  notificationManager->OnNotificationTimer();
+}
+
+void TimeoutTimerCallback(TimerHandle_t xTimer) {
+  auto dfuService = static_cast<DfuService *>(pvTimerGetTimerID(xTimer));
+  dfuService->OnTimeout();
+}
+
+DfuService::DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController,
+                       Pinetime::Drivers::SpiNorFlash &spiNorFlash) :
+        systemTask{systemTask},
+        bleController{bleController},
+        dfuImage{spiNorFlash},
+        characteristicDefinition{
+                {
+                        .uuid = (ble_uuid_t *) &packetCharacteristicUuid,
+                        .access_cb = DfuServiceCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
+                        .val_handle = nullptr,
+                },
+                {
+                        .uuid = (ble_uuid_t *) &controlPointCharacteristicUuid,
+                        .access_cb = DfuServiceCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
+                        .val_handle = nullptr,
+                },
+                {
+                        .uuid = (ble_uuid_t *) &revisionCharacteristicUuid,
+                        .access_cb = DfuServiceCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_READ,
+                        .val_handle = &revision,
+
+                },
+                {
+                        0
+                }
+
+        },
+        serviceDefinition{
+                {
+                        /* Device Information Service */
+                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+                        .uuid = (ble_uuid_t *) &serviceUuid,
+                        .characteristics = characteristicDefinition
+                },
+                {
+                        0
+                },
+        } {
+  timeoutTimer = xTimerCreate ("notificationTimer", 10000, pdFALSE, this, TimeoutTimerCallback);
+}
+
+void DfuService::Init() {
+  int res;
+  res = ble_gatts_count_cfg(serviceDefinition);
+  ASSERT(res == 0);
+
+  res = ble_gatts_add_svcs(serviceDefinition);
+  ASSERT(res == 0);
+}
+
+int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) {
+  if(bleController.IsFirmwareUpdating()){
+    xTimerStart(timeoutTimer, 0);
+  }
+
+
+  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &packetCharacteristicUuid, nullptr,
+                     &packetCharacteristicHandle);
+  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &controlPointCharacteristicUuid, nullptr,
+                     &controlPointCharacteristicHandle);
+  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &revisionCharacteristicUuid, nullptr,
+                     &revisionCharacteristicHandle);
+
+  if (attributeHandle == packetCharacteristicHandle) {
+    if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
+      return WritePacketHandler(connectionHandle, context->om);
+    else return 0;
+  } else if (attributeHandle == controlPointCharacteristicHandle) {
+    if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
+      return ControlPointHandler(connectionHandle, context->om);
+    else return 0;
+  } else if (attributeHandle == revisionCharacteristicHandle) {
+    if (context->op == BLE_GATT_ACCESS_OP_READ_CHR)
+      return SendDfuRevision(context->om);
+    else return 0;
+  } else {
+    NRF_LOG_INFO("[DFU] Unknown Characteristic : %d", attributeHandle);
+    return 0;
+  }
+}
+
+int DfuService::SendDfuRevision(os_mbuf *om) const {
+  int res = os_mbuf_append(om, &revision, sizeof(revision));
+  return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+}
+
+int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
+  switch (state) {
+    case States::Start: {
+      softdeviceSize = om->om_data[0] + (om->om_data[1] << 8) + (om->om_data[2] << 16) + (om->om_data[3] << 24);
+      bootloaderSize = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
+      applicationSize = om->om_data[8] + (om->om_data[9] << 8) + (om->om_data[10] << 16) + (om->om_data[11] << 24);
+      bleController.FirmwareUpdateTotalBytes(applicationSize);
+      NRF_LOG_INFO("[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize,
+                   bootloaderSize, applicationSize);
+
+      dfuImage.Erase();
+
+      uint8_t data[]{16, 1, 1};
+      notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
+      state = States::Init;
+    }
+      return 0;
+    case States::Init: {
+      uint16_t deviceType = om->om_data[0] + (om->om_data[1] << 8);
+      uint16_t deviceRevision = om->om_data[2] + (om->om_data[3] << 8);
+      uint32_t applicationVersion =
+              om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
+      uint16_t softdeviceArrayLength = om->om_data[8] + (om->om_data[9] << 8);
+      uint16_t sd[softdeviceArrayLength];
+      for (int i = 0; i < softdeviceArrayLength; i++) {
+        sd[i] = om->om_data[10 + (i * 2)] + (om->om_data[10 + (i * 2) + 1] << 8);
+      }
+      expectedCrc =
+              om->om_data[10 + (softdeviceArrayLength * 2)] + (om->om_data[10 + (softdeviceArrayLength * 2) + 1] << 8);
+
+      NRF_LOG_INFO(
+              "[DFU] -> Init data received : deviceType = %d, deviceRevision = %d, applicationVersion = %d, nb SD = %d, First SD = %d, CRC = %u",
+              deviceType, deviceRevision, applicationVersion, softdeviceArrayLength, sd[0], expectedCrc);
+
+      return 0;
+    }
+
+    case States::Data: {
+      nbPacketReceived++;
+      dfuImage.Append(om->om_data, om->om_len);
+      bytesReceived += om->om_len;
+      bleController.FirmwareUpdateCurrentBytes(bytesReceived);
+
+      if ((nbPacketReceived % nbPacketsToNotify) == 0 && bytesReceived != applicationSize) {
+        uint8_t data[5]{static_cast<uint8_t>(Opcodes::PacketReceiptNotification),
+                        (uint8_t) (bytesReceived & 0x000000FFu), (uint8_t) (bytesReceived >> 8u),
+                        (uint8_t) (bytesReceived >> 16u), (uint8_t) (bytesReceived >> 24u)};
+        NRF_LOG_INFO("[DFU] -> Send packet notification: %d bytes received", bytesReceived);
+        notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 5);
+      }
+      if (dfuImage.IsComplete()) {
+        uint8_t data[3]{static_cast<uint8_t>(Opcodes::Response),
+                        static_cast<uint8_t>(Opcodes::ReceiveFirmwareImage),
+                        static_cast<uint8_t>(ErrorCodes::NoError)};
+        NRF_LOG_INFO("[DFU] -> Send packet notification : all bytes received!");
+        notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
+        state = States::Validate;
+      }
+    }
+      return 0;
+    default:
+      // Invalid state
+      return 0;
+  }
+  return 0;
+}
+
+int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
+  auto opcode = static_cast<Opcodes>(om->om_data[0]);
+  NRF_LOG_INFO("[DFU] -> ControlPointHandler");
+
+  switch (opcode) {
+    case Opcodes::StartDFU: {
+      if (state != States::Idle && state != States::Start) {
+        NRF_LOG_INFO("[DFU] -> Start DFU requested, but we are not in Idle state");
+        return 0;
+      }
+      if (state == States::Start) {
+        NRF_LOG_INFO("[DFU] -> Start DFU requested, but we are already in Start state");
+        return 0;
+      }
+      auto imageType = static_cast<ImageTypes>(om->om_data[1]);
+      if (imageType == ImageTypes::Application) {
+        NRF_LOG_INFO("[DFU] -> Start DFU, mode = Application");
+        state = States::Start;
+        bleController.StartFirmwareUpdate();
+        bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Running);
+        bleController.FirmwareUpdateTotalBytes(0xffffffffu);
+        bleController.FirmwareUpdateCurrentBytes(0);
+        systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateStarted);
+        return 0;
+      } else {
+        NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType);
+        return 0;
+      }
+    }
+      break;
+    case Opcodes::InitDFUParameters: {
+      if (state != States::Init) {
+        NRF_LOG_INFO("[DFU] -> Init DFU requested, but we are not in Init state");
+        return 0;
+      }
+      bool isInitComplete = (om->om_data[1] != 0);
+      NRF_LOG_INFO("[DFU] -> Init DFU parameters %s", isInitComplete ? " complete" : " not complete");
+
+      if (isInitComplete) {
+        uint8_t data[3] {
+                static_cast<uint8_t>(Opcodes::Response),
+                static_cast<uint8_t>(Opcodes::InitDFUParameters),
+                (isInitComplete ? uint8_t{1} : uint8_t{0})
+        };
+        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
+        return 0;
+      }
+    }
+      return 0;
+    case Opcodes::PacketReceiptNotificationRequest:
+      nbPacketsToNotify = om->om_data[1];
+      NRF_LOG_INFO("[DFU] -> Receive Packet Notification Request, nb packet = %d", nbPacketsToNotify);
+      return 0;
+    case Opcodes::ReceiveFirmwareImage:
+      if (state != States::Init) {
+        NRF_LOG_INFO("[DFU] -> Receive firmware image requested, but we are not in Start Init");
+        return 0;
+      }
+      // TODO the chunk size is dependant of the implementation of the host application...
+      dfuImage.Init(20, applicationSize, expectedCrc);
+      NRF_LOG_INFO("[DFU] -> Starting receive firmware");
+      state = States::Data;
+      return 0;
+    case Opcodes::ValidateFirmware: {
+      if (state != States::Validate) {
+        NRF_LOG_INFO("[DFU] -> Validate firmware image requested, but we are not in Data state %d", state);
+        return 0;
+      }
+
+      NRF_LOG_INFO("[DFU] -> Validate firmware image requested -- %d", connectionHandle);
+
+      if(dfuImage.Validate()){
+        state = States::Validated;
+        bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
+        NRF_LOG_INFO("Image OK");
+
+        uint8_t data[3] {
+                static_cast<uint8_t>(Opcodes::Response),
+                static_cast<uint8_t>(Opcodes::ValidateFirmware),
+                static_cast<uint8_t>(ErrorCodes::NoError)
+        };
+        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
+      } else {
+        bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
+        NRF_LOG_INFO("Image Error : bad CRC");
+
+        uint8_t data[3] {
+                static_cast<uint8_t>(Opcodes::Response),
+                static_cast<uint8_t>(Opcodes::ValidateFirmware),
+                static_cast<uint8_t>(ErrorCodes::CrcError)
+        };
+        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
+      }
+
+      return 0;
+    }
+    case Opcodes::ActivateImageAndReset:
+      if (state != States::Validated) {
+        NRF_LOG_INFO("[DFU] -> Activate image and reset requested, but we are not in Validated state");
+        return 0;
+      }
+      NRF_LOG_INFO("[DFU] -> Activate image and reset!");
+      bleController.StopFirmwareUpdate();
+      systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
+      Reset();
+      bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
+      return 0;
+    default:
+      return 0;
+  }
+}
+
+void DfuService::OnTimeout() {
+  Reset();
+}
+
+void DfuService::Reset() {
+  state = States::Idle;
+  nbPacketsToNotify = 0;
+  nbPacketReceived = 0;
+  bytesReceived = 0;
+  softdeviceSize = 0;
+  bootloaderSize = 0;
+  applicationSize = 0;
+  expectedCrc = 0;
+  notificationManager.Reset();
+  bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
+  bleController.StopFirmwareUpdate();
+  systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
+}
+
+DfuService::NotificationManager::NotificationManager() {
+  timer = xTimerCreate ("notificationTimer", 1000, pdFALSE, this, NotificationTimerCallback);
+}
+
+bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t *data, size_t s) {
+  if(size != 0 || s > 10)
+    return false;
+
+  connectionHandle = connection;
+  characteristicHandle = charactHandle;
+  size = s;
+  std::memcpy(buffer, data, size);
+  xTimerStart(timer, 0);
+  return true;
+}
+
+void DfuService::NotificationManager::OnNotificationTimer() {
+  if(size > 0) {
+    Send(connectionHandle, characteristicHandle, buffer, size);
+    size = 0;
+  }
+}
+
+void DfuService::NotificationManager::Send(uint16_t connection, uint16_t charactHandle, const uint8_t *data, const size_t s) {
+  auto *om = ble_hs_mbuf_from_flat(data, s);
+  auto ret = ble_gattc_notify_custom(connection, charactHandle, om);
+  ASSERT(ret == 0);
+}
+
+void DfuService::NotificationManager::Reset() {
+  connectionHandle = 0;
+  characteristicHandle = 0;
+  size = 0;
+  xTimerStop(timer, 0);
+}
+
+void DfuService::DfuImage::Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc) {
+  if(chunkSize != 20) return;
+  this->chunkSize = chunkSize;
+  this->totalSize = totalSize;
+  this->expectedCrc = expectedCrc;
+  this->ready = true;
+}
+
+void DfuService::DfuImage::Append(uint8_t *data, size_t size) {
+  if(!ready) return;
+  ASSERT(size <= 20);
+
+  std::memcpy(tempBuffer + bufferWriteIndex, data, size);
+  bufferWriteIndex += size;
+
+  if(bufferWriteIndex == bufferSize) {
+    spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
+    totalWriteIndex += bufferWriteIndex;
+    bufferWriteIndex = 0;
+  }
+
+  if(bufferWriteIndex > 0 && totalWriteIndex + bufferWriteIndex == totalSize) {
+    spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
+    totalWriteIndex += bufferWriteIndex;
+    if (totalSize < maxSize)
+      WriteMagicNumber();
+  }
+}
+
+void DfuService::DfuImage::WriteMagicNumber() {
+  uint32_t magic[4] = { // TODO When this variable is a static constexpr, the values written to the memory are not correct. Why?
+          0xf395c277,
+          0x7fefd260,
+          0x0f505235,
+          0x8079b62c,
+  };
+
+  uint32_t offset = writeOffset + (maxSize - (4 * sizeof(uint32_t)));
+  spiNorFlash.Write(offset, reinterpret_cast<const uint8_t *>(magic), 4 * sizeof(uint32_t));
+}
+
+void DfuService::DfuImage::Erase() {
+  for (size_t erased = 0; erased < maxSize; erased += 0x1000) {
+    spiNorFlash.SectorErase(writeOffset + erased);
+  }
+}
+
+bool DfuService::DfuImage::Validate() {
+  uint32_t chunkSize = 200;
+  size_t currentOffset = 0;
+  uint16_t crc = 0;
+
+  bool first = true;
+  while (currentOffset < totalSize) {
+    uint32_t readSize = (totalSize - currentOffset) > chunkSize ? chunkSize : (totalSize - currentOffset);
+
+    spiNorFlash.Read(writeOffset + currentOffset, tempBuffer, readSize);
+    if (first) {
+      crc = ComputeCrc(tempBuffer, readSize, NULL);
+      first = false;
+    } else
+      crc = ComputeCrc(tempBuffer, readSize, &crc);
+    currentOffset += readSize;
+  }
+
+  return (crc == expectedCrc);
+}
+
+uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const *p_data, uint32_t size, uint16_t const *p_crc) {
+  uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
+
+  for (uint32_t i = 0; i < size; i++) {
+    crc = (uint8_t) (crc >> 8) | (crc << 8);
+    crc ^= p_data[i];
+    crc ^= (uint8_t) (crc & 0xFF) >> 4;
+    crc ^= (crc << 8) << 4;
+    crc ^= ((crc & 0xFF) << 4) << 1;
+  }
+
+  return crc;
+}
+
+bool DfuService::DfuImage::IsComplete() {
+  if(!ready) return false;
+  return totalWriteIndex == totalSize;
+}




diff --git a/src/components/Ble/DfuService.h b/src/components/Ble/DfuService.h
new file mode 100644
index 0000000000000000000000000000000000000000..d7ba460c70b767a5ee03520f88a0550e7121b69d
--- /dev/null
+++ b/src/components/Ble/DfuService.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <cstdint>
+#include <array>
+
+#include <host/ble_gap.h>
+
+namespace Pinetime {
+  namespace System {
+    class SystemTask;
+  }
+  namespace Drivers {
+    class SpiNorFlash;
+  }
+  namespace Controllers {
+    class Ble;
+
+    class DfuService {
+      public:
+        DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController,
+                   Pinetime::Drivers::SpiNorFlash &spiNorFlash);
+        void Init();
+        int OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
+        void OnTimeout();
+        void Reset();
+
+        class NotificationManager {
+          public:
+            NotificationManager();
+            bool AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t *data, size_t size);
+            void Send(uint16_t connection, uint16_t characteristicHandle, const uint8_t *data, const size_t s);
+          private:
+            TimerHandle_t timer;
+            uint16_t connectionHandle = 0;
+            uint16_t characteristicHandle = 0;
+            size_t size = 0;
+            uint8_t buffer[10];
+          public:
+            void OnNotificationTimer();
+            void Reset();
+        };
+        class DfuImage {
+          public:
+            DfuImage(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash{spiNorFlash} {}
+            void Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc);
+            void Erase();
+            void Append(uint8_t* data, size_t size);
+            bool Validate();
+            bool IsComplete();
+
+          private:
+            Pinetime::Drivers::SpiNorFlash& spiNorFlash;
+            static constexpr size_t bufferSize = 200;
+            bool ready = false;
+            size_t chunkSize = 0;
+            size_t totalSize = 0;
+            size_t maxSize = 475136;
+            size_t bufferWriteIndex = 0;
+            size_t totalWriteIndex = 0;
+            static constexpr size_t writeOffset = 0x40000;
+            uint8_t tempBuffer[bufferSize];
+            uint16_t expectedCrc = 0;
+
+            void WriteMagicNumber();
+            uint16_t ComputeCrc(uint8_t const *p_data, uint32_t size, uint16_t const *p_crc);
+
+        };
+
+      private:
+        Pinetime::System::SystemTask &systemTask;
+        Pinetime::Controllers::Ble &bleController;
+        DfuImage dfuImage;
+        NotificationManager notificationManager;
+
+        static constexpr uint16_t dfuServiceId{0x1530};
+        static constexpr uint16_t packetCharacteristicId{0x1532};
+        static constexpr uint16_t controlPointCharacteristicId{0x1531};
+        static constexpr uint16_t revisionCharacteristicId{0x1534};
+
+        uint16_t revision{0x0008};
+
+        static constexpr ble_uuid128_t serviceUuid{
+                .u {.type = BLE_UUID_TYPE_128},
+                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
+                          0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}
+        };
+
+        static constexpr ble_uuid128_t packetCharacteristicUuid{
+                .u {.type = BLE_UUID_TYPE_128},
+                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
+                          0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00}
+        };
+
+        static constexpr ble_uuid128_t controlPointCharacteristicUuid{
+                .u {.type = BLE_UUID_TYPE_128},
+                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
+                          0xDE, 0xEF, 0x12, 0x12, 0x31, 0x15, 0x00, 0x00}
+        };
+
+        static constexpr ble_uuid128_t revisionCharacteristicUuid{
+                .u {.type = BLE_UUID_TYPE_128},
+                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
+                          0xDE, 0xEF, 0x12, 0x12, 0x34, 0x15, 0x00, 0x00}
+        };
+
+        struct ble_gatt_chr_def characteristicDefinition[4];
+        struct ble_gatt_svc_def serviceDefinition[2];
+        uint16_t packetCharacteristicHandle;
+        uint16_t controlPointCharacteristicHandle;
+        uint16_t revisionCharacteristicHandle;
+
+        enum class States : uint8_t {
+            Idle, Init, Start, Data, Validate, Validated
+        };
+        States state = States::Idle;
+
+        enum class ImageTypes : uint8_t {
+            NoImage = 0x00,
+            SoftDevice = 0x01,
+            Bootloader = 0x02,
+            SoftDeviceAndBootloader = 0x03,
+            Application = 0x04
+        };
+
+        enum class Opcodes : uint8_t {
+            StartDFU = 0x01,
+            InitDFUParameters = 0x02,
+            ReceiveFirmwareImage = 0x03,
+            ValidateFirmware = 0x04,
+            ActivateImageAndReset = 0x05,
+            PacketReceiptNotificationRequest = 0x08,
+            Response = 0x10,
+            PacketReceiptNotification = 0x11
+        };
+
+        enum class ErrorCodes {
+            NoError = 0x01,
+            InvalidState = 0x02,
+            NotSupported = 0x03,
+            DataSizeExceedsLimits = 0x04,
+            CrcError = 0x05,
+            OperationFailed = 0x06
+        };
+
+        uint8_t nbPacketsToNotify = 0;
+        uint32_t nbPacketReceived = 0;
+        uint32_t bytesReceived = 0;
+
+        uint32_t softdeviceSize = 0;
+        uint32_t bootloaderSize = 0;
+        uint32_t applicationSize = 0;
+        uint16_t expectedCrc = 0;
+
+        int SendDfuRevision(os_mbuf *om) const;
+        int WritePacketHandler(uint16_t connectionHandle, os_mbuf *om);
+        int ControlPointHandler(uint16_t connectionHandle, os_mbuf *om);
+
+        TimerHandle_t timeoutTimer;
+    };
+  }
+}
\ No newline at end of file




diff --git a/src/components/Ble/ImmediateAlertService.cpp b/src/components/Ble/ImmediateAlertService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2c4cffb0d01bd76fb36c0fe1067add572b45b99
--- /dev/null
+++ b/src/components/Ble/ImmediateAlertService.cpp
@@ -0,0 +1,76 @@
+#include "ImmediateAlertService.h"
+#include <SystemTask/SystemTask.h>
+#include "AlertNotificationService.h"
+
+using namespace Pinetime::Controllers;
+
+constexpr ble_uuid16_t ImmediateAlertService::immediateAlertServiceUuid;
+constexpr ble_uuid16_t ImmediateAlertService::alertLevelUuid;
+
+namespace {
+  int AlertLevelCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
+    auto *immediateAlertService = static_cast<ImmediateAlertService *>(arg);
+    return immediateAlertService->OnAlertLevelChanged(conn_handle, attr_handle, ctxt);
+  }
+
+  const char* ToString(ImmediateAlertService::Levels level) {
+    switch (level) {
+      case ImmediateAlertService::Levels::NoAlert: return "Alert : None";
+      case ImmediateAlertService::Levels::HighAlert: return "Alert : High";
+      case ImmediateAlertService::Levels::MildAlert: return "Alert : Mild";
+      default: return "";
+    }
+  }
+}
+
+ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask &systemTask,
+                                             Pinetime::Controllers::NotificationManager &notificationManager) :
+        systemTask{systemTask},
+        notificationManager{notificationManager},
+        characteristicDefinition{
+                {
+                        .uuid = (ble_uuid_t *) &alertLevelUuid,
+                        .access_cb = AlertLevelCallback,
+                        .arg = this,
+                        .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
+                        .val_handle = &alertLevelHandle
+                },
+                {
+                        0
+                }
+        },
+        serviceDefinition{
+                {
+                        /* Device Information Service */
+                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+                        .uuid = (ble_uuid_t *) &immediateAlertServiceUuid,
+                        .characteristics = characteristicDefinition
+                },
+                {
+                        0
+                },
+        }{
+
+}
+
+void ImmediateAlertService::Init() {
+  int res = 0;
+  res = ble_gatts_count_cfg(serviceDefinition);
+  ASSERT(res == 0);
+
+  res = ble_gatts_add_svcs(serviceDefinition);
+  ASSERT(res == 0);
+}
+
+int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) {
+  if(attributeHandle == alertLevelHandle) {
+    if(context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+      auto alertLevel = static_cast<Levels>(context->om->om_data[0]);
+      auto* alertString = ToString(alertLevel);
+      notificationManager.Push(Pinetime::Controllers::NotificationManager::Categories::SimpleAlert, alertString, strlen(alertString));
+      systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
+    }
+  }
+
+  return 0;
+}
\ No newline at end of file




diff --git a/src/components/Ble/ImmediateAlertService.h b/src/components/Ble/ImmediateAlertService.h
new file mode 100644
index 0000000000000000000000000000000000000000..c42846c485cd9fe25713bcc511674ee3ba9c87f9
--- /dev/null
+++ b/src/components/Ble/ImmediateAlertService.h
@@ -0,0 +1,46 @@
+#pragma once
+#include <host/ble_gap.h>
+
+namespace Pinetime {
+  namespace System {
+    class SystemTask;
+  }
+  namespace Controllers {
+    class NotificationManager;
+    class ImmediateAlertService {
+      public:
+        enum class Levels : uint8_t {
+            NoAlert = 0,
+            MildAlert = 1,
+            HighAlert = 2
+        };
+
+        ImmediateAlertService(Pinetime::System::SystemTask &systemTask,
+                              Pinetime::Controllers::NotificationManager &notificationManager);
+        void Init();
+        int OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
+
+      private:
+        Pinetime::System::SystemTask& systemTask;
+        NotificationManager& notificationManager;
+
+        static constexpr uint16_t immediateAlertServiceId {0x1802};
+        static constexpr uint16_t alertLevelId {0x2A06};
+
+        static constexpr ble_uuid16_t immediateAlertServiceUuid {
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = immediateAlertServiceId
+        };
+
+        static constexpr ble_uuid16_t alertLevelUuid {
+                .u {.type = BLE_UUID_TYPE_16},
+                .value = alertLevelId
+        };
+
+        struct ble_gatt_chr_def characteristicDefinition[3];
+        struct ble_gatt_svc_def serviceDefinition[2];
+
+        uint16_t alertLevelHandle;
+    };
+  }
+}




diff --git a/src/components/Ble/MusicService.cpp b/src/components/Ble/MusicService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5fa53562bd2fd054f370ab73adee332ad18e859
--- /dev/null
+++ b/src/components/Ble/MusicService.cpp
@@ -0,0 +1,129 @@
+#include <SystemTask/SystemTask.h>
+#include "MusicService.h"
+
+int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
+  auto musicService = static_cast<Pinetime::Controllers::MusicService*>(arg);
+  return musicService->OnCommand(conn_handle, attr_handle, ctxt);
+}
+
+Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask &system) : m_system(system)
+{
+    msUuid.value[11] = msId[0];
+    msUuid.value[12] = msId[1];
+    msEventCharUuid.value[11] = msEventCharId[0];
+    msEventCharUuid.value[12] = msEventCharId[1];
+    msStatusCharUuid.value[11] = msStatusCharId[0];
+    msStatusCharUuid.value[12] = msStatusCharId[1];
+    msTrackCharUuid.value[11] = msTrackCharId[0];
+    msTrackCharUuid.value[12] = msTrackCharId[1];
+    msArtistCharUuid.value[11] = msArtistCharId[0];
+    msArtistCharUuid.value[12] = msArtistCharId[1];
+    msAlbumCharUuid.value[11] = msAlbumCharId[0];
+    msAlbumCharUuid.value[12] = msAlbumCharId[1];
+
+    characteristicDefinition[0] = { .uuid = (ble_uuid_t*)(&msEventCharUuid),
+                                    .access_cb = MSCallback,
+                                    .arg = this,
+                                    .flags =  BLE_GATT_CHR_F_NOTIFY,
+                                    .val_handle = &m_eventHandle
+    };
+    characteristicDefinition[1] = { .uuid = (ble_uuid_t*)(&msStatusCharUuid),
+                                    .access_cb = MSCallback,
+                                    .arg = this,
+                                    .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
+    };
+    characteristicDefinition[2] = { .uuid = (ble_uuid_t*)(&msTrackCharUuid),
+                                    .access_cb = MSCallback,
+                                    .arg = this,
+                                    .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
+    };
+    characteristicDefinition[3] = { .uuid = (ble_uuid_t*)(&msArtistCharUuid),
+                                    .access_cb = MSCallback,
+                                    .arg = this,
+                                    .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
+    };
+    characteristicDefinition[4] = { .uuid = (ble_uuid_t*)(&msAlbumCharUuid),
+                                    .access_cb = MSCallback,
+                                    .arg = this,
+                                    .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
+    };
+    characteristicDefinition[5] = {0};
+
+    serviceDefinition[0] = {
+                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+                        .uuid = (ble_uuid_t *) &msUuid,
+                        .characteristics = characteristicDefinition
+    };
+    serviceDefinition[1] = {0};
+
+    m_artist = "Waiting for";
+    m_album = "";
+    m_track = "track information...";
+}
+
+void Pinetime::Controllers::MusicService::Init()
+{
+  int res = 0;
+  res = ble_gatts_count_cfg(serviceDefinition);
+  ASSERT(res == 0);
+
+  res = ble_gatts_add_svcs(serviceDefinition);
+  ASSERT(res == 0);
+}
+
+int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle,
+                                                    struct ble_gatt_access_ctxt *ctxt) {
+
+  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+        size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
+        uint8_t data[notifSize + 1];
+        data[notifSize] = '\0';
+        os_mbuf_copydata(ctxt->om, 0, notifSize, data);
+        char *s = (char *) &data[0];
+        NRF_LOG_INFO("DATA : %s", s);
+        if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msArtistCharUuid) == 0) {
+            m_artist = s;
+        } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msTrackCharUuid) == 0) {
+            m_track = s;
+        } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msAlbumCharUuid) == 0) {
+            m_album = s;
+        } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *)&msStatusCharUuid) == 0) {
+            m_status = s[0];
+        }
+  }
+  return 0;
+}
+
+std::string Pinetime::Controllers::MusicService::album()
+{
+    return m_album;
+}
+
+std::string Pinetime::Controllers::MusicService::artist()
+{
+    return m_artist;
+}
+
+std::string Pinetime::Controllers::MusicService::track()
+{
+    return m_track;
+}
+
+unsigned char Pinetime::Controllers::MusicService::status()
+{
+    return m_status;
+}
+
+void Pinetime::Controllers::MusicService::event(char event)
+{
+    auto *om = ble_hs_mbuf_from_flat(&event, 1);
+
+    uint16_t connectionHandle = m_system.nimble().connHandle();
+
+    if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
+        return;
+    }
+
+    ble_gattc_notify_custom(connectionHandle, m_eventHandle, om);
+}
+




diff --git a/src/components/Ble/MusicService.h b/src/components/Ble/MusicService.h
new file mode 100644
index 0000000000000000000000000000000000000000..ab6db572b183980efec46bd4942f729b4762d672
--- /dev/null
+++ b/src/components/Ble/MusicService.h
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <cstdint>
+#include <array>
+#include <host/ble_gap.h>
+#include <host/ble_uuid.h>
+#include <string>
+
+//c7e50000-78fc-48fe-8e23-43b37a1942d0
+#define MUSIC_SERVICE_UUID_BASE {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0xe5, 0xc7}
+
+namespace Pinetime {
+  namespace System {
+    class SystemTask;
+  }
+  namespace Controllers {
+
+    class MusicService {
+      public:
+        MusicService(Pinetime::System::SystemTask &system);
+        void Init();
+        int OnCommand(uint16_t conn_handle, uint16_t attr_handle,
+                                    struct ble_gatt_access_ctxt *ctxt);
+
+        std::string artist();
+        std::string track();
+        std::string album();
+        unsigned char status();
+
+        void event(char event);
+
+        static const char EVENT_MUSIC_OPEN = 0xe0;
+        static const char EVENT_MUSIC_PLAY = 0x00;
+        static const char EVENT_MUSIC_PAUSE = 0x01;
+        static const char EVENT_MUSIC_NEXT = 0x03;
+        static const char EVENT_MUSIC_PREV = 0x04;
+        static const char EVENT_MUSIC_VOLUP = 0x05;
+        static const char EVENT_MUSIC_VOLDOWN = 0x06;
+        static const char STATUS_MUSIC_PAUSED = 0x00;
+        static const char STATUS_MUSIC_PLAYING = 0x01;
+
+      private:
+        static constexpr uint8_t msId[2] = {0x00, 0x01};
+        static constexpr uint8_t msEventCharId[2] = {0x00, 0x02};
+        static constexpr uint8_t msStatusCharId[2] = {0x00, 0x03};
+        static constexpr uint8_t msArtistCharId[2] = {0x00, 0x04};
+        static constexpr uint8_t msTrackCharId[2] = {0x00, 0x05};
+        static constexpr uint8_t msAlbumCharId[2] = {0x00, 0x06};
+
+        ble_uuid128_t msUuid {
+                .u = { .type = BLE_UUID_TYPE_128 },
+                .value = MUSIC_SERVICE_UUID_BASE
+        };
+
+        ble_uuid128_t msEventCharUuid {
+                .u = { .type = BLE_UUID_TYPE_128 },
+                .value = MUSIC_SERVICE_UUID_BASE
+        };
+        ble_uuid128_t msStatusCharUuid {
+                .u = { .type = BLE_UUID_TYPE_128 },
+                .value = MUSIC_SERVICE_UUID_BASE
+        };
+        ble_uuid128_t msArtistCharUuid {
+                .u = { .type = BLE_UUID_TYPE_128 },
+                .value = MUSIC_SERVICE_UUID_BASE
+        };
+        ble_uuid128_t msTrackCharUuid {
+                .u = { .type = BLE_UUID_TYPE_128 },
+                .value = MUSIC_SERVICE_UUID_BASE
+        };
+        ble_uuid128_t msAlbumCharUuid {
+                .u = { .type = BLE_UUID_TYPE_128 },
+                .value = MUSIC_SERVICE_UUID_BASE
+        };
+
+        struct ble_gatt_chr_def characteristicDefinition[6];
+        struct ble_gatt_svc_def serviceDefinition[2];
+
+        uint16_t m_eventHandle;
+
+        std::string m_artist;
+        std::string m_album;
+        std::string m_track;
+
+        unsigned char m_status;
+
+        Pinetime::System::SystemTask& m_system;
+
+    };
+  }
+}
+




diff --git a/src/components/Ble/NimbleController.cpp b/src/components/Ble/NimbleController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b13f9ce3bf7888df78e000a0653bd29afaeac5ad
--- /dev/null
+++ b/src/components/Ble/NimbleController.cpp
@@ -0,0 +1,337 @@
+
+#include <Components/DateTime/DateTimeController.h>
+
+#include <SystemTask/SystemTask.h>
+#include <Components/Ble/NotificationManager.h>
+#include <hal/nrf_rtc.h>
+
+#include "NimbleController.h"
+#include "MusicService.h"
+#include <services/gatt/ble_svc_gatt.h>
+#include <services/gap/ble_svc_gap.h>
+#include <host/util/util.h>
+#include <host/ble_hs_id.h>
+#include <host/ble_hs.h>
+#include <host/ble_gap.h>
+
+
+
+using namespace Pinetime::Controllers;
+
+// TODO I'm not satisfied by how this code looks like (AlertNotificationClient and CurrentTimeClient must
+// expose too much data, too many callbacks -> NimbleController -> CTS/ANS client.
+// Let's try to improve this code (and keep it working!)
+
+NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
+                                   Pinetime::Controllers::Ble& bleController,
+        DateTime& dateTimeController,
+        Pinetime::Controllers::NotificationManager& notificationManager,
+        Controllers::Battery& batteryController,
+        Pinetime::Drivers::SpiNorFlash& spiNorFlash) :
+        systemTask{systemTask},
+        bleController{bleController},
+        dateTimeController{dateTimeController},
+        notificationManager{notificationManager},
+        spiNorFlash{spiNorFlash},
+        dfuService{systemTask, bleController, spiNorFlash},
+        currentTimeClient{dateTimeController},
+        anService{systemTask, notificationManager},
+        alertNotificationClient{systemTask, notificationManager},
+        currentTimeService{dateTimeController},
+        musicService{systemTask},
+        batteryInformationService{batteryController},
+        immediateAlertService{systemTask, notificationManager} {
+
+}
+
+int GAPEventCallback(struct ble_gap_event *event, void *arg) {
+  auto nimbleController = static_cast<NimbleController*>(arg);
+  return nimbleController->OnGAPEvent(event);
+}
+
+int CurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
+                                                const struct ble_gatt_chr *chr, void *arg) {
+  auto client = static_cast<NimbleController*>(arg);
+  return client->OnCTSCharacteristicDiscoveryEvent(conn_handle, error, chr);
+}
+
+int AlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
+                                                const struct ble_gatt_chr *chr, void *arg) {
+  auto client = static_cast<NimbleController*>(arg);
+  return client->OnANSCharacteristicDiscoveryEvent(conn_handle, error, chr);
+}
+
+int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
+                                   struct ble_gatt_attr *attr, void *arg) {
+  auto client = static_cast<NimbleController*>(arg);
+  return client->OnCurrentTimeReadResult(conn_handle, error, attr);
+}
+
+int AlertNotificationDescriptorDiscoveryEventCallback(uint16_t conn_handle,
+                                                                             const struct ble_gatt_error *error,
+                                                                             uint16_t chr_val_handle,
+                                                                             const struct ble_gatt_dsc *dsc,
+                                                                             void *arg) {
+  auto client = static_cast<NimbleController*>(arg);
+  return client->OnANSDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc);
+}
+
+void NimbleController::Init() {
+  while (!ble_hs_synced()) {}
+
+  ble_svc_gap_init();
+  ble_svc_gatt_init();
+
+  deviceInformationService.Init();
+  currentTimeClient.Init();
+  currentTimeService.Init();
+  musicService.Init();
+  anService.Init();
+  dfuService.Init();
+  batteryInformationService.Init();
+  immediateAlertService.Init();
+  int res;
+  res = ble_hs_util_ensure_addr(0);
+  ASSERT(res == 0);
+  res = ble_hs_id_infer_auto(0, &addrType);
+  ASSERT(res == 0);
+  res = ble_svc_gap_device_name_set(deviceName);
+  ASSERT(res == 0);
+  Pinetime::Controllers::Ble::BleAddress address;
+  res = ble_hs_id_copy_addr(addrType, address.data(), nullptr);
+  ASSERT(res == 0);
+  bleController.AddressType((addrType == 0) ? Ble::AddressTypes::Public : Ble::AddressTypes::Random);
+  bleController.Address(std::move(address));
+
+  res = ble_gatts_start();
+  ASSERT(res == 0);
+}
+
+void NimbleController::StartAdvertising() {
+  if(ble_gap_adv_active()) return;
+
+  ble_svc_gap_device_name_set(deviceName);
+
+  /* set adv parameters */
+  struct ble_gap_adv_params adv_params;
+  struct ble_hs_adv_fields fields;
+  /* advertising payload is split into advertising data and advertising
+     response, because all data cannot fit into single packet; name of device
+     is sent as response to scan request */
+  struct ble_hs_adv_fields rsp_fields;
+
+  /* fill all fields and parameters with zeros */
+  memset(&adv_params, 0, sizeof(adv_params));
+  memset(&fields, 0, sizeof(fields));
+  memset(&rsp_fields, 0, sizeof(rsp_fields));
+
+  adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
+  adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+
+  fields.flags = BLE_HS_ADV_F_DISC_GEN |
+                 BLE_HS_ADV_F_BREDR_UNSUP;
+//  fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE(
+//          0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+//          0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
+  fields.uuids128 = &dfuServiceUuid;
+  fields.num_uuids128 = 1;
+  fields.uuids128_is_complete = 1;
+  fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
+
+  rsp_fields.name = (uint8_t *)deviceName;
+  rsp_fields.name_len = strlen(deviceName);
+  rsp_fields.name_is_complete = 1;
+
+  ble_gap_adv_set_fields(&fields);
+//  ASSERT(res == 0); // TODO this one sometimes fails with error 22 (notsync)
+
+  ble_gap_adv_rsp_set_fields(&rsp_fields);
+//  ASSERT(res == 0);
+
+  ble_gap_adv_start(addrType, NULL, 180000,
+                          &adv_params, GAPEventCallback, this);
+//  ASSERT(res == 0);// TODO I've disabled these ASSERT as they sometime asserts and reset the mcu.
+  // For now, the advertising is restarted as soon as it ends. There may be a race condition
+  // that prevent the advertising from restarting reliably.
+  // I remove the assert to prevent this uncesseray crash, but in the long term, the management of
+  // the advertising should be improve (better error handling, and advertise for 3 minutes after
+  // the application has been woken up, for example.
+}
+
+int OnAllSvrDisco(uint16_t conn_handle,
+                                 const struct ble_gatt_error *error,
+                                 const struct ble_gatt_svc *service,
+                                 void *arg) {
+  auto nimbleController = static_cast<NimbleController*>(arg);
+  return nimbleController->OnDiscoveryEvent(conn_handle, error, service);
+  return 0;
+}
+
+int NimbleController::OnGAPEvent(ble_gap_event *event) {
+  switch (event->type) {
+    case BLE_GAP_EVENT_ADV_COMPLETE:
+      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE");
+      NRF_LOG_INFO("advertise complete; reason=%dn status=%d", event->adv_complete.reason, event->connect.status);
+      break;
+    case BLE_GAP_EVENT_CONNECT: {
+      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONNECT");
+
+      /* A new connection was established or a connection attempt failed. */
+      NRF_LOG_INFO("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed",
+                   event->connect.status);
+
+      if (event->connect.status != 0) {
+        /* Connection failed; resume advertising. */
+        StartAdvertising();
+        bleController.Disconnect();
+      } else {
+        bleController.Connect();
+        systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleConnected);
+        connectionHandle = event->connect.conn_handle;
+        // Service discovery is deffered via systemtask
+      }
+    }
+      break;
+    case BLE_GAP_EVENT_DISCONNECT:
+      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_DISCONNECT");
+      NRF_LOG_INFO("disconnect; reason=%d", event->disconnect.reason);
+
+      /* Connection terminated; resume advertising. */
+      connectionHandle = BLE_HS_CONN_HANDLE_NONE;
+      bleController.Disconnect();
+      StartAdvertising();
+      break;
+    case BLE_GAP_EVENT_CONN_UPDATE:
+      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONN_UPDATE");
+      /* The central has updated the connection parameters. */
+      NRF_LOG_INFO("connection updated; status=%d ", event->conn_update.status);
+      break;
+    case BLE_GAP_EVENT_ENC_CHANGE:
+      /* Encryption has been enabled or disabled for this connection. */
+      NRF_LOG_INFO("encryption change event; status=%d ", event->enc_change.status);
+      return 0;
+    case BLE_GAP_EVENT_SUBSCRIBE:
+      NRF_LOG_INFO("subscribe event; conn_handle=%d attr_handle=%d "
+                        "reason=%d prevn=%d curn=%d previ=%d curi=???\n",
+                  event->subscribe.conn_handle,
+                  event->subscribe.attr_handle,
+                  event->subscribe.reason,
+                  event->subscribe.prev_notify,
+                  event->subscribe.cur_notify,
+                  event->subscribe.prev_indicate);
+      return 0;
+    case BLE_GAP_EVENT_MTU:
+      NRF_LOG_INFO("mtu update event; conn_handle=%d cid=%d mtu=%d\n",
+                  event->mtu.conn_handle,
+                  event->mtu.channel_id,
+                  event->mtu.value);
+      return 0;
+
+    case BLE_GAP_EVENT_REPEAT_PAIRING: {
+      /* We already have a bond with the peer, but it is attempting to
+       * establish a new secure link.  This app sacrifices security for
+       * convenience: just throw away the old bond and accept the new link.
+       */
+
+      /* Delete the old bond. */
+      struct ble_gap_conn_desc desc;
+      ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
+      ble_store_util_delete_peer(&desc.peer_id_addr);
+
+      /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
+       * continue with the pairing operation.
+       */
+    }
+      return BLE_GAP_REPEAT_PAIRING_RETRY;
+
+    case BLE_GAP_EVENT_NOTIFY_RX: {
+      /* Peer sent us a notification or indication. */
+      size_t notifSize = OS_MBUF_PKTLEN(event->notify_rx.om);
+
+      NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d "
+                   "attr_len=%d",
+                   event->notify_rx.indication ?
+                   "indication" :
+                   "notification",
+                   event->notify_rx.conn_handle,
+                   event->notify_rx.attr_handle,
+                   notifSize);
+
+      alertNotificationClient.OnNotification(event);
+      return 0;
+    }
+      /* Attribute data is contained in event->notify_rx.attr_data. */
+
+    default:
+//      NRF_LOG_INFO("Advertising event : %d", event->type);
+      break;
+  }
+  return 0;
+}
+
+int NimbleController::OnDiscoveryEvent(uint16_t i, const ble_gatt_error *error, const ble_gatt_svc *service) {
+  if(service == nullptr && error->status == BLE_HS_EDONE) {
+    NRF_LOG_INFO("Service Discovery complete");
+    if(currentTimeClient.IsDiscovered()) {
+      ble_gattc_disc_all_chrs(connectionHandle, currentTimeClient.StartHandle(), currentTimeClient.EndHandle(),
+                              CurrentTimeCharacteristicDiscoveredCallback, this);
+
+    } else if(alertNotificationClient.IsDiscovered()) {
+      ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(), alertNotificationClient.EndHandle(),
+                              AlertNotificationCharacteristicDiscoveredCallback, this);
+    }
+  }
+
+  alertNotificationClient.OnDiscoveryEvent(i, error, service);
+  currentTimeClient.OnDiscoveryEvent(i, error, service);
+  return 0;
+}
+
+int NimbleController::OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
+                                                        const ble_gatt_chr *characteristic) {
+  if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
+    NRF_LOG_INFO("CTS characteristic Discovery complete");
+    ble_gattc_read(connectionHandle, currentTimeClient.CurrentTimeHandle(), CurrentTimeReadCallback, this);
+    return 0;
+  }
+  return currentTimeClient.OnCharacteristicDiscoveryEvent(connectionHandle, error, characteristic);
+}
+
+int NimbleController::OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
+                                                        const ble_gatt_chr *characteristic) {
+  if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
+    NRF_LOG_INFO("ANS characteristic Discovery complete");
+    ble_gattc_disc_all_dscs(connectionHandle,
+            alertNotificationClient.NewAlerthandle(), alertNotificationClient.EndHandle(),
+            AlertNotificationDescriptorDiscoveryEventCallback, this);
+    return 0;
+  }
+  return alertNotificationClient.OnCharacteristicsDiscoveryEvent(connectionHandle, error, characteristic);
+}
+
+int NimbleController::OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute) {
+  currentTimeClient.OnCurrentTimeReadResult(connectionHandle, error, attribute);
+
+  if (alertNotificationClient.IsDiscovered()) {
+    ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(),
+                            alertNotificationClient.EndHandle(),
+                            AlertNotificationCharacteristicDiscoveredCallback, this);
+  }
+  return 0;
+}
+
+int NimbleController::OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
+                                                            uint16_t characteristicValueHandle,
+                                                            const ble_gatt_dsc *descriptor) {
+  return alertNotificationClient.OnDescriptorDiscoveryEventCallback(connectionHandle, error, characteristicValueHandle, descriptor);
+}
+
+void NimbleController::StartDiscovery() {
+  ble_gattc_disc_all_svcs(connectionHandle, OnAllSvrDisco, this);
+}
+
+
+uint16_t NimbleController::connHandle() {
+    return connectionHandle;
+}
+




diff --git a/src/components/Ble/NimbleController.h b/src/components/Ble/NimbleController.h
new file mode 100644
index 0000000000000000000000000000000000000000..89fa4250e692fa387dd7e1e615e369c09883213a
--- /dev/null
+++ b/src/components/Ble/NimbleController.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include <cstdint>
+#include "AlertNotificationService.h"
+#include "AlertNotificationClient.h"
+#include "DeviceInformationService.h"
+#include "CurrentTimeClient.h"
+#include "DfuService.h"
+#include "CurrentTimeService.h"
+#include "MusicService.h"
+#include "BatteryInformationService.h"
+#include "ImmediateAlertService.h"
+#include <host/ble_gap.h>
+
+namespace Pinetime {
+  namespace Drivers {
+    class SpiNorFlash;
+  }
+  namespace Controllers {
+    class DateTime;
+
+    class NimbleController {
+
+      public:
+        NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController,
+                DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager,
+                Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash);
+        void Init();
+        void StartAdvertising();
+        int OnGAPEvent(ble_gap_event *event);
+
+        int OnDiscoveryEvent(uint16_t i, const ble_gatt_error *pError, const ble_gatt_svc *pSvc);
+        int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
+                                              const ble_gatt_chr *characteristic);
+        int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
+                                              const ble_gatt_chr *characteristic);
+        int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute);
+        int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
+                                                  uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
+
+        void StartDiscovery();
+
+        Pinetime::Controllers::MusicService& music() {return musicService;};
+
+        uint16_t connHandle();
+
+      private:
+        static constexpr const char* deviceName = "InfiniTime";
+        Pinetime::System::SystemTask& systemTask;
+        Pinetime::Controllers::Ble& bleController;
+        DateTime& dateTimeController;
+        Pinetime::Controllers::NotificationManager& notificationManager;
+        Pinetime::Drivers::SpiNorFlash& spiNorFlash;
+        Pinetime::Controllers::DfuService dfuService;
+
+        DeviceInformationService deviceInformationService;
+        CurrentTimeClient currentTimeClient;
+        AlertNotificationService anService;
+        AlertNotificationClient alertNotificationClient;
+        CurrentTimeService currentTimeService;
+        MusicService musicService;
+        BatteryInformationService batteryInformationService;
+        ImmediateAlertService immediateAlertService;
+
+        uint8_t addrType; // 1 = Random, 0 = PUBLIC
+        uint16_t connectionHandle = 0;
+
+        ble_uuid128_t dfuServiceUuid {
+                .u { .type = BLE_UUID_TYPE_128},
+                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
+                          0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}
+        };
+    };
+  }
+}




diff --git a/src/components/Ble/NotificationManager.cpp b/src/components/Ble/NotificationManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0aea0697358482a966e6c158e525751c9c4bc156
--- /dev/null
+++ b/src/components/Ble/NotificationManager.cpp
@@ -0,0 +1,30 @@
+#include <cstring>
+#include "NotificationManager.h"
+
+using namespace Pinetime::Controllers;
+
+void NotificationManager::Push(Pinetime::Controllers::NotificationManager::Categories category,
+                                                      const char *message, uint8_t currentMessageSize) {
+  // TODO handle edge cases on read/write index
+  auto checkedSize = std::min(currentMessageSize, uint8_t{18});
+  auto& notif = notifications[writeIndex];
+  std::memcpy(notif.message.data(), message, checkedSize);
+  notif.message[checkedSize] = '\0';
+  notif.category = category;
+
+  writeIndex = (writeIndex + 1 < TotalNbNotifications) ? writeIndex + 1 : 0;
+  if(!empty && writeIndex == readIndex)
+    readIndex = writeIndex + 1;
+}
+
+NotificationManager::Notification Pinetime::Controllers::NotificationManager::Pop() {
+// TODO handle edge cases on read/write index
+  NotificationManager::Notification notification = notifications[readIndex];
+
+  if(readIndex != writeIndex) {
+    readIndex = (readIndex + 1 < TotalNbNotifications) ? readIndex + 1 : 0;
+  }
+
+  // TODO Check move optimization on return
+  return notification;
+}




diff --git a/src/components/Ble/NotificationManager.h b/src/components/Ble/NotificationManager.h
new file mode 100644
index 0000000000000000000000000000000000000000..daa1571b3d6feeafcc80287854d265b9ab9b5c4b
--- /dev/null
+++ b/src/components/Ble/NotificationManager.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <array>
+
+namespace Pinetime {
+  namespace Controllers {
+    class NotificationManager {
+      public:
+        enum class Categories {Unknown, SimpleAlert, Email, News, IncomingCall, MissedCall, Sms, VoiceMail, Schedule, HighProriotyAlert, InstantMessage };
+        static constexpr uint8_t MessageSize{18};
+
+        struct Notification {
+          std::array<char, MessageSize+1> message;
+          Categories category = Categories::Unknown;
+        };
+
+      void Push(Categories category, const char* message, uint8_t messageSize);
+      Notification Pop();
+
+
+      private:
+        static constexpr uint8_t TotalNbNotifications = 5;
+        std::array<Notification, TotalNbNotifications> notifications;
+        uint8_t readIndex = 0;
+        uint8_t writeIndex = 0;
+        bool empty = true;
+    };
+  }
+}
\ No newline at end of file




diff --git a/src/components/Brightness/BrightnessController.cpp b/src/components/Brightness/BrightnessController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c8825d6807a722d7bd401ac86af2457910911da5
--- /dev/null
+++ b/src/components/Brightness/BrightnessController.cpp
@@ -0,0 +1,70 @@
+#include <hal/nrf_gpio.h>
+#include "BrightnessController.h"
+
+using namespace Pinetime::Controllers;
+
+
+void BrightnessController::Init() {
+  nrf_gpio_cfg_output(pinLcdBacklight1);
+  nrf_gpio_cfg_output(pinLcdBacklight2);
+  nrf_gpio_cfg_output(pinLcdBacklight3);
+  Set(level);
+}
+
+void BrightnessController::Set(BrightnessController::Levels level) {
+  this->level = level;
+  switch(level) {
+    default:
+    case Levels::High:
+      nrf_gpio_pin_clear(pinLcdBacklight1);
+      nrf_gpio_pin_clear(pinLcdBacklight2);
+      nrf_gpio_pin_clear(pinLcdBacklight3);
+      break;
+    case Levels::Medium:
+      nrf_gpio_pin_clear(pinLcdBacklight1);
+      nrf_gpio_pin_clear(pinLcdBacklight2);
+      nrf_gpio_pin_set(pinLcdBacklight3);
+      break;
+    case Levels::Low:
+      nrf_gpio_pin_clear(pinLcdBacklight1);
+      nrf_gpio_pin_set(pinLcdBacklight2);
+      nrf_gpio_pin_set(pinLcdBacklight3);
+      break;
+    case Levels::Off:
+      nrf_gpio_pin_set(pinLcdBacklight1);
+      nrf_gpio_pin_set(pinLcdBacklight2);
+      nrf_gpio_pin_set(pinLcdBacklight3);
+      break;
+  }
+}
+
+void BrightnessController::Lower() {
+  switch(level) {
+    case Levels::High: Set(Levels::Medium); break;
+    case Levels::Medium: Set(Levels::Low); break;
+    case Levels::Low: Set(Levels::Off); break;
+    default: break;
+  }
+}
+
+void BrightnessController::Higher() {
+  switch(level) {
+    case Levels::Off: Set(Levels::Low); break;
+    case Levels::Low: Set(Levels::Medium); break;
+    case Levels::Medium: Set(Levels::High); break;
+    default: break;
+  }
+}
+
+BrightnessController::Levels BrightnessController::Level() const {
+  return level;
+}
+
+void BrightnessController::Backup() {
+  backupLevel = level;
+}
+
+void BrightnessController::Restore() {
+  Set(backupLevel);
+}
+




diff --git a/src/components/Brightness/BrightnessController.h b/src/components/Brightness/BrightnessController.h
new file mode 100644
index 0000000000000000000000000000000000000000..b8354ec0538b58e70befe0a16758cdf456d530d8
--- /dev/null
+++ b/src/components/Brightness/BrightnessController.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <cstdint>
+
+namespace Pinetime {
+  namespace Controllers {
+    class BrightnessController {
+    public:
+      enum class Levels {Off, Low, Medium, High};
+      void Init();
+
+      void Set(Levels level);
+      Levels Level() const;
+      void Lower();
+      void Higher();
+
+      void Backup();
+      void Restore();
+
+    private:
+      static constexpr uint8_t pinLcdBacklight1 = 14;
+      static constexpr uint8_t pinLcdBacklight2 = 22;
+      static constexpr uint8_t pinLcdBacklight3 = 23;
+      Levels level = Levels::High;
+      Levels backupLevel = Levels::High;
+    };
+  }
+}




diff --git a/src/components/DateTime/DateTimeController.cpp b/src/components/DateTime/DateTimeController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..30d9c13f2587a4d2a9a5a7f4196189b2932d5256
--- /dev/null
+++ b/src/components/DateTime/DateTimeController.cpp
@@ -0,0 +1,66 @@
+#include "DateTimeController.h"
+#include <date/date.h>
+#include <libraries/log/nrf_log.h>
+
+using namespace Pinetime::Controllers;
+
+
+void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute,
+                       uint8_t second, uint32_t systickCounter) {
+  std::tm tm = { /* .tm_sec  = */ second,
+          /* .tm_min  = */ minute,
+          /* .tm_hour = */ hour,
+          /* .tm_mday = */ day,
+          /* .tm_mon  = */ month - 1,
+          /* .tm_year = */ year - 1900,
+  };
+  tm.tm_isdst = -1; // Use DST value from local time zone
+  currentDateTime =  std::chrono::system_clock::from_time_t(std::mktime(&tm));
+
+  NRF_LOG_INFO("%d %d %d ", day, month, year);
+  NRF_LOG_INFO("%d %d %d ", hour, minute, second);
+  previousSystickCounter = systickCounter;
+
+  UpdateTime(systickCounter);
+  NRF_LOG_INFO("* %d %d %d ", this->hour, this->minute, this->second);
+  NRF_LOG_INFO("* %d %d %d ", this->day, this->month, this->year);
+}
+
+void DateTime::UpdateTime(uint32_t systickCounter) {
+  // Handle systick counter overflow
+  uint32_t systickDelta = 0;
+  if(systickCounter < previousSystickCounter) {
+    systickDelta = 0xffffff - previousSystickCounter;
+    systickDelta += systickCounter + 1;
+  } else {
+    systickDelta = systickCounter - previousSystickCounter;
+  }
+
+  /*
+ * 1000 ms = 1024 ticks
+ */
+  auto correctedDelta = systickDelta / 1024;
+  auto rest = (systickDelta - (correctedDelta*1024));
+  if(systickCounter >= rest) {
+    previousSystickCounter = systickCounter - rest;
+  } else {
+    previousSystickCounter = 0xffffff - (rest - systickCounter);
+  }
+
+  currentDateTime += std::chrono::seconds(correctedDelta);
+  uptime += std::chrono::seconds(correctedDelta);
+
+  auto dp = date::floor<date::days>(currentDateTime);
+  auto time = date::make_time(currentDateTime-dp);
+  auto yearMonthDay = date::year_month_day(dp);
+
+  year = (int)yearMonthDay.year();
+  month = static_cast<Months>((unsigned)yearMonthDay.month());
+  day = (unsigned)yearMonthDay.day();
+  dayOfWeek = static_cast<Days>(date::weekday(yearMonthDay).iso_encoding());
+
+  hour = time.hours().count();
+  minute = time.minutes().count();
+  second = time.seconds().count();
+}
+




diff --git a/src/components/DateTime/DateTimeController.h b/src/components/DateTime/DateTimeController.h
new file mode 100644
index 0000000000000000000000000000000000000000..d6020745609e1ad6b5281b8f34c9397046fe1294
--- /dev/null
+++ b/src/components/DateTime/DateTimeController.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <cstdint>
+#include <chrono>
+
+namespace Pinetime {
+  namespace Controllers {
+    class DateTime {
+      public:
+        enum class Days : uint8_t {Unknown, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
+        enum class Months : uint8_t {Unknown, January, February, March, April, May, June, July, August, September, October, November, December};
+
+        void SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter);
+        void UpdateTime(uint32_t systickCounter);
+        uint16_t Year() const { return year; }
+        Months Month() const { return month; }
+        uint8_t Day() const { return day; }
+        Days DayOfWeek() const { return dayOfWeek; }
+        uint8_t Hours() const { return hour; }
+        uint8_t Minutes() const { return minute; }
+        uint8_t Seconds() const { return second; }
+
+        std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const { return currentDateTime; }
+        std::chrono::seconds Uptime() const { return uptime; }
+      private:
+        uint16_t year = 0;
+        Months month = Months::Unknown;
+        uint8_t day = 0;
+        Days dayOfWeek = Days::Unknown;
+        uint8_t hour = 0;
+        uint8_t minute = 0;
+        uint8_t second = 0;
+
+        uint32_t previousSystickCounter = 0;
+        std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
+        std::chrono::seconds uptime {0};
+    };
+  }
+}
\ No newline at end of file




diff --git a/src/components/FirmwareValidator/FirmwareValidator.cpp b/src/components/FirmwareValidator/FirmwareValidator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..244d5c06bf704afdc9727e02a5248a82c4e381f9
--- /dev/null
+++ b/src/components/FirmwareValidator/FirmwareValidator.cpp
@@ -0,0 +1,20 @@
+#include <drivers/InternalFlash.h>
+#include <hal/nrf_rtc.h>
+
+#include "FirmwareValidator.h"
+
+using namespace Pinetime::Controllers;
+
+bool FirmwareValidator::IsValidated() const {
+  auto* imageOkPtr = reinterpret_cast<uint32_t *>(validBitAdress);
+  return (*imageOkPtr) == validBitValue;
+}
+
+void FirmwareValidator::Validate() {
+  if(!IsValidated())
+    Pinetime::Drivers::InternalFlash::WriteWord(validBitAdress, validBitValue);
+}
+
+void FirmwareValidator::Reset() {
+  NVIC_SystemReset();
+}




diff --git a/src/components/FirmwareValidator/FirmwareValidator.h b/src/components/FirmwareValidator/FirmwareValidator.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa576d8872157563227a112beab9401450a9444f
--- /dev/null
+++ b/src/components/FirmwareValidator/FirmwareValidator.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <cstdint>
+
+namespace Pinetime {
+  namespace Controllers {
+    class FirmwareValidator {
+      public:
+        void Validate();
+        bool IsValidated() const;
+
+        void Reset();
+      private:
+        static constexpr uint32_t validBitAdress {0x7BFE8};
+        static constexpr uint32_t validBitValue {1};
+    };
+  }
+}




diff --git a/src/components/Gfx/Gfx.cpp b/src/components/Gfx/Gfx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3c5dbfb7857279eac1ab21fe6c3c25719dd1b50b
--- /dev/null
+++ b/src/components/Gfx/Gfx.cpp
@@ -0,0 +1,207 @@
+#include <libraries/svc/nrf_svci.h>
+#include <FreeRTOS.h>
+#include <task.h>
+#include "Gfx.h"
+#include "../../drivers/St7789.h"
+using namespace Pinetime::Components;
+
+Gfx::Gfx(Pinetime::Drivers::St7789 &lcd) : lcd{lcd} {
+}
+
+void Gfx::Init() {
+
+}
+
+void Gfx::ClearScreen() {
+  SetBackgroundColor(0x0000);
+
+  state.remainingIterations = 240 + 1;
+  state.currentIteration = 0;
+  state.busy = true;
+  state.action = Action::FillRectangle;
+  state.taskToNotify = xTaskGetCurrentTaskHandle();
+
+  lcd.BeginDrawBuffer(0, 0, width, height);
+  lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
+  WaitTransfertFinished();
+
+}
+
+void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
+  SetBackgroundColor(color);
+
+  state.remainingIterations = h;
+  state.currentIteration = 0;
+  state.busy = true;
+  state.action = Action::FillRectangle;
+  state.color = color;
+  state.taskToNotify = xTaskGetCurrentTaskHandle();
+
+  lcd.BeginDrawBuffer(x, y, w, h);
+  lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
+
+  WaitTransfertFinished();
+}
+
+void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b) {
+  state.remainingIterations = h;
+  state.currentIteration = 0;
+  state.busy = true;
+  state.action = Action::FillRectangle;
+  state.color = 0x00;
+  state.taskToNotify = xTaskGetCurrentTaskHandle();
+
+  lcd.BeginDrawBuffer(x, y, w, h);
+  lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(b), width * 2);
+
+  WaitTransfertFinished();
+}
+
+void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, const FONT_INFO *p_font, bool wrap) {
+  if (y > (height - p_font->height)) {
+    // Not enough space to write even single char.
+    return;
+  }
+
+  uint8_t current_x = x;
+  uint8_t current_y = y;
+
+  for (size_t i = 0; text[i] != '\0'; i++) {
+    if (text[i] == '\n') {
+      current_x = x;
+      current_y += p_font->height + p_font->height / 10;
+    } else {
+      DrawChar(p_font, (uint8_t) text[i], &current_x, current_y, color);
+    }
+
+    uint8_t char_idx = text[i] - p_font->startChar;
+    uint16_t char_width = text[i] == ' ' ? (p_font->height / 2) : p_font->charInfo[char_idx].widthBits;
+
+    if (current_x > (width - char_width)) {
+      if (wrap) {
+        current_x = x;
+        current_y += p_font->height + p_font->height / 10;
+      } else {
+        break;
+      }
+
+      if (y > (height - p_font->height)) {
+        break;
+      }
+    }
+  }
+}
+
+void Gfx::DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color) {
+  uint8_t char_idx = c - font->startChar;
+  uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8);
+  uint16_t bg = 0x0000;
+
+  if (c == ' ') {
+    *x += font->height / 2;
+    return;
+  }
+
+  // Build first line
+  for (uint16_t j = 0; j < bytes_in_line; j++) {
+    for (uint8_t k = 0; k < 8; k++) {
+      if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + j]) {
+        buffer[(j*8)+k] = color;
+      }
+      else {
+        buffer[(j*8)+k] = bg;
+      }
+    }
+  }
+
+  state.remainingIterations = font->height + 0;
+  state.currentIteration = 0;
+  state.busy = true;
+  state.action = Action::DrawChar;
+  state.font = const_cast<FONT_INFO *>(font);
+  state.character = c;
+  state.color = color;
+  state.taskToNotify = xTaskGetCurrentTaskHandle();
+
+  lcd.BeginDrawBuffer(*x, y, bytes_in_line*8, font->height);
+  lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(&buffer), bytes_in_line*8*2);
+  WaitTransfertFinished();
+
+  *x += font->charInfo[char_idx].widthBits + font->spacePixels;
+}
+
+void Gfx::pixel_draw(uint8_t x, uint8_t y, uint16_t color) {
+  lcd.DrawPixel(x, y, color);
+}
+
+void Gfx::Sleep() {
+  lcd.Sleep();
+}
+
+void Gfx::Wakeup() {
+  lcd.Wakeup();
+}
+
+void Gfx::SetBackgroundColor(uint16_t color) {
+  for(int i = 0; i < width; i++) {
+    buffer[i] = color;
+  }
+}
+
+bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) {
+  if(!state.busy) return false;
+  state.remainingIterations--;
+  if (state.remainingIterations == 0) {
+    state.busy = false;
+    NotifyEndOfTransfert(state.taskToNotify);
+    return false;
+  }
+
+  if(state.action == Action::FillRectangle) {
+    *data = reinterpret_cast<uint8_t *>(buffer);
+    size = width * 2;
+  } else if(state.action == Action::DrawChar) {
+    uint16_t bg = 0x0000;
+    uint8_t char_idx = state.character - state.font->startChar;
+    uint16_t bytes_in_line = CEIL_DIV(state.font->charInfo[char_idx].widthBits, 8);
+
+    for (uint16_t j = 0; j < bytes_in_line; j++) {
+      for (uint8_t k = 0; k < 8; k++) {
+        if ((1 << (7 - k)) & state.font->data[state.font->charInfo[char_idx].offset + ((state.currentIteration+1) * bytes_in_line) + j]) {
+          buffer[(j*8)+k] = state.color;
+        }
+        else {
+          buffer[(j*8)+k] = bg;
+        }
+      }
+    }
+
+    *data = reinterpret_cast<uint8_t *>(buffer);
+    size = bytes_in_line*8*2;
+  }
+
+  state.currentIteration++;
+
+  return true;
+}
+
+void Gfx::NotifyEndOfTransfert(TaskHandle_t task) {
+  if(task != nullptr) {
+    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+    vTaskNotifyGiveFromISR(task, &xHigherPriorityTaskWoken);
+    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+  }
+}
+
+void Gfx::WaitTransfertFinished() const {
+  ulTaskNotifyTake(pdTRUE, 500);
+}
+
+void Gfx::SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines) {
+  lcd.VerticalScrollDefinition(topFixedLines, scrollLines, bottomFixedLines);
+}
+
+void Gfx::SetScrollStartLine(uint16_t line) {
+  lcd.VerticalScrollStartAddress(line);
+}
+




diff --git a/src/components/Gfx/Gfx.h b/src/components/Gfx/Gfx.h
new file mode 100644
index 0000000000000000000000000000000000000000..091f06f5604c5febddf3637a8c7307b904b43147
--- /dev/null
+++ b/src/components/Gfx/Gfx.h
@@ -0,0 +1,60 @@
+#pragma once
+#include <cstdint>
+#include <nrf_font.h>
+#include <drivers/BufferProvider.h>
+#include <FreeRTOS.h>
+#include <task.h>
+
+
+namespace Pinetime {
+  namespace Drivers {
+    class St7789;
+  }
+  namespace Components {
+    class Gfx : public Pinetime::Drivers::BufferProvider {
+      public:
+        explicit Gfx(Drivers::St7789& lcd);
+        void Init();
+        void ClearScreen();
+        void DrawString(uint8_t x, uint8_t y, uint16_t color, const char* text, const FONT_INFO *p_font, bool wrap);
+        void DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color);
+        void FillRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t color);
+        void FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b);
+        void SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
+        void SetScrollStartLine(uint16_t line);
+
+
+        void Sleep();
+        void Wakeup();
+        bool GetNextBuffer(uint8_t **buffer, size_t &size) override;
+        void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
+
+
+      private:
+        static constexpr uint8_t width = 240;
+        static constexpr uint8_t height = 240;
+
+        enum class Action { None, FillRectangle, DrawChar};
+        struct State {
+          State() : busy{false}, action{Action::None}, remainingIterations{0}, currentIteration{0} {}
+          volatile bool busy;
+          volatile Action action;
+          volatile uint16_t remainingIterations;
+          volatile uint16_t currentIteration;
+          volatile FONT_INFO *font;
+          volatile uint16_t color;
+          volatile uint8_t character;
+          volatile TaskHandle_t taskToNotify = nullptr;
+        };
+
+        volatile State state;
+
+        uint16_t buffer[width]; // 1 line buffer
+        Drivers::St7789& lcd;
+
+        void SetBackgroundColor(uint16_t color);
+        void WaitTransfertFinished() const;
+        void NotifyEndOfTransfert(TaskHandle_t task);
+    };
+  }
+}