-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
lib.rs
280 lines (240 loc) · 9.89 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
//! Watch Face Framework for Rust + Mynewt on PineTime Smart Watch
#![no_std] // Don't link with standard Rust library, which is not compatible with embedded systems
#![feature(trace_macros)] // Allow macro tracing: `trace_macros!(true)`
#![feature(concat_idents)] // Allow `concat_idents!()` macro used in `coap!()` macro
#![feature(proc_macro_hygiene)] // Allow Procedural Macros like `run!()`
#![feature(exclusive_range_pattern)] // Allow ranges like `0..128` in `match` statements
// Declare the libraries that contain macros
extern crate macros as mynewt_macros; // Declare the Mynewt Procedural Macros library
pub use lvgl; // Export LVGL API
use core::{
ptr,
};
use lvgl::mynewt::{
fill_zero,
kernel::os,
result::*,
sys::console,
Strn,
};
use lvgl::{
core::{
disp,
obj
},
};
///////////////////////////////////////////////////////////////////////////////
// Watch Face Trait
/// Watch Faces shall implement this trait
pub trait WatchFace {
/// Create the widgets for the Watch Face
fn new() -> MynewtResult<Self>
where Self: core::marker::Sized; // Result type must have known size
/// Update the widgets in the Watch Face with the current state
fn update(&mut self, state: &WatchFaceState) -> MynewtResult<()>;
}
///////////////////////////////////////////////////////////////////////////////
// Mynewt Timer Functions
/// Start rendering the watch face every minute
pub fn start_watch_face(update_watch_face: UpdateWatchFace) -> MynewtResult<()> {
console::print("Init Rust watch face...\n"); console::flush();
// Save the callback for updating the watch face
unsafe { UPDATE_WATCH_FACE = Some(update_watch_face); }
// Get active screen from LVGL
let screen = get_active_screen();
// Allow touch events
obj::set_click(screen, true) ? ;
// Set a timer to update the watch face every minute
unsafe { // Unsafe because os_callout_init is a Mynewt C function
os::os_callout_init(
&mut WATCH_FACE_CALLOUT, // Timer for the watch face
os::eventq_dflt_get().unwrap(), // Use default event queue
Some(watch_face_callback), // Callback function for the timer
ptr::null_mut() // No argument
);
}
// Trigger the watch face timer now to render the watch face for the first time
let rc = unsafe { // Unsafe because os_callout_reset is a Mynewt C function
os::os_callout_reset(
&mut WATCH_FACE_CALLOUT, // Timer for the watch face
os::OS_TICKS_PER_SEC * 0 // Trigger the timer now
)
};
assert!(rc == 0, "Timer fail");
Ok(())
}
/// Timer callback that is called every minute
extern fn watch_face_callback(_ev: *mut os::os_event) {
console::print("Update Rust watch face...\n"); console::flush();
// If there is no callback, fail.
assert!(unsafe { UPDATE_WATCH_FACE.is_some() }, "Update watch face missing");
// Get the system time
let time = get_system_time()
.expect("Can't get system time");
// Compose the watch face state
let state = WatchFaceState {
time,
millivolts: 0, // TODO: Get current voltage
charging: true, // TODO: Get charging status
powered: true, // TODO: Get powered status
bluetooth: BluetoothState::BLUETOOTH_STATE_CONNECTED, // TODO: Get BLE state
};
// Update the watch face
unsafe { // Unsafe because WATCH_FACE is a mutable static
UPDATE_WATCH_FACE.unwrap()(&state)
.expect("Update Watch Face fail");
}
// Render the watch face
let rc = unsafe { pinetime_lvgl_mynewt_render() };
assert!(rc == 0, "LVGL render fail");
// Trigger the watch face timer in 60 seconds
let rc = unsafe { // Unsafe because os_callout_reset is a Mynewt C function
os::os_callout_reset(
&mut WATCH_FACE_CALLOUT, // Timer for the watch face
os::OS_TICKS_PER_SEC * 60 // Trigger timer in 60 seconds
)
};
assert!(rc == 0, "Timer fail");
}
/// Get active screen from LVGL
pub fn get_active_screen() -> lvgl::Ptr {
// Get active screen from LVGL
let screen = disp::get_scr_act(
disp::get_default()
.expect("Failed to get display")
).expect("Failed to get active display");
assert!(!screen.is_null(), "null screen");
screen
}
/// Timer that is triggered every minute to update the watch face
static mut WATCH_FACE_CALLOUT: os::os_callout = fill_zero!(os::os_callout);
/// Called every minute to update the Watch Face
static mut UPDATE_WATCH_FACE: Option<UpdateWatchFace> = None;
/// Type of callback to update the Watch Face
type UpdateWatchFace = fn (state: &WatchFaceState) -> MynewtResult<()>;
///////////////////////////////////////////////////////////////////////////////
// Date Time Functions
/// Get the system time
fn get_system_time() -> MynewtResult<WatchFaceTime> {
// Get the system time
static mut TV: os::os_timeval = fill_zero!(os::os_timeval);
static mut TZ: os::os_timezone = fill_zero!(os::os_timezone);
let rc = unsafe { os::os_gettimeofday(&mut TV, &mut TZ) };
assert!(rc == 0, "Can't get time");
// Convert the time
static mut CT: clocktime = fill_zero!(clocktime);
let rc = unsafe { timeval_to_clocktime(&TV, &TZ, &mut CT) };
assert!(rc == 0, "Can't convert time");
// Return the time
let result = unsafe { // Unsafe because CT is a mutable static
WatchFaceTime {
year: CT.year as u16, // Year (4 digit year)
month: CT.mon as u8, // Month (1 - 12)
day: CT.day as u8, // Day (1 - 31)
hour: CT.hour as u8, // Hour (0 - 23)
minute: CT.min as u8, // Minute (0 - 59)
second: CT.sec as u8, // Second (0 - 59)
day_of_week: CT.dow as u8, // Day of week (0 - 6; 0 = Sunday)
}
};
Ok(result)
}
///////////////////////////////////////////////////////////////////////////////
// String Definitions
/// Create a new String
pub const fn new_string() -> String {
heapless::String(heapless::i::String::new())
}
/// Convert a static String to null-terminated Strn
pub fn to_strn(str: &String) -> Strn {
Strn::new(str.as_bytes())
}
/// Limit Strings to 64 chars (which may include multiple color codes like "#ffffff")
pub type String = heapless::String::<heapless::consts::U64>;
///////////////////////////////////////////////////////////////////////////////
// Watch Face Definitions
/// State for the Watch Face
#[repr(C)] // Allow this struct to be passed to C (for WebAssembly integration)
pub struct WatchFaceState {
/// Current date and time
pub time: WatchFaceTime,
/// Bluetooth state
pub bluetooth: BluetoothState,
/// Current power
pub millivolts: u32,
/// True if watch is charging
pub charging: bool,
/// True if watch is powered
pub powered: bool,
}
/// Watch Face Time
#[repr(C)] // Allow this struct to be passed to C (for WebAssembly integration)
pub struct WatchFaceTime {
/// Year (4 digit year)
pub year: u16,
/// Month (1 - 12)
pub month: u8,
/// Day (1 - 31)
pub day: u8,
/// Hour (0 - 23)
pub hour: u8,
/// Minute (0 - 59)
pub minute: u8,
/// Second (0 - 59)
pub second: u8,
/// Day of week (0 - 6; 0 = Sunday)
pub day_of_week: u8,
}
/// Bluetooth State
#[repr(u8)] // Store the enum as 1 byte, so that we can pass this enum to C (for WebAssembly integration)
#[derive(PartialEq)] // Allow comparison of enum
#[allow(dead_code)] // TODO: Use all enum values
#[allow(non_camel_case_types)]
pub enum BluetoothState {
/// Bluetooth is inactive
BLUETOOTH_STATE_INACTIVE = 0,
/// Bluetooth is advertising
BLUETOOTH_STATE_ADVERTISING = 1,
/// Bluetooth is disconnected
BLUETOOTH_STATE_DISCONNECTED = 2,
/// Bluetooth is connected
BLUETOOTH_STATE_CONNECTED = 3,
}
///////////////////////////////////////////////////////////////////////////////
// Import C APIs
extern {
/// Render the LVGL display. Defined in libs/pinetime_lvgl_mynewt/src/pinetime/lvgl.c
fn pinetime_lvgl_mynewt_render() -> i32;
/// Convert timeval to clocktime. From https://github.com/apache/mynewt-core/blob/master/time/datetime/include/datetime/datetime.h
fn timeval_to_clocktime(tv: *const os::os_timeval, tz: *const os::os_timezone, ct: *mut clocktime) -> i32;
}
/// Mynewt Clock Time. From https://github.com/apache/mynewt-core/blob/master/time/datetime/include/datetime/datetime.h
#[repr(C)] // Allow this struct to be passed to Mynewt in C
struct clocktime {
year: i32, // Year (4 digit year)
mon: i32, // Month (1 - 12)
day: i32, // Day (1 - 31)
hour: i32, // Hour (0 - 23)
min: i32, // Minute (0 - 59)
sec: i32, // Second (0 - 59)
dow: i32, // Day of week (0 - 6; 0 = Sunday)
usec: i32, // Micro seconds
}