(development-workflow)= # Development Workflow This document outlines the complete development lifecycle for UNA Watch applications. ## Workflow Stages ### 1. Planning Before writing code, decide on your app type: - **Activity**: For long-running, interactive apps. - **Utility**: For tools like calculators or settings. - **Glance**: For notification-based widgets. - **Clockface**: For time-keeping displays. ### 2. Development Use the SDK interfaces to build your app logic. - Implement the **Service process** for background tasks. - Implement the **GUI process** for user interaction. - Use **IPC messages** to communicate between them. ### 3. Building Compile your app into an ELF binary using the provided toolchain. ```bash make release ``` Ensure you are using the correct optimization flags for PIC execution. ### 4. Testing - **Simulator**: Test UI and basic logic on your development machine. - **Unit Tests**: Write tests for individual components. - **Integration Tests**: Verify IPC and sensor data flow. ### 5. Debugging - Use `ILogger` for real-time logging. - Set breakpoints in the simulator. - Use performance profiling to monitor CPU and memory usage. ### 6. Packaging Package your ELF binary and assets into a `.uapp` container using `app_packer.py`. - Inject version metadata. - Embed app icons. ### 7. Deployment Deploy your app to the watch via: - **USB**: For direct development flashing. - **BLE OTA**: For wireless updates. ### 8. Maintenance - Monitor crash reports. - Release version updates. - Respond to user feedback. ## Technical Architecture Details ### Application Framework Implementation **AppInstance Architecture:** **Dual-Process Model:** - **Service Component**: Background logic, sensor access, data processing - **GUI Component**: User interface, TouchGFX integration, screen rendering **Lifecycle State Machine:** ```cpp enum AppPartState { NONE, // Not loaded RUN, // Running normally RESUMED, // GUI active and visible GLANCE, // Glance mode (notification display) SUSPENDED // Backgrounded }; ``` **Message Processing Thread:** - Dedicated OS thread with cancellable event loop - Handler dispatch: lifecycle, app-specific, sensor layer messages - Automatic queue clearing on termination to prevent memory leaks **Dynamic Loading Mechanism:** - ELF parsing with `Loader::ReadMainHeader()` for .uapp files - Memory-mapped execution with `mSrvLoader.load()` and `mGuiLoader.load()` - Offset-based loading: GUI loads after service + icons in .uapp structure **Sensor Integration:** - **SDK::Sensor::RequestDefault** for default sensor discovery - **SDK::Sensor::RequestConnect** with period/latency negotiation - Listener registration for service vs GUI process isolation #### Application Framework Dual-Process Architecture ```mermaid graph TD subgraph "Application Package (.uapp)" UAPP[.uapp File
Container] SERVICE_ELF[Service ELF
Background Logic] GUI_ELF[GUI ELF
User Interface] METADATA[Metadata
Icons/Config] end subgraph "Runtime Processes" SERVICE_PROC[Service Process
PID: Service_PID] GUI_PROC[GUI Process
PID: GUI_PID] end subgraph "Kernel Interfaces" KERNEL_SDK[SDK Interfaces
IAppComm/ISystem/IFileSystem] MESSAGE_POOLS[Message Pools
Events/Requests/Commands] SENSOR_LAYER[Sensor Layer
Hardware Access] FILESYSTEM[FileSystem
Storage Access] end subgraph "Inter-Process Communication" DUAL_APP_COMM[DualAppComm
IPC Bridge] MSG_QUEUES[Message Queues
20-slot Circular] EVENT_DISPATCH[Event Dispatch
Type-safe Routing] end UAPP --> SERVICE_PROC UAPP --> GUI_PROC SERVICE_PROC --> KERNEL_SDK GUI_PROC --> KERNEL_SDK KERNEL_SDK --> MESSAGE_POOLS KERNEL_SDK --> SENSOR_LAYER KERNEL_SDK --> FILESYSTEM SERVICE_PROC --> DUAL_APP_COMM GUI_PROC --> DUAL_APP_COMM DUAL_APP_COMM --> MSG_QUEUES DUAL_APP_COMM --> EVENT_DISPATCH style SERVICE_PROC fill:#bbdefb style GUI_PROC fill:#c8e6c9 style DUAL_APP_COMM fill:#fff3e0 ``` #### Dynamic Loading and Memory Management Flow ```mermaid flowchart TD A[App Package
.uapp File] --> B[App::Manager::scan
Package Discovery] B --> C{ELF Validation} C -->|Valid| D[Loader::ReadMainHeader
Parse ELF Sections] C -->|Invalid| E[Error: Corrupt Package] D --> F[Memory Allocation
Protected Memory Regions] F --> G{Service ELF
Load First} G -->|Yes| H[Service Loader
Load ELF + Relocate] G -->|No| I[GUI Loader
Load ELF + Relocate
Offset from Service] H --> J[Thread Creation
Service Process Thread] I --> K[Thread Creation
GUI Process Thread] J --> L[Kernel Registration
PID Assignment] K --> L L --> M[Interface Binding
SDK Kernel Access] M --> N[AppInstance Ready
Lifecycle Management] N --> O{App State
Transition} O -->|Start| P[InternalAppRunService
Background Processing] O -->|Resume| Q[InternalAppRun
GUI Active] O -->|Glance| R[InternalAppGlanceRun
Notification Mode] O -->|Suspend| S[GUI Suspend
Service Only] O -->|Stop| T[InternalAppStop
Cleanup] T --> U[Memory Deallocation
Thread Termination] U --> V[App Unloaded] style B fill:#bbdefb style N fill:#c8e6c9 style V fill:#fff3e0 ``` #### Application Message Processing Architecture ```mermaid graph TD subgraph "Message Sources" KERNEL_EVENTS[Kernel Events
System/App/BLE/Timer] USER_INPUT[User Input
Buttons/Touch/Gestures] SENSOR_DATA[Sensor Data
Periodic/Event-driven] SYSTEM_REQUESTS[System Requests
Settings/Battery/Status] end subgraph "Message Classification" EVENTS[Events
0x03010000-0x03040000
Fire-and-forget] REQUESTS[Requests
0x01070000-0x020A0000
With Response] COMMANDS[Commands
0x01010000-0x01060000
With Confirmation] end subgraph "Processing Pipeline" MSG_RECEIVE[Message Receive
getMessage Timeout] TYPE_DISPATCH[Type Dispatch
std::visit Pattern] HANDLER_EXEC[Handler Execution
App-specific Logic] RESPONSE_SEND[Response Send
sendResponse Completion] end subgraph "App Components" SERVICE_LOGIC[Service Logic
Background Processing] GUI_LOGIC[GUI Logic
UI Updates] STATE_MGMT[State Management
Lifecycle Transitions] end KERNEL_EVENTS --> EVENTS USER_INPUT --> REQUESTS SENSOR_DATA --> EVENTS SYSTEM_REQUESTS --> REQUESTS EVENTS --> MSG_RECEIVE REQUESTS --> MSG_RECEIVE COMMANDS --> MSG_RECEIVE MSG_RECEIVE --> TYPE_DISPATCH TYPE_DISPATCH --> HANDLER_EXEC HANDLER_EXEC --> RESPONSE_SEND HANDLER_EXEC --> SERVICE_LOGIC HANDLER_EXEC --> GUI_LOGIC HANDLER_EXEC --> STATE_MGMT SERVICE_LOGIC -.->|Data Processing| HANDLER_EXEC GUI_LOGIC -.->|UI Feedback| HANDLER_EXEC STATE_MGMT -.->|State Changes| HANDLER_EXEC style MSG_RECEIVE fill:#bbdefb style TYPE_DISPATCH fill:#c8e6c9 style RESPONSE_SEND fill:#fff3e0 ``` #### Sensor Integration and Data Flow ```mermaid flowchart TD A[App Sensor Request] --> B{SDK::Sensor::RequestDefault} B -->|Heart Rate| C[Sensor::Type::HEART_RATE] B -->|Accelerometer| D[Sensor::Type::ACCELEROMETER] B -->|Other| E[Sensor::Type::*] C --> F[Sensor::Manager::getSensorList] D --> F E --> F F --> G{Sensor Available?} G -->|Yes| H[Return Sensor Handle
1-based Index] G -->|No| I[Error: No Sensor
Available] H --> J[SDK::Sensor::RequestConnect] J --> K[Handle Assignment
Period Negotiation] K --> L[Latency Configuration
ms/microseconds] L --> M[Listener Registration
Service vs GUI Process] M --> N[Sensor Layer
Connection Established] N --> O[Periodic Sampling
Event Loop] O --> P[Data Acquisition
Sensor Driver] P --> Q[Data Processing
Calibration/Filtering] Q --> R[Callback Dispatch
onSensorUpdate] R --> S{Process Type} S -->|Service| T[Service Process
Background Logic] S -->|GUI| U[GUI Process
UI Updates] T --> V[Data Storage
Analysis/Trends] U --> W[Display Update
Real-time Feedback] V --> X[Persistent Storage
File System] W --> Y[TouchGFX Render
Screen Update] style A fill:#e1f5fe style N fill:#c8e6c9 style R fill:#fff3e0 ``` ## App Development Framework ### App Architecture & Lifecycle **Dual-Process Model:** - **Service Process**: Background logic, sensor access, data processing, glance notifications - **GUI Process**: User interface, TouchGFX integration, screen rendering, user interaction **App Types:** - **Activity**: Full-screen GUI app with service background processing - **Utility**: Specialized function app (calculator, settings, etc.) - **Glance**: Notification-only app without GUI (240x60 pixel display) - **Clockface**: Watch face app with time display **Lifecycle States:** ```cpp enum AppPartState { NONE, // Not loaded RUN, // Running normally RESUMED, // GUI active and visible GLANCE, // Glance mode (notification display) SUSPENDED // Backgrounded }; ``` #### App Lifecycle & Management ```mermaid stateDiagram-v2 [*] --> Created Created --> Initialized Initialized --> ServiceRunning ServiceRunning --> FullRunning FullRunning --> ServiceOnly ServiceOnly --> FullRunning FullRunning --> GlanceMode GlanceMode --> ServiceOnly ServiceRunning --> Stopped ServiceOnly --> Stopped GlanceMode --> Stopped Stopped --> Destroyed Destroyed --> [*] Created : App Manager addApp() Initialized : AppInstance init() ServiceRunning : InternalAppRunService FullRunning : InternalAppRun (GUI) GlanceMode : InternalAppGlanceRun ServiceOnly : GUI Suspend Stopped : InternalAppStop Destroyed : AppInstance deinit() Destroyed --> [*] note right of Created .uapp file loaded ELF parsed Memory allocated end note note right of Initialized Thread created Message queues ready Kernel registered end note note right of ServiceRunning Service ELF loaded Background processing Sensor connections end note note right of FullRunning GUI ELF loaded TouchGFX active User interaction end note note right of GlanceMode Notification display 240x60 pixel canvas Low power mode end note ``` ### SDK Interfaces for App Development **Core Interfaces Available to Apps:** **1. IAppComm (Communication)** - Message passing between app and kernel - Type-safe message allocation from pools - Process identity with unique PIDs **2. ISystem (System Services)** - Timing functions and delays - Battery status and power management - Device identification **3. IFileSystem (Storage)** - File operations on multiple volumes (0:/, 1:/, 2:/) - Directory navigation and content listing **4. ILogger (Debugging)** - Formatted logging with multiple levels - System timestamp integration ### Message System Architecture **Message Type Ranges:** - **Custom/App-specific** (0x00000000-0x0000FFFF): Application-specific internal communication - **Commands** (0x01010000-0x01060000): Kernel-to-app directives (response expected) - **Requests** (0x01070000-0x020A0000): App-to-kernel lifecycle and hardware requests - **Events** (0x03010000-0x03040000): System-level notifications (fire-and-forget) - **Sensors** (0x03100000-0x03180000): Sensor discovery and data events **Key System Messages:** ```cpp REQUEST_BATTERY_STATUS // Get battery level REQUEST_SYSTEM_SETTINGS // Get watch settings REQUEST_DISPLAY_CONFIG // Get screen dimensions (GUI only) REQUEST_BACKLIGHT_SET // Set screen brightness REQUEST_SENSOR_LAYER_CONNECT // Start sensor sampling ``` ### Sensor Layer Integration **Sensor Access Pattern:** ```cpp // Modern way using SDK::Sensor::Connection wrapper SDK::Sensor::Connection hrSensor(SDK::Sensor::Type::HEART_RATE, 1000.0f); hrSensor.connect(); // Manual way using messages auto msg = SDK::make_msg(kernel); msg->id = SDK::Sensor::Type::HEART_RATE; if (msg.send(100) && msg.ok()) { uint32_t handle = msg->handle; // ... use handle to connect } ``` #### Sensor Data Flow Architecture ```mermaid graph TD subgraph "Hardware Sensors" PPG[PPG Sensor
Heart Rate] IMU[IMU Sensor
Accel/Gyro] BARO[Barometer
Pressure/Altitude] TEMP[Temperature
Ambient] end subgraph "Sensor Drivers" PPG_DRV[PPG Driver
PAH8316LS] IMU_DRV[IMU Driver
BMI270] BARO_DRV[Barometer Driver
MS5837] TEMP_DRV[Temp Driver
Internal] end subgraph "Sensor Manager" SM[Sensor::Manager
Coordination Hub] SM_THREAD[Sensor Thread
Polling Loop] SM_QUEUE[Data Queue
Sample Buffer] end subgraph "App Integration" APP_SENSOR_LISTENER[Sensor Listener
App Callback] APP_DATA_QUEUE[App Data Queue
Processed Samples] APP_SERVICE[App Service
Data Processing] end PPG --> PPG_DRV IMU --> IMU_DRV BARO --> BARO_DRV TEMP --> TEMP_DRV PPG_DRV --> SM IMU_DRV --> SM BARO_DRV --> SM TEMP_DRV --> SM SM --> SM_THREAD SM_THREAD --> SM_QUEUE SM_QUEUE --> APP_SENSOR_LISTENER APP_SENSOR_LISTENER --> APP_DATA_QUEUE APP_DATA_QUEUE --> APP_SERVICE SM -.->|Period Negotiation| PPG_DRV SM -.->|Latency Control| IMU_DRV SM -.->|Handle Assignment| BARO_DRV style SM fill:#bbdefb style SM_THREAD fill:#c8e6c9 style APP_SERVICE fill:#fff3e0 ``` ### App Development Workflow **1. Project Structure Setup:** ``` AppName/ ├── Software/ │ ├── App/ │ │ └── AppName-CubeIDE/ # STM32CubeIDE project │ │ ├── Core/ │ │ │ ├── Inc/main.h │ │ │ └── Src/main.cpp # App entry point │ │ └── SDK/ # SDK headers │ └── Libs/ │ ├── Header/Service.hpp # Service class │ └── Source/Service.cpp # Service implementation ├── Resources/ │ ├── icon_30x30.png # Small app icon │ └── icon_60x60.png # Large app icon └── Output/Release/ # Built .uapp files ``` **2. Service Implementation:** ```cpp class Service : public SDK::Interface::IApp::Callback, public SDK::Interface::IGlance { private: SDK::Kernel& mKernel; bool mTerminate = false; public: Service(SDK::Kernel& kernel) : mKernel(kernel) { // Register this instance as a lifecycle callback // Note: IApp interface must be queried via IKIP if not passed directly } void run() { while (!mTerminate) { // Main service loop mKernel.sys.delay(1000); // Handle messages SDK::MessageBase* msg = nullptr; if (mKernel.comm.getMessage(msg, 0)) { // ... handle message mKernel.comm.releaseMessage(msg); } } } // Lifecycle callbacks void onCreate() override { /* Initialize */ } void onStart() override { /* Start processing */ } void onStop() override { mTerminate = true; } }; ``` **3. Message Handling:** ```cpp bool getMessage(MessageBase*& msg, uint32_t timeout) { return mKernel.comm.getMessage(msg, timeout); } void sendResponse(MessageBase* msg) { mKernel.comm.sendResponse(msg); } ``` ### App Capabilities System **Permission-Based Features:** - **Phone Notifications**: iOS ANCS integration - **USB Charging Screen**: Custom charging UI - **Music Control**: Media playback integration ### App Deployment **OTA Update Process:** 1. File transfer via BLE to `2:/Update/` directory 2. CRC and signature verification 3. Atomic replacement with rollback capability 4. System reboot to activate new version #### App Development Data Flow ```mermaid graph LR subgraph "App Development" DEV[Developer
Writes Code] SDK[SDK Headers
Interfaces] BUILD[STM32CubeIDE
Build Process] PACK[Post-build Scripts
app_packer.py] end subgraph "App Package" UAPP[.uapp File
ELF + Icons + Metadata] HEADER[MainHeader
App Info] SERVICE[Service ELF
Background Logic] GUI[GUI ELF
User Interface] ICONS[Icons
30x30 + 60x60 PNG] end subgraph "Runtime Loading" MANAGER[App::Manager
Package Scanner] LOADER[App Loader
ELF Parser] MEMORY[Memory Allocator
Protected Regions] KERNEL[Kernel Interfaces
SDK Bridge] end subgraph "Execution" SRV_PROCESS[Service Process
PID Assigned] GUI_PROCESS[GUI Process
PID Assigned] COMM[DualAppComm
IPC Bridge] MESSAGES[Message System
Kernel Communication] end DEV --> SDK SDK --> BUILD BUILD --> PACK PACK --> UAPP UAPP --> HEADER UAPP --> SERVICE UAPP --> GUI UAPP --> ICONS MANAGER --> LOADER LOADER --> MEMORY MEMORY --> KERNEL KERNEL --> SRV_PROCESS KERNEL --> GUI_PROCESS SRV_PROCESS --> COMM GUI_PROCESS --> COMM COMM --> MESSAGES style DEV fill:#e1f5fe style UAPP fill:#c8e6c9 style SRV_PROCESS fill:#fff3e0 style GUI_PROCESS fill:#fff3e0 ``` This framework enables **independent app development** with clean SDK interfaces, resource management, and comprehensive tooling support. ### Building Individual Apps 1. Open STM32CubeIDE 2. Import the CubeIDE project: `File > Import > Existing Projects into Workspace` 3. Select the `.cproject` file in `Apps//Software/App/-CubeIDE/` 4. Build the project (Project > Build All) 5. The post-build script will automatically generate a `.uapp` file in `Output/Release/` ### App Installation - Apps are installed as `.uapp` files - Deploy via the watch's companion app or development interface - Apps can be updated independently of the main firmware