+68
-65
src/badge_display/mod.rs
+68
-65
src/badge_display/mod.rs
···
1
1
pub mod display_image;
2
2
3
-
use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU8};
3
+
use core::{
4
+
cell::RefCell,
5
+
sync::atomic::{AtomicBool, AtomicU32, AtomicU8},
6
+
};
7
+
use cortex_m::interrupt::CriticalSection;
4
8
use defmt::*;
5
9
use display_image::get_current_image;
6
10
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
7
11
use embassy_rp::gpio;
8
12
use embassy_rp::gpio::Input;
13
+
use embassy_sync::{
14
+
blocking_mutex::{self, raw::CriticalSectionRawMutex},
15
+
mutex,
16
+
};
9
17
use embassy_time::{Delay, Duration, Timer};
10
18
use embedded_graphics::{
11
19
image::Image,
···
34
42
pub static CURRENT_IMAGE: AtomicU8 = AtomicU8::new(0);
35
43
pub static CHANGE_IMAGE: AtomicBool = AtomicBool::new(true);
36
44
pub static WIFI_COUNT: AtomicU32 = AtomicU32::new(0);
45
+
46
+
pub static RTC_TIME_STRING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<String<8>>> =
47
+
blocking_mutex::Mutex::new(RefCell::new(String::<8>::new()));
48
+
37
49
pub static HOUR: AtomicU8 = AtomicU8::new(10);
38
50
pub static MINUTE: AtomicU8 = AtomicU8::new(57);
39
51
pub static SECOND: AtomicU8 = AtomicU8::new(0);
40
-
pub static WRITE_NEW_TIME: AtomicBool = AtomicBool::new(true);
41
52
42
53
#[embassy_executor::task]
43
54
pub async fn run_the_display(
···
53
64
54
65
display.reset().await;
55
66
56
-
// Initialise display. Using the default LUT speed setting
57
-
let _ = display.setup(LUT::Fast).await;
67
+
// Initialise display with speed
68
+
let _ = display.setup(LUT::Medium).await;
58
69
59
70
// Note we're setting the Text color to `Off`. The driver is set up to treat Off as Black so that BMPs work as expected.
60
71
let character_style = MonoTextStyle::new(&FONT_9X18_BOLD, BinaryColor::Off);
···
89
100
// Draw the text box.
90
101
name_and_detail_box.draw(&mut display).unwrap();
91
102
92
-
let _ = display.update().await;
103
+
// let _ = display.update().await;
93
104
105
+
//Each cycle is half a second
94
106
let cycle: Duration = Duration::from_millis(500);
107
+
95
108
let mut first_run = true;
96
-
let mut text: String<16> = String::<16>::new();
97
-
let cycles_to_skip = 30;
109
+
//New start every 120 cycles or 60 seconds
110
+
let cycles_to_clear_at: i32 = 120;
98
111
let mut cycles_since_last_clear = 0;
99
112
100
-
let mut time_text: String<16> = String::<16>::new();
113
+
loop {
114
+
//Timed based display events
101
115
102
-
loop {
103
-
//if cycles are even, update the time values
116
+
//Runs every second;
104
117
if cycles_since_last_clear % 2 == 0 {
105
-
update_time_values_from_cycles();
118
+
// update_time_values_from_cycles();
106
119
}
107
120
108
-
if WRITE_NEW_TIME.load(core::sync::atomic::Ordering::Relaxed) {
109
-
let hour = HOUR.load(core::sync::atomic::Ordering::Relaxed);
110
-
let minute = MINUTE.load(core::sync::atomic::Ordering::Relaxed);
111
-
let second = SECOND.load(core::sync::atomic::Ordering::Relaxed);
112
-
113
-
let _ = core::fmt::write(
114
-
&mut time_text,
115
-
format_args!("{:02}:{:02}:{:02}", hour, minute, second),
116
-
);
117
-
118
-
let time_bounds = Rectangle::new(Point::new(0, 24), Size::new(WIDTH, 16));
119
-
time_bounds
121
+
//Updates the top bar
122
+
//Runs every 30 cycles/15 seconds and first run
123
+
if cycles_since_last_clear % 30 == 0 || first_run {
124
+
let count = WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed);
125
+
let count_text: String<16> = easy_format::<16>(format_args!("Count: {}", count));
126
+
let count_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24));
127
+
count_bounds
120
128
.into_styled(
121
129
PrimitiveStyleBuilder::default()
122
130
.stroke_color(BinaryColor::Off)
···
127
135
.draw(&mut display)
128
136
.unwrap();
129
137
130
-
Text::new(time_text.as_str(), Point::new(8, 32), character_style)
138
+
Text::new(count_text.as_str(), Point::new(8, 16), character_style)
131
139
.draw(&mut display)
132
140
.unwrap();
133
141
142
+
// // Draw the text box.
134
143
let result = display
135
-
.partial_update(time_bounds.try_into().unwrap())
144
+
.partial_update(count_bounds.try_into().unwrap())
136
145
.await;
137
146
match result {
138
147
Ok(_) => {}
···
140
149
info!("Error updating display");
141
150
}
142
151
}
143
-
WRITE_NEW_TIME.store(false, core::sync::atomic::Ordering::Relaxed);
152
+
// let _ = display.clear(Rgb565::WHITE.into());
153
+
// let _ = display.update().await;
154
+
WIFI_COUNT.store(count + 1, core::sync::atomic::Ordering::Relaxed);
144
155
}
145
156
146
-
if cycles_since_last_clear >= cycles_to_skip || first_run {
147
-
let count = WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed);
148
-
let _ = core::fmt::write(&mut text, format_args!("Count: {}", count));
149
-
let count_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24));
150
-
count_bounds
157
+
//Runs every 120 cycles/60 seconds and first run
158
+
if cycles_since_last_clear == 0 {
159
+
let mut time_text: String<8> = String::<8>::new();
160
+
161
+
let time_box_rectangle_location = Point::new(0, 96);
162
+
RTC_TIME_STRING.lock(|x| {
163
+
time_text.push_str(x.borrow().as_str()).unwrap();
164
+
});
165
+
166
+
//The bounds of the box for time and refresh area
167
+
let time_bounds = Rectangle::new(time_box_rectangle_location, Size::new(88, 24));
168
+
time_bounds
151
169
.into_styled(
152
170
PrimitiveStyleBuilder::default()
153
171
.stroke_color(BinaryColor::Off)
···
158
176
.draw(&mut display)
159
177
.unwrap();
160
178
161
-
Text::new(text.as_str(), Point::new(8, 16), character_style)
162
-
.draw(&mut display)
163
-
.unwrap();
179
+
//Adding a y offset to the box location to fit inside the box
180
+
Text::new(
181
+
time_text.as_str(),
182
+
(
183
+
time_box_rectangle_location.x + 8,
184
+
time_box_rectangle_location.y + 16,
185
+
)
186
+
.into(),
187
+
character_style,
188
+
)
189
+
.draw(&mut display)
190
+
.unwrap();
164
191
165
-
// // Draw the text box.
166
192
let result = display
167
-
.partial_update(count_bounds.try_into().unwrap())
193
+
.partial_update(time_bounds.try_into().unwrap())
168
194
.await;
169
195
match result {
170
196
Ok(_) => {}
···
172
198
info!("Error updating display");
173
199
}
174
200
}
175
-
text.clear();
176
-
// let _ = display.clear(Rgb565::WHITE.into());
177
-
// let _ = display.update().await;
178
-
WIFI_COUNT.store(count + 1, core::sync::atomic::Ordering::Relaxed);
179
-
cycles_since_last_clear = 0;
180
201
}
202
+
203
+
//Manually triggered display events
181
204
182
205
if CHANGE_IMAGE.load(core::sync::atomic::Ordering::Relaxed) {
183
206
let current_image = get_current_image();
···
203
226
}
204
227
205
228
cycles_since_last_clear += 1;
229
+
if cycles_since_last_clear >= cycles_to_clear_at {
230
+
cycles_since_last_clear = 0;
231
+
}
206
232
first_run = false;
233
+
// info!("Display Cycle: {}", cycles_since_last_clear);
207
234
Timer::after(cycle).await;
208
235
}
209
236
}
210
-
211
-
fn update_time_values_from_cycles() {
212
-
let current_second = SECOND.load(core::sync::atomic::Ordering::Relaxed);
213
-
let current_minute = MINUTE.load(core::sync::atomic::Ordering::Relaxed);
214
-
let current_hour = HOUR.load(core::sync::atomic::Ordering::Relaxed);
215
-
216
-
let new_second = (current_second + 1) % 60;
217
-
let new_minute = if new_second == 0 {
218
-
WRITE_NEW_TIME.store(true, core::sync::atomic::Ordering::Relaxed);
219
-
(current_minute + 1) % 60
220
-
} else {
221
-
current_minute
222
-
};
223
-
let new_hour = if new_minute == 0 && new_second == 0 {
224
-
WRITE_NEW_TIME.store(true, core::sync::atomic::Ordering::Relaxed);
225
-
(current_hour + 1) % 24
226
-
} else {
227
-
current_hour
228
-
};
229
-
230
-
SECOND.store(new_second, core::sync::atomic::Ordering::Relaxed);
231
-
MINUTE.store(new_minute, core::sync::atomic::Ordering::Relaxed);
232
-
HOUR.store(new_hour, core::sync::atomic::Ordering::Relaxed);
233
-
}
+55
-1
src/main.rs
+55
-1
src/main.rs
···
5
5
#![no_std]
6
6
#![no_main]
7
7
use badge_display::display_image::DisplayImage;
8
-
use badge_display::{run_the_display, CHANGE_IMAGE, CURRENT_IMAGE};
8
+
use badge_display::{run_the_display, CHANGE_IMAGE, CURRENT_IMAGE, RTC_TIME_STRING};
9
9
use defmt::info;
10
10
use embassy_executor::Spawner;
11
11
use embassy_rp::gpio;
12
12
use embassy_rp::gpio::Input;
13
13
use embassy_rp::peripherals::SPI0;
14
+
use embassy_rp::rtc::{DateTime, DayOfWeek};
14
15
use embassy_rp::spi::Spi;
15
16
use embassy_rp::spi::{self};
16
17
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
···
29
30
TextBox,
30
31
};
31
32
use gpio::{Level, Output, Pull};
33
+
use helpers::easy_format;
32
34
use static_cell::StaticCell;
33
35
use {defmt_rtt as _, panic_probe as _};
34
36
···
90
92
// control.gpio_set(0, true).await;
91
93
spawner.must_spawn(run_the_display(spi_bus, cs, dc, busy, reset));
92
94
95
+
//rtc setup
96
+
let mut rtc = embassy_rp::rtc::Rtc::new(p.RTC);
97
+
if !rtc.is_running() {
98
+
info!("Start RTC");
99
+
let now = DateTime {
100
+
year: 2000,
101
+
month: 1,
102
+
day: 1,
103
+
day_of_week: DayOfWeek::Saturday,
104
+
hour: 0,
105
+
minute: 0,
106
+
second: 0,
107
+
};
108
+
rtc.set_datetime(now).unwrap();
109
+
}
110
+
93
111
//Input loop
94
112
loop {
95
113
//Change Image Button
···
102
120
Timer::after(Duration::from_millis(500)).await;
103
121
continue;
104
122
}
123
+
124
+
let now = rtc.now();
125
+
match now {
126
+
Ok(time) => set_display_time(time),
127
+
Err(_) => {
128
+
info!("Error getting time");
129
+
}
130
+
}
131
+
105
132
Timer::after(Duration::from_millis(100)).await;
106
133
}
107
134
}
135
+
136
+
fn set_display_time(time: DateTime) {
137
+
let mut am = true;
138
+
let twelve_hour = if time.hour > 12 {
139
+
am = false;
140
+
time.hour - 12
141
+
} else if time.hour == 0 {
142
+
12
143
+
} else {
144
+
time.hour
145
+
};
146
+
147
+
let am_pm = if am { "AM" } else { "PM" };
148
+
149
+
let formatted_time = easy_format::<8>(format_args!(
150
+
"{:02}:{:02} {}",
151
+
twelve_hour, time.minute, am_pm
152
+
));
153
+
154
+
RTC_TIME_STRING.lock(|rtc_time_string| {
155
+
rtc_time_string.borrow_mut().clear();
156
+
rtc_time_string
157
+
.borrow_mut()
158
+
.push_str(formatted_time.as_str())
159
+
.unwrap();
160
+
});
161
+
}